From 170bfca2ec8b3ec9b28e59c546774ce2ff092c54 Mon Sep 17 00:00:00 2001 From: Rich Neswold Date: Wed, 9 Aug 2023 21:16:47 -0500 Subject: [PATCH 1/8] :see_no_evil: add local files that shouldn't be added These are files that are on my system. If the project starts building a community, these entries will have to be removed and I'll move them to a different location. --- .gitignore | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.gitignore b/.gitignore index ea8c4bf..d5fb21a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,20 @@ /target + +# These are local files of the author that shouldn't get included with +# the project. + +CHAT-GPT.md +test.sh +watch.sh +build-dev.sh +build-release.sh +publish-api.sh +publish-backends.sh +publish-drivers.sh +publish-drmemd.sh +run-clippy.sh +run-tests.sh +test-publish-api.sh +test-publish-backends.sh +test-publish-drivers.sh +test-publish-drmemd.sh From be07188c2cbe17808bd51724eabe129a660611d1 Mon Sep 17 00:00:00 2001 From: Rich Neswold Date: Wed, 9 Aug 2023 22:01:31 -0500 Subject: [PATCH 2/8] :recycle: refactor the `Error` type - rename `DbCommunicationError` to `BackendError` - add a `String` parameter to `BackendError` - add a `String` parameter to `OperationError` - remove `UnknownError` --- backends/drmem-db-redis/src/lib.rs | 12 ++++-------- backends/drmem-db-simple/src/lib.rs | 6 ++++-- drivers/drmem-drv-ntp/src/lib.rs | 2 +- drivers/drmem-drv-weather-wu/src/lib.rs | 2 +- drmem-api/src/types/mod.rs | 24 +++++++++--------------- drmemd/src/logic/mod.rs | 2 +- drmemd/src/main.rs | 8 +++++--- 7 files changed, 25 insertions(+), 31 deletions(-) diff --git a/backends/drmem-db-redis/src/lib.rs b/backends/drmem-db-redis/src/lib.rs index 8f5d62c..638280f 100644 --- a/backends/drmem-db-redis/src/lib.rs +++ b/backends/drmem-db-redis/src/lib.rs @@ -41,12 +41,6 @@ pub mod config; fn xlat_err(e: redis::RedisError) -> Error { match e.kind() { - redis::ErrorKind::ResponseError - | redis::ErrorKind::ClusterDown - | redis::ErrorKind::CrossSlot - | redis::ErrorKind::MasterDown - | redis::ErrorKind::IoError => Error::DbCommunicationError, - redis::ErrorKind::AuthenticationFailed | redis::ErrorKind::InvalidClientConfig => Error::AuthenticationError, @@ -57,13 +51,15 @@ fn xlat_err(e: redis::RedisError) -> Error { | redis::ErrorKind::TryAgain | redis::ErrorKind::ClientError | redis::ErrorKind::ExtensionError - | redis::ErrorKind::ReadOnly => Error::OperationError, + | redis::ErrorKind::ReadOnly => { + Error::OperationError("backend is read-only".to_owned()) + } redis::ErrorKind::NoScriptError | redis::ErrorKind::Moved | redis::ErrorKind::Ask => Error::NotFound, - _ => Error::UnknownError, + _ => Error::BackendError(format!("{}", &e)), } } diff --git a/backends/drmem-db-simple/src/lib.rs b/backends/drmem-db-simple/src/lib.rs index 6d42980..afb0baa 100644 --- a/backends/drmem-db-simple/src/lib.rs +++ b/backends/drmem-db-simple/src/lib.rs @@ -335,7 +335,7 @@ impl Store for SimpleStore { )), } } else { - Err(Error::OperationError) + Err(Error::OperationError(format!("{} is read-only", &name))) } } else { Err(Error::NotFound) @@ -457,7 +457,9 @@ impl Store for SimpleStore { } } } else { - Err(Error::OperationError) + Err(Error::OperationError( + "unable to lock reading channel".to_owned(), + )) } } else { Err(Error::NotFound) diff --git a/drivers/drmem-drv-ntp/src/lib.rs b/drivers/drmem-drv-ntp/src/lib.rs index 9539d8b..f51a55c 100644 --- a/drivers/drmem-drv-ntp/src/lib.rs +++ b/drivers/drmem-drv-ntp/src/lib.rs @@ -422,7 +422,7 @@ impl driver::API for Instance { return Ok(Box::new(Instance { sock, seq: 1 })); } } - Err(Error::OperationError) + Err(Error::OperationError("couldn't create socket".to_owned())) }; Box::pin(fut) diff --git a/drivers/drmem-drv-weather-wu/src/lib.rs b/drivers/drmem-drv-weather-wu/src/lib.rs index 40ef56f..6c81849 100644 --- a/drivers/drmem-drv-weather-wu/src/lib.rs +++ b/drivers/drmem-drv-weather-wu/src/lib.rs @@ -100,7 +100,7 @@ impl Instance { )) } else { error!("couldn't determine public API key"); - Err(Error::UnknownError) + Err(Error::NotFound) } } } diff --git a/drmem-api/src/types/mod.rs b/drmem-api/src/types/mod.rs index c21f3ee..422a780 100644 --- a/drmem-api/src/types/mod.rs +++ b/drmem-api/src/types/mod.rs @@ -33,10 +33,9 @@ pub enum Error { /// An invalid value was provided. InvArgument(String), - /// Returned when a communication error occurred with the backend - /// database. Each backend will have its own recommendations on - /// how to recover. - DbCommunicationError, + /// A general error returned by the backend storage. The string + /// will have more information about the error. + BackendError(String), /// Communication was disrupted due to one end not following a /// protocol. @@ -51,7 +50,7 @@ pub enum Error { /// The requested operation couldn't complete. The description /// field will have more information for the user. - OperationError, + OperationError(String), /// A bad parameter was given in a configuration or a /// configuration was missing a required parameter. @@ -60,10 +59,6 @@ pub enum Error { /// There was a problem parsing a string. The associated string /// will describe how the parsing failed. ParseError(String), - - /// A dependent library introduced a new error that hasn't been - /// properly mapped in DrMem. This needs to be reported as a bug. - UnknownError, } impl std::error::Error for Error {} @@ -80,19 +75,18 @@ impl fmt::Display for Error { write!(f, "{} is missing peer", detail) } Error::TypeError => write!(f, "incorrect type"), - Error::InvArgument(s) => write!(f, "{}", s), - Error::DbCommunicationError => { - write!(f, "db communication error") + Error::InvArgument(v) => write!(f, "{}", &v), + Error::BackendError(v) => { + write!(f, "backend error: {}", &v) } Error::ProtocolError(v) => write!(f, "protocol error: {}", &v), Error::AuthenticationError => write!(f, "permission error"), Error::TimeoutError => write!(f, "timeout"), - Error::OperationError => { - write!(f, "couldn't complete operation") + Error::OperationError(v) => { + write!(f, "couldn't complete operation: {}", &v) } Error::BadConfig(v) => write!(f, "config error: {}", &v), Error::ParseError(v) => write!(f, "parse error: {}", &v), - Error::UnknownError => write!(f, "unhandled error"), } } } diff --git a/drmemd/src/logic/mod.rs b/drmemd/src/logic/mod.rs index b91540d..8310124 100644 --- a/drmemd/src/logic/mod.rs +++ b/drmemd/src/logic/mod.rs @@ -226,7 +226,7 @@ impl Node { // problem and return an error. error!("all inputs have closed ... terminating"); - Err(Error::OperationError) + Err(Error::OperationError("no available inputs".to_owned())) } // Starts a new instance of a logic node. diff --git a/drmemd/src/main.rs b/drmemd/src/main.rs index 90251fa..4f5b5a6 100644 --- a/drmemd/src/main.rs +++ b/drmemd/src/main.rs @@ -60,12 +60,12 @@ async fn wrap_task( match handle.await { Err(e) if e.is_panic() => { error!("terminated due to panic"); - Err(Error::OperationError) + Err(Error::OperationError("task panicked".to_owned())) } Err(_) => { error!("terminated due to cancellation"); - Err(Error::OperationError) + Err(Error::OperationError("task was canceled".to_owned())) } Ok(Ok(_)) => unreachable!(), @@ -110,7 +110,9 @@ async fn run() -> Result<()> { drv_tbl.clone(), tx_clnt_req.clone(), ) - .then(|_| async { Err(Error::OperationError) }); + .then(|_| async { + Err(Error::OperationError("graphql server exited".to_owned())) + }); tasks.push(wrap_task(tokio::spawn(f))); } From 29e8f48c82e0d4a64056eebcab279b7e3d8dded1 Mon Sep 17 00:00:00 2001 From: Rich Neswold Date: Wed, 9 Aug 2023 22:17:07 -0500 Subject: [PATCH 3/8] :pencil2: slight improvements to docs --- drmem-api/src/client.rs | 6 +++--- drmem-api/src/driver.rs | 4 ++-- drmem-api/src/lib.rs | 3 ++- drmem-api/src/types/device/mod.rs | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drmem-api/src/client.rs b/drmem-api/src/client.rs index 40a15e1..a96863e 100644 --- a/drmem-api/src/client.rs +++ b/drmem-api/src/client.rs @@ -1,6 +1,6 @@ -//! This module defines types and interfaces that internal clients use -//! to interact with the core of DrMem. The primary, internal client -//! is the GraphQL interface. +//! Defines types and interfaces that internal clients use to interact +//! with the core of DrMem. The primary, internal client is the +//! GraphQL interface, but asks in logic blocks also use this module. //! //! Any new, internal tasks that need access to device readings or //! wish to set the value of the device need to have a diff --git a/drmem-api/src/driver.rs b/drmem-api/src/driver.rs index df0fdd7..8350531 100644 --- a/drmem-api/src/driver.rs +++ b/drmem-api/src/driver.rs @@ -1,5 +1,5 @@ -//! This module defines types and interfaces that drivers use to -//! interact with the core of DrMem. +//! Defines types and interfaces that drivers use to interact with the +//! core of DrMem. use crate::types::{ device::{Base, Name, Path, Value}, diff --git a/drmem-api/src/lib.rs b/drmem-api/src/lib.rs index 8e58d53..052a122 100644 --- a/drmem-api/src/lib.rs +++ b/drmem-api/src/lib.rs @@ -1,4 +1,5 @@ -//! This crate is used by various, internal tasks of `drmemd`. +//! This crate is used by internal tasks of `drmemd` as well as +//! hardware drivers. //! //! The interfaces and types defined in this crate are useful for //! those wishing to write a new back-end storage module or a driver diff --git a/drmem-api/src/types/device/mod.rs b/drmem-api/src/types/device/mod.rs index 96ed24e..911420b 100644 --- a/drmem-api/src/types/device/mod.rs +++ b/drmem-api/src/types/device/mod.rs @@ -1,4 +1,5 @@ -//! This module defines types related to devices. +//! Defines types related to devices. + use std::{pin::Pin, time}; use tokio_stream::Stream; From c2ace1b6707436fadf7d3c56fb3c60cfdc08ca20 Mon Sep 17 00:00:00 2001 From: Rich Neswold Date: Wed, 9 Aug 2023 22:32:24 -0500 Subject: [PATCH 4/8] :arrow_up: get `tokio` v1.30.0 --- Cargo.lock | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d697f50..25d9697 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1434,9 +1434,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -2046,18 +2046,17 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.29.1" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "2d3ce25f50619af8b0aec2eb23deebe84249e19e2ddd393a6e16e3300a6dadfd" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.5.3", "tokio-macros", "windows-sys", ] From a70b184db85b9aa129530ec0ead5807fff736ff7 Mon Sep 17 00:00:00 2001 From: Rich Neswold Date: Wed, 9 Aug 2023 22:44:48 -0500 Subject: [PATCH 5/8] :art: remove borrows Suggested by clippy. --- drmemd/src/driver/drv_cycle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drmemd/src/driver/drv_cycle.rs b/drmemd/src/driver/drv_cycle.rs index 71e3cf9..0bb3092 100644 --- a/drmemd/src/driver/drv_cycle.rs +++ b/drmemd/src/driver/drv_cycle.rs @@ -188,7 +188,7 @@ impl Instance { // If the output is already at the desired value, // don't emit it again. - if &self.disabled != &self.enabled[self.index] { + if self.disabled != self.enabled[self.index] { Some(self.disabled.clone()) } else { None From 1b4175012784de245d2f87a84f95062eed89857b Mon Sep 17 00:00:00 2001 From: Rich Neswold Date: Wed, 9 Aug 2023 22:46:59 -0500 Subject: [PATCH 6/8] :art: remove `-> ()` from function Unit return values are the default, so clippy recommends not specifying it. --- drmemd/src/logic/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drmemd/src/logic/mod.rs b/drmemd/src/logic/mod.rs index 8310124..fcb6223 100644 --- a/drmemd/src/logic/mod.rs +++ b/drmemd/src/logic/mod.rs @@ -40,7 +40,7 @@ impl Output { // Attempts to set the associated device to a new value. - pub async fn send(&mut self, value: device::Value) -> () { + pub async fn send(&mut self, value: device::Value) { // Only attempt the setting if the device isn't set to the // value. From cb10028c62430370eaf8da95b28e134140c2cf7c Mon Sep 17 00:00:00 2001 From: Rich Neswold Date: Wed, 9 Aug 2023 22:56:21 -0500 Subject: [PATCH 7/8] :bug: another allowed header for GraphQL subscriptions --- drmemd/src/graphql/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drmemd/src/graphql/mod.rs b/drmemd/src/graphql/mod.rs index 933fbdf..8109b2c 100644 --- a/drmemd/src/graphql/mod.rs +++ b/drmemd/src/graphql/mod.rs @@ -671,7 +671,10 @@ pub fn server( .with( warp::cors() .allow_any_origin() - .allow_headers(vec!["content-type"]) + .allow_headers(vec![ + "content-type", + "Access-Control-Allow-Origin", + ]) .allow_methods(vec!["OPTIONS", "GET", "POST"]) .max_age(Duration::from_secs(3_600)), ); From e7c6a77db3d1dc00c5ec787671fc36517ad0bde7 Mon Sep 17 00:00:00 2001 From: Rich Neswold Date: Wed, 9 Aug 2023 23:45:02 -0500 Subject: [PATCH 8/8] :bug: don't scale "millis" parameter When the `cycle` driver simply emitted a square wave, the "millis" parameter indicated the whole cycle so we divided it by 2. Now that it can be a longer sequence, it just indicates the length of time each value affects the output. Added unit tests to make sure this, and other configuration conditions, are correct. --- drmemd/src/driver/drv_cycle.rs | 128 ++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/drmemd/src/driver/drv_cycle.rs b/drmemd/src/driver/drv_cycle.rs index 0bb3092..eeae516 100644 --- a/drmemd/src/driver/drv_cycle.rs +++ b/drmemd/src/driver/drv_cycle.rs @@ -73,7 +73,7 @@ impl Instance { // drmem-api crate indicating the max sample rate? if (50..=3_600_000).contains(millis) { - Ok(time::Duration::from_millis(*millis as u64 / 2)) + Ok(time::Duration::from_millis(*millis as u64)) } else { Err(Error::BadConfig(String::from("'millis' out of range"))) } @@ -327,6 +327,132 @@ impl driver::API for Instance { #[cfg(test)] mod tests { use super::*; + use drmem_api::driver::API; + use std::time::Duration; + + #[tokio::test] + async fn test_cfg() { + { + let mut cfg = DriverConfig::new(); + + cfg.insert("millis".to_owned(), toml::value::Value::Integer(500)); + cfg.insert( + "enabled_at_boot".to_owned(), + toml::value::Value::Boolean(true), + ); + cfg.insert( + "disabled".to_owned(), + toml::value::Value::Boolean(false), + ); + cfg.insert( + "enabled".to_owned(), + toml::value::Value::Array(vec![ + toml::value::Value::Boolean(true), + toml::value::Value::Boolean(false), + ]), + ); + + let inst = Instance::create_instance(&cfg).await.unwrap(); + + assert_eq!(inst.enabled_at_boot, true); + assert_eq!(inst.millis, Duration::from_millis(500)); + assert_eq!(inst.disabled, device::Value::Bool(false)); + assert_eq!( + inst.enabled, + vec![device::Value::Bool(true), device::Value::Bool(false)] + ); + } + + { + let mut cfg = DriverConfig::new(); + + cfg.insert("millis".to_owned(), toml::value::Value::Integer(500)); + cfg.insert( + "disabled".to_owned(), + toml::value::Value::Boolean(false), + ); + cfg.insert( + "enabled".to_owned(), + toml::value::Value::Array(vec![ + toml::value::Value::Boolean(true), + toml::value::Value::Boolean(false), + ]), + ); + + let inst = Instance::create_instance(&cfg).await.unwrap(); + + assert_eq!(inst.enabled_at_boot, false); + assert_eq!(inst.millis, Duration::from_millis(500)); + assert_eq!(inst.disabled, device::Value::Bool(false)); + assert_eq!( + inst.enabled, + vec![device::Value::Bool(true), device::Value::Bool(false)] + ); + } + + { + let mut cfg = DriverConfig::new(); + + cfg.insert( + "disabled".to_owned(), + toml::value::Value::Boolean(false), + ); + cfg.insert( + "enabled".to_owned(), + toml::value::Value::Array(vec![ + toml::value::Value::Boolean(true), + toml::value::Value::Boolean(false), + ]), + ); + + assert!(Instance::create_instance(&cfg).await.is_err()); + } + + { + let mut cfg = DriverConfig::new(); + + cfg.insert("millis".to_owned(), toml::value::Value::Integer(500)); + cfg.insert( + "enabled".to_owned(), + toml::value::Value::Array(vec![ + toml::value::Value::Boolean(true), + toml::value::Value::Boolean(false), + ]), + ); + + assert!(Instance::create_instance(&cfg).await.is_err()); + } + + { + let mut cfg = DriverConfig::new(); + + cfg.insert("millis".to_owned(), toml::value::Value::Integer(500)); + cfg.insert( + "disabled".to_owned(), + toml::value::Value::Boolean(false), + ); + + assert!(Instance::create_instance(&cfg).await.is_err()); + } + + { + let mut cfg = DriverConfig::new(); + + cfg.insert("millis".to_owned(), toml::value::Value::Integer(500)); + cfg.insert( + "disabled".to_owned(), + toml::value::Value::Boolean(false), + ); + cfg.insert( + "enabled".to_owned(), + toml::value::Value::Array(vec![toml::value::Value::Boolean( + true, + )]), + ); + + assert!(Instance::create_instance(&cfg).await.is_err()); + } + } #[test] fn test_state_changes() {