Mvp finished, can run programs and move them to windows.

This commit is contained in:
redxef 2023-10-14 23:54:06 +02:00
parent 0f24273c93
commit 23dc47a008
Signed by: redxef
GPG key ID: 7DAC3AA211CBD921
4 changed files with 224 additions and 134 deletions

View file

@ -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
View 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
View 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
}

View file

@ -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);
println!("{:?}", result);
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(())