Mvp finished, can run programs and move them to windows.
This commit is contained in:
parent
0f24273c93
commit
23dc47a008
4 changed files with 224 additions and 134 deletions
|
@ -9,7 +9,8 @@ edition = "2021"
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
rust_lisp = "0.18.0"
|
rust_lisp = { git = "https://github.com/brundonsmith/rust_lisp.git", branch = "arc-feature-addition", features = ["arc"] }
|
||||||
serde = { version = "1.0.188", features = ["std", "derive", "serde_derive"] }
|
serde = { version = "1.0.188", features = ["std", "derive", "serde_derive"] }
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.107"
|
||||||
|
serde_yaml = "0.9.25"
|
||||||
tokio = { version = "1.33.0", features = ["full"] }
|
tokio = { version = "1.33.0", features = ["full"] }
|
||||||
|
|
67
src/config.rs
Normal file
67
src/config.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use serde::{Serialize, Deserialize, Serializer, Deserializer};
|
||||||
|
use rust_lisp::model::Value as RValue;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Value(Vec<RValue>);
|
||||||
|
unsafe impl Send for Value {}
|
||||||
|
unsafe impl Sync for Value {}
|
||||||
|
|
||||||
|
impl Into<Value> for RValue {
|
||||||
|
fn into(self) -> Value {
|
||||||
|
Value(vec![self])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Vec<RValue>> for Value {
|
||||||
|
fn into(self) -> Vec<RValue> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Value {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>
|
||||||
|
{
|
||||||
|
let s: String = Deserialize::deserialize(deserializer)?;
|
||||||
|
let r: Vec<RValue> = rust_lisp::parser::parse(&s).filter_map(|x| x.ok()).collect();
|
||||||
|
Ok(Value(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Program {
|
||||||
|
#[serde(rename = "match")]
|
||||||
|
pub match_: Value,
|
||||||
|
pub cmd: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub run: Option<String>,
|
||||||
|
}
|
||||||
|
unsafe impl Send for Program {}
|
||||||
|
unsafe impl Sync for Program {}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
#[serde(default = "Config::default_timeout")]
|
||||||
|
pub timeout: u32,
|
||||||
|
#[serde(default = "Config::default_init")]
|
||||||
|
pub init: Value,
|
||||||
|
#[serde(default = "Config::default_programs")]
|
||||||
|
pub programs: Vec<Program>,
|
||||||
|
}
|
||||||
|
unsafe impl Send for Config {}
|
||||||
|
unsafe impl Sync for Config {}
|
||||||
|
impl Config {
|
||||||
|
fn default_timeout() -> u32 {
|
||||||
|
3000
|
||||||
|
}
|
||||||
|
fn default_init() -> Value {
|
||||||
|
Value(vec![])
|
||||||
|
}
|
||||||
|
fn default_programs() -> Vec<Program> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
120
src/lisp.rs
Normal file
120
src/lisp.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use rust_lisp::model::{IntType, FloatType, Value, List, Env, reference, reference::Reference};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn serde_lisp_value(value: &serde_json::Value) -> Value {
|
||||||
|
match value {
|
||||||
|
serde_json::Value::Null => Value::NIL,
|
||||||
|
serde_json::Value::Bool(b) => {
|
||||||
|
if *b {
|
||||||
|
Value::True
|
||||||
|
} else {
|
||||||
|
Value::False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serde_json::Value::Number(n) => {
|
||||||
|
if n.is_i64() {
|
||||||
|
Value::Int(n.as_i64().unwrap() as IntType)
|
||||||
|
} else if n.is_u64() {
|
||||||
|
Value::Int(n.as_u64().unwrap() as IntType)
|
||||||
|
} else if n.is_f64() {
|
||||||
|
Value::Float(n.as_f64().unwrap() as FloatType)
|
||||||
|
} else {
|
||||||
|
panic!("should never happen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serde_json::Value::String(s) => Value::String(s.clone()),
|
||||||
|
serde_json::Value::Array(a) => {
|
||||||
|
let mut l = List::NIL;
|
||||||
|
for li in a.into_iter().rev() {
|
||||||
|
l = l.cons(serde_lisp_value(li));
|
||||||
|
}
|
||||||
|
Value::List(l)
|
||||||
|
}
|
||||||
|
serde_json::Value::Object(o) => {
|
||||||
|
let mut r = HashMap::new();
|
||||||
|
for (k, v) in o.into_iter() {
|
||||||
|
let k_ = Value::String(k.clone());
|
||||||
|
let v_ = serde_lisp_value(v);
|
||||||
|
r.insert(k_, v_);
|
||||||
|
}
|
||||||
|
Value::HashMap(reference::new(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn env(value: &serde_json::Value) -> Env {
|
||||||
|
let mut environment = rust_lisp::default_env();
|
||||||
|
environment.define(
|
||||||
|
rust_lisp::model::Symbol::from("__input__"),
|
||||||
|
serde_lisp_value(value),
|
||||||
|
);
|
||||||
|
environment.define(
|
||||||
|
rust_lisp::model::Symbol::from("load"),
|
||||||
|
rust_lisp::model::Value::NativeClosure(reference::new(
|
||||||
|
move |e: Reference<rust_lisp::model::Env>, args: Vec<rust_lisp::model::Value>| {
|
||||||
|
let path: &String =
|
||||||
|
rust_lisp::utils::require_typed_arg::<&String>("load", &args, 0)?;
|
||||||
|
let path = (*path).as_str().split('.');
|
||||||
|
let mut i: rust_lisp::model::Value = reference::borrow(&e)
|
||||||
|
.get(&rust_lisp::model::Symbol::from("__input__"))
|
||||||
|
.unwrap();
|
||||||
|
for p in path
|
||||||
|
.into_iter()
|
||||||
|
.filter(|x| !(*x).eq(""))
|
||||||
|
.map(|x| rust_lisp::model::Value::String(x.into()))
|
||||||
|
{
|
||||||
|
match i {
|
||||||
|
rust_lisp::model::Value::HashMap(x) => {
|
||||||
|
if let Some(_i) = reference::borrow(&x).get(&p) {
|
||||||
|
i = _i.clone();
|
||||||
|
} else {
|
||||||
|
return Err(rust_lisp::model::RuntimeError {
|
||||||
|
msg: format!(r#"No such key {:?}"#, p).into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(rust_lisp::model::RuntimeError {
|
||||||
|
msg: format!(r#"No such key {:?}"#, p).into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(i)
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
environment.define(
|
||||||
|
rust_lisp::model::Symbol::from("has-key"),
|
||||||
|
rust_lisp::model::Value::NativeClosure(reference::new(
|
||||||
|
move |e: Reference<rust_lisp::model::Env>, args: Vec<rust_lisp::model::Value>| {
|
||||||
|
let path: &String =
|
||||||
|
rust_lisp::utils::require_typed_arg::<&String>("has-key", &args, 0)?;
|
||||||
|
let path = (*path).as_str().split('.');
|
||||||
|
let mut i: rust_lisp::model::Value = reference::borrow(&e)
|
||||||
|
.get(&rust_lisp::model::Symbol::from("__input__"))
|
||||||
|
.unwrap();
|
||||||
|
for p in path
|
||||||
|
.into_iter()
|
||||||
|
.filter(|x| !(*x).eq(""))
|
||||||
|
.map(|x| rust_lisp::model::Value::String(x.into()))
|
||||||
|
{
|
||||||
|
match i {
|
||||||
|
rust_lisp::model::Value::HashMap(x) => {
|
||||||
|
if let Some(_i) = reference::borrow(&x).get(&p) {
|
||||||
|
i = _i.clone();
|
||||||
|
} else {
|
||||||
|
return Ok(rust_lisp::model::Value::False);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Ok(rust_lisp::model::Value::False),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(rust_lisp::model::Value::True)
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
environment
|
||||||
|
}
|
166
src/main.rs
166
src/main.rs
|
@ -1,151 +1,47 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use tokio::time::{timeout, Duration};
|
use tokio::time::{timeout, Duration};
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
|
||||||
|
mod config;
|
||||||
mod i3ipc;
|
mod i3ipc;
|
||||||
|
mod lisp;
|
||||||
|
|
||||||
|
use config::Config;
|
||||||
use i3ipc::{Connection, MessageType};
|
use i3ipc::{Connection, MessageType};
|
||||||
|
|
||||||
fn serde_lisp_value(value: &serde_json::Value) -> rust_lisp::model::Value {
|
|
||||||
match value {
|
|
||||||
serde_json::Value::Null => rust_lisp::model::Value::NIL,
|
|
||||||
serde_json::Value::Bool(b) => {
|
|
||||||
if *b {
|
|
||||||
rust_lisp::model::Value::True
|
|
||||||
} else {
|
|
||||||
rust_lisp::model::Value::False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serde_json::Value::Number(n) => {
|
|
||||||
if n.is_i64() {
|
|
||||||
rust_lisp::model::Value::Int(n.as_i64().unwrap() as rust_lisp::model::IntType)
|
|
||||||
} else if n.is_u64() {
|
|
||||||
rust_lisp::model::Value::Int(n.as_u64().unwrap() as rust_lisp::model::IntType)
|
|
||||||
} else if n.is_f64() {
|
|
||||||
rust_lisp::model::Value::Float(n.as_f64().unwrap() as rust_lisp::model::FloatType)
|
|
||||||
} else {
|
|
||||||
panic!("should never happen");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serde_json::Value::String(s) => rust_lisp::model::Value::String(s.clone()),
|
|
||||||
serde_json::Value::Array(a) => {
|
|
||||||
let mut l = rust_lisp::model::List::NIL;
|
|
||||||
for li in a.into_iter().rev() {
|
|
||||||
l = l.cons(serde_lisp_value(li));
|
|
||||||
}
|
|
||||||
rust_lisp::model::Value::List(l)
|
|
||||||
}
|
|
||||||
serde_json::Value::Object(o) => {
|
|
||||||
let mut r = HashMap::new();
|
|
||||||
for (k, v) in o.into_iter() {
|
|
||||||
let k_ = rust_lisp::model::Value::String(k.clone());
|
|
||||||
let v_ = serde_lisp_value(v);
|
|
||||||
r.insert(k_, v_);
|
|
||||||
}
|
|
||||||
rust_lisp::model::Value::HashMap(Rc::new(RefCell::new(r)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_window_cb(
|
fn new_window_cb(
|
||||||
b: MessageType,
|
b: MessageType,
|
||||||
c: serde_json::Value,
|
c: serde_json::Value,
|
||||||
d: bool,
|
config: &Config,
|
||||||
) -> futures::future::BoxFuture<'static, Vec<(MessageType, Vec<u8>)>> {
|
) -> futures::future::BoxFuture<'static, Vec<(MessageType, Vec<u8>)>> {
|
||||||
|
let config_ = config.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut environment = rust_lisp::default_env();
|
for p in config_.programs.iter() {
|
||||||
environment.define(
|
let e = lisp::env(&c);
|
||||||
rust_lisp::model::Symbol::from("__input__"),
|
let init: Vec<rust_lisp::model::Value> = config_.init.clone().into();
|
||||||
serde_lisp_value(&c),
|
let prog: Vec<rust_lisp::model::Value> = p.match_.clone().into();
|
||||||
);
|
let m = init.into_iter().chain(prog.into_iter());
|
||||||
environment.define(
|
let result = rust_lisp::interpreter::eval_block(rust_lisp::model::reference::new(e), m);
|
||||||
rust_lisp::model::Symbol::from("load"),
|
|
||||||
rust_lisp::model::Value::NativeClosure(Rc::new(RefCell::new(
|
|
||||||
move |e: Rc<RefCell<rust_lisp::model::Env>>, args: Vec<rust_lisp::model::Value>| {
|
|
||||||
let path: &String =
|
|
||||||
rust_lisp::utils::require_typed_arg::<&String>("load", &args, 0)?;
|
|
||||||
let path = (*path).as_str().split('.');
|
|
||||||
let mut i: rust_lisp::model::Value = e
|
|
||||||
.as_ref()
|
|
||||||
.borrow()
|
|
||||||
.get(&rust_lisp::model::Symbol::from("__input__"))
|
|
||||||
.unwrap();
|
|
||||||
for p in path
|
|
||||||
.into_iter()
|
|
||||||
.filter(|x| !(*x).eq(""))
|
|
||||||
.map(|x| rust_lisp::model::Value::String(x.into()))
|
|
||||||
{
|
|
||||||
match i {
|
|
||||||
rust_lisp::model::Value::HashMap(x) => {
|
|
||||||
if let Some(_i) = x.as_ref().borrow().get(&p) {
|
|
||||||
i = _i.clone();
|
|
||||||
} else {
|
|
||||||
return Err(rust_lisp::model::RuntimeError {
|
|
||||||
msg: format!(r#"No such key {:?}"#, p).into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(rust_lisp::model::RuntimeError {
|
|
||||||
msg: format!(r#"No such key {:?}"#, p).into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(i)
|
|
||||||
},
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
environment.define(
|
|
||||||
rust_lisp::model::Symbol::from("has-key"),
|
|
||||||
rust_lisp::model::Value::NativeClosure(Rc::new(RefCell::new(
|
|
||||||
move |e: Rc<RefCell<rust_lisp::model::Env>>, args: Vec<rust_lisp::model::Value>| {
|
|
||||||
let path: &String =
|
|
||||||
rust_lisp::utils::require_typed_arg::<&String>("has-key", &args, 0)?;
|
|
||||||
let path = (*path).as_str().split('.');
|
|
||||||
let mut i: rust_lisp::model::Value = e
|
|
||||||
.as_ref()
|
|
||||||
.borrow()
|
|
||||||
.get(&rust_lisp::model::Symbol::from("__input__"))
|
|
||||||
.unwrap();
|
|
||||||
for p in path
|
|
||||||
.into_iter()
|
|
||||||
.filter(|x| !(*x).eq(""))
|
|
||||||
.map(|x| rust_lisp::model::Value::String(x.into()))
|
|
||||||
{
|
|
||||||
match i {
|
|
||||||
rust_lisp::model::Value::HashMap(x) => {
|
|
||||||
if let Some(_i) = x.as_ref().borrow().get(&p) {
|
|
||||||
i = _i.clone();
|
|
||||||
} else {
|
|
||||||
return Ok(rust_lisp::model::Value::False);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Ok(rust_lisp::model::Value::False),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(rust_lisp::model::Value::True)
|
|
||||||
},
|
|
||||||
))),
|
|
||||||
);
|
|
||||||
let environment = environment;
|
|
||||||
let code = r#"(load ".container.geometry")"#;
|
|
||||||
let ast = rust_lisp::parser::parse(code).filter_map(|a| a.ok());
|
|
||||||
let result = rust_lisp::interpreter::eval_block(Rc::new(RefCell::new(environment)), ast);
|
|
||||||
println!("{:?}", result);
|
println!("{:?}", result);
|
||||||
|
if let Ok(rust_lisp::model::Value::True) = result {
|
||||||
|
return vec![(MessageType::Command, p.cmd.clone().into_bytes())];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vec::new()
|
Vec::new()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run<'a>(c: &mut Connection<'a>) -> Result<(), anyhow::Error> {
|
async fn run<'a>(connection: &mut Connection<'a>, config: &Config) -> Result<(), anyhow::Error> {
|
||||||
let resp = c.communicate(&MessageType::Version, b"").await?;
|
let resp = connection.communicate(&MessageType::Version, b"").await?;
|
||||||
println!("{:?}", resp);
|
println!("{:?}", resp);
|
||||||
|
|
||||||
c.communicate(&MessageType::Command, b"exec alacritty")
|
for p in config.programs.iter() {
|
||||||
.await?;
|
if let Some(r) = &p.run {
|
||||||
|
let (message_type, response) = connection.communicate(&MessageType::Command, r.as_bytes()).await?;
|
||||||
|
println!("{:?}", (message_type, response));
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,14 +50,20 @@ async fn main() -> Result<()> {
|
||||||
let mut connection = Connection::connect((i3ipc::get_socket_path().await?).as_ref())?;
|
let mut connection = Connection::connect((i3ipc::get_socket_path().await?).as_ref())?;
|
||||||
let mut sub_connection = connection.clone();
|
let mut sub_connection = connection.clone();
|
||||||
|
|
||||||
let b_ = true;
|
let mut config = String::new();
|
||||||
let cb = move |a, b| {new_window_cb(a,b,b_)};
|
tokio::fs::File::open("/home/redxef/CODE/i3toolwait/i3_autostart.yaml").await?.read_to_string(&mut config).await?;
|
||||||
|
let config: Config = serde_yaml::from_str(&config)?;
|
||||||
|
let config = std::sync::Arc::new(config);
|
||||||
|
|
||||||
|
let cb_config = config.clone();
|
||||||
|
let cb = move |a, b| {new_window_cb(a, b, &cb_config)};
|
||||||
sub_connection
|
sub_connection
|
||||||
.subscribe(&[MessageType::SubWindow], &cb)
|
.subscribe(&[MessageType::SubWindow], &cb)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
tokio::join!(
|
tokio::join!(
|
||||||
timeout(Duration::from_secs(1), sub_connection.run()),
|
timeout(Duration::from_millis(config.timeout as u64), sub_connection.run()),
|
||||||
run(&mut connection),
|
run(&mut connection, &config),
|
||||||
)
|
)
|
||||||
.1?;
|
.1?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Reference in a new issue