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"
|
||||
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"] }
|
||||
|
|
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 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<u8>)>> {
|
||||
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<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);
|
||||
for p in config_.programs.iter() {
|
||||
let e = lisp::env(&c);
|
||||
let init: Vec<rust_lisp::model::Value> = config_.init.clone().into();
|
||||
let prog: Vec<rust_lisp::model::Value> = 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(())
|
||||
|
|
Loading…
Reference in a new issue