diff --git a/Cargo.toml b/Cargo.toml index f71e683..97dcbe9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,8 @@ edition = "2021" anyhow = "1.0.75" byteorder = "1.5.0" 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_json = "1.0.107" +serde_yaml = "0.9.25" tokio = { version = "1.33.0", features = ["full"] } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..1a9ff75 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,67 @@ +use serde::{Serialize, Deserialize, Serializer, Deserializer}; +use rust_lisp::model::Value as RValue; + +#[derive(Clone, Debug)] +pub struct Value(Vec); +unsafe impl Send for Value {} +unsafe impl Sync for Value {} + +impl Into for RValue { + fn into(self) -> Value { + Value(vec![self]) + } +} + +impl Into> for Value { + fn into(self) -> Vec { + self.0 + } +} + +impl<'de> Deserialize<'de> for Value { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de> + { + let s: String = Deserialize::deserialize(deserializer)?; + let r: Vec = 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, +} +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, +} +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 { + vec![] + } +} diff --git a/src/lisp.rs b/src/lisp.rs new file mode 100644 index 0000000..2715cc9 --- /dev/null +++ b/src/lisp.rs @@ -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, args: Vec| { + 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, args: Vec| { + 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 +} diff --git a/src/main.rs b/src/main.rs index 722368f..b2a1634 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,151 +1,47 @@ use anyhow::Result; -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; use tokio::time::{timeout, Duration}; +use tokio::io::AsyncReadExt; +mod config; mod i3ipc; +mod lisp; +use config::Config; 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( b: MessageType, c: serde_json::Value, - d: bool, + config: &Config, ) -> futures::future::BoxFuture<'static, Vec<(MessageType, Vec)>> { + let config_ = config.clone(); Box::pin(async move { - let mut environment = rust_lisp::default_env(); - environment.define( - rust_lisp::model::Symbol::from("__input__"), - serde_lisp_value(&c), - ); - environment.define( - rust_lisp::model::Symbol::from("load"), - rust_lisp::model::Value::NativeClosure(Rc::new(RefCell::new( - move |e: Rc>, args: Vec| { - 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>, args: Vec| { - 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); + for p in config_.programs.iter() { + let e = lisp::env(&c); + let init: Vec = config_.init.clone().into(); + let prog: Vec = p.match_.clone().into(); + let m = init.into_iter().chain(prog.into_iter()); + let result = rust_lisp::interpreter::eval_block(rust_lisp::model::reference::new(e), m); + println!("{:?}", result); + if let Ok(rust_lisp::model::Value::True) = result { + return vec![(MessageType::Command, p.cmd.clone().into_bytes())]; + } + } Vec::new() }) } -async fn run<'a>(c: &mut Connection<'a>) -> Result<(), anyhow::Error> { - let resp = c.communicate(&MessageType::Version, b"").await?; +async fn run<'a>(connection: &mut Connection<'a>, config: &Config) -> Result<(), anyhow::Error> { + let resp = connection.communicate(&MessageType::Version, b"").await?; println!("{:?}", resp); - c.communicate(&MessageType::Command, b"exec alacritty") - .await?; + for p in config.programs.iter() { + if let Some(r) = &p.run { + let (message_type, response) = connection.communicate(&MessageType::Command, r.as_bytes()).await?; + println!("{:?}", (message_type, response)); + } + } Ok(()) } @@ -154,14 +50,20 @@ async fn main() -> Result<()> { let mut connection = Connection::connect((i3ipc::get_socket_path().await?).as_ref())?; let mut sub_connection = connection.clone(); - let b_ = true; - let cb = move |a, b| {new_window_cb(a,b,b_)}; + let mut config = String::new(); + 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 .subscribe(&[MessageType::SubWindow], &cb) .await?; + tokio::join!( - timeout(Duration::from_secs(1), sub_connection.run()), - run(&mut connection), + timeout(Duration::from_millis(config.timeout as u64), sub_connection.run()), + run(&mut connection, &config), ) .1?; Ok(())