diff --git a/Cargo.lock b/Cargo.lock index f54134c..bc8c8ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,6 +160,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -667,7 +680,7 @@ checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -700,6 +713,25 @@ dependencies = [ "version_check", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.1" @@ -1019,6 +1051,7 @@ name = "sampad" version = "0.1.0" dependencies = [ "bitvec", + "chrono", "hidapi", "rumqttc", "serde", @@ -1270,6 +1303,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1510,6 +1554,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 9d1477f..8954dc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,4 @@ serde = "1" serde_dhall = "0.11" tracing = "0.1.35" tracing-subscriber = "0.3.14" +chrono = "0.4.19" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..2dd7ac3 --- /dev/null +++ b/build.rs @@ -0,0 +1,13 @@ +use std::error::Error; +use std::process::Command; + +fn main() -> Result<(), Box> { + let hash = Command::new("git") + .arg("rev-parse") + .arg("--short") + .arg("HEAD") + .output()? + .stdout; + println!("cargo:rustc-env=VERSION={}", std::str::from_utf8(&hash)?); + Ok(()) +} diff --git a/src/config.rs b/src/config.rs index b3f99cd..e6388df 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,7 @@ +use crate::StdError; +use chrono::Utc; use serde::Deserialize; -use serde_dhall::StaticType; +use serde_dhall::{SimpleType, StaticType}; use std::collections::HashMap; use std::error::Error; use std::fmt; @@ -24,7 +26,11 @@ impl fmt::Display for Action { } } -#[derive(Deserialize, StaticType, Debug)] +// Newtype wrapper around a Vec of actions. +// serde(transparent) and the imp StaticType below ensures we ignore the outer struct and +// transparently deserialize into the inner vec. +#[derive(Deserialize, Debug)] +#[serde(transparent)] pub struct Actions(Vec); impl Actions { @@ -40,6 +46,12 @@ impl fmt::Display for Actions { } } +impl StaticType for Actions { + fn static_type() -> SimpleType { + SimpleType::List(Box::new(Action::static_type())) + } +} + #[derive(Deserialize, StaticType, Debug)] pub enum Mapping { NOP, // Do nothing. @@ -87,16 +99,25 @@ pub struct Config { impl Config { pub fn from_file(p: &Path) -> Result> { - match serde_dhall::from_file(p) - .with_builtin_type("Mapping".to_string(), Mapping::static_type()) - .with_builtin_type("Action".to_string(), Action::static_type()) + serde_dhall::from_file(p) .parse::() - { - Ok(c) => Ok(c), - Err(e) => { - println!("{}", e); - Err(Box::new(e)) - } - } + .map_err(|e| e.into()) + } + + pub fn generate_prelude() -> StdError<()> { + let mut m = HashMap::new(); + m.insert("Action".to_string(), Action::static_type()); + m.insert("Mapping".to_string(), Mapping::static_type()); + println!( + "-- Sampad Dhall types, autogenerated by git#{} on {}", + option_env!("VERSION").unwrap_or("unknown"), + Utc::now(), + ); + println!( + "{{ Action = {}, Mapping = {} }}", + Action::static_type(), + Mapping::static_type() + ); + Ok(()) } } diff --git a/src/main.rs b/src/main.rs index ac67286..93d7bbe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,14 @@ use config::{Action, Config, Mapping}; use hidapi::HidApi; use rumqttc::{Client, MqttOptions, QoS}; use std::collections::HashMap; +use std::env; use std::error::Error; use std::path::Path; use std::thread; use tracing::{event, Level}; +type StdError = Result>; + struct State<'a> { mqtt_servers: HashMap, conf: &'a Config, @@ -107,7 +110,19 @@ impl<'a> State<'a> { } } -fn main() -> Result<(), Box> { +fn main() -> StdError<()> { + let args: Vec<_> = env::args().collect(); + if let [prog, args @ ..] = &args[..] { + if args == ["generate-dhall"] { + return Config::generate_prelude(); + } else if args.len() > 0 { + eprintln!("Usage: {} [generate-dhall]", prog); + return Err("Invalid usage".into()); + } + } else { + unreachable!(); + } + tracing_subscriber::fmt::init(); event!(Level::INFO, "Starting..."); let conf = Config::from_file(Path::new("./config.dhall"))?; diff --git a/types.dhall b/types.dhall new file mode 100644 index 0000000..0f0e5c9 --- /dev/null +++ b/types.dhall @@ -0,0 +1,22 @@ +-- Sampad Dhall types, autogenerated by git#96d9ca3 on 2022-07-11 13:54:53.099283885 UTC +{ Action = + < ActivateLayer : Text + | MQTTPub : { payload : Text, topic : Text } + | Print : Text + > +, Mapping = + < NOP + | Passthrough + | Trigger : + < ActivateLayer : Text + | MQTTPub : { payload : Text, topic : Text } + | Print : Text + > + | TriggerMulti : + List + < ActivateLayer : Text + | MQTTPub : { payload : Text, topic : Text } + | Print : Text + > + > +}