From 116afe78fd44531278304967c6445754f03675fa Mon Sep 17 00:00:00 2001 From: glyph Date: Tue, 23 Nov 2021 10:51:42 +0200 Subject: [PATCH 1/5] remove snafu and bump deps --- peach-oled/Cargo.toml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/peach-oled/Cargo.toml b/peach-oled/Cargo.toml index 680e267..3da3807 100644 --- a/peach-oled/Cargo.toml +++ b/peach-oled/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "peach-oled" -version = "0.1.3" +version = "0.1.4" authors = ["Andrew Reid "] edition = "2018" description = "Write and draw to OLED display using JSON-RPC over HTTP." @@ -27,18 +27,16 @@ travis-ci = { repository = "peachcloud/peach-oled", branch = "master" } maintenance = { status = "actively-developed" } [dependencies] -jsonrpc-core = "11.0.0" -jsonrpc-http-server = "11.0.0" -linux-embedded-hal = "0.2.2" embedded-graphics = "0.4.7" -tinybmp = "0.1.0" -ssd1306 = "0.2.6" -serde = { version = "1.0.87", features = ["derive"] } -serde_json = "1.0.39" -log = "0.4.0" -env_logger = "0.6.1" -snafu = "0.4.1" +env_logger = "0.9" +jsonrpc-core = "18" +jsonrpc-http-server = "18" +linux-embedded-hal = "0.2.2" +log = "0.4" +serde = { version = "1", features = ["derive"] } nix="0.11" +ssd1306 = "0.2.6" +tinybmp = "0.1.0" [dev-dependencies] -jsonrpc-test = "11.0.0" +jsonrpc-test = "18" From 8d18e712a19238e689a593809807cc6a25d1e241 Mon Sep 17 00:00:00 2001 From: glyph Date: Tue, 23 Nov 2021 10:51:54 +0200 Subject: [PATCH 2/5] implement custom error --- peach-oled/src/error.rs | 88 ++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/peach-oled/src/error.rs b/peach-oled/src/error.rs index 1926d54..3f5264c 100644 --- a/peach-oled/src/error.rs +++ b/peach-oled/src/error.rs @@ -1,44 +1,68 @@ -use std::error; +use std::{error, fmt}; -use jsonrpc_core::{types::error::Error, ErrorCode}; -use linux_embedded_hal as hal; -use snafu::Snafu; +use jsonrpc_core::types::error::Error as JsonRpcError; +use jsonrpc_core::ErrorCode; +use linux_embedded_hal::i2cdev::linux::LinuxI2CError; -pub type BoxError = Box; - -#[derive(Debug, Snafu)] -#[snafu(visibility(pub(crate)))] +#[derive(Debug)] pub enum OledError { - #[snafu(display("Failed to create interface for I2C device: {}", source))] I2CError { - source: hal::i2cdev::linux::LinuxI2CError, + source: LinuxI2CError, }, - - #[snafu(display("Coordinate {} out of range {}: {}", coord, range, value))] InvalidCoordinate { coord: String, range: String, value: i32, }, - - // TODO: implement for validate() in src/lib.rs - #[snafu(display("Font size invalid: {}", font))] - InvalidFontSize { font: String }, - - #[snafu(display("String length out of range 0-21: {}", len))] - InvalidString { len: usize }, - - #[snafu(display("Missing expected parameter: {}", e))] - MissingParameter { e: Error }, - - #[snafu(display("Failed to parse parameter: {}", e))] - ParseError { e: Error }, + InvalidFontSize { + font: String, + }, + InvalidString { + len: usize, + }, + MissingParameter { + source: JsonRpcError, + }, + ParseError { + source: JsonRpcError, + }, } -impl From for Error { +impl error::Error for OledError {} + +impl fmt::Display for OledError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + OledError::ParseError { ref source } => { + write!(f, "Failed to parse parameter: {}", source) + } + OledError::MissingParameter { ref source } => { + write!(f, "Missing expected parameter: {}", source) + } + OledError::InvalidString { len } => { + write!(f, "String length out of range 0-21: {}", len) + } + OledError::InvalidFontSize { ref font } => { + write!(f, "Invalid font size: {}", font) + } + OledError::InvalidCoordinate { + ref coord, + ref range, + value, + } => { + write!(f, "Coordinate {} out of range {}: {}", coord, range, value) + } + OledError::I2CError { ref source } => { + write!(f, "Failed to create interface for I2C device: {}", source) + } + } + } +} + +impl From for JsonRpcError { fn from(err: OledError) -> Self { match &err { - OledError::I2CError { source } => Error { + OledError::I2CError { source } => JsonRpcError { code: ErrorCode::ServerError(-32000), message: format!("Failed to create interface for I2C device: {}", source), data: None, @@ -47,7 +71,7 @@ impl From for Error { coord, value, range, - } => Error { + } => JsonRpcError { code: ErrorCode::ServerError(-32001), message: format!( "Validation error: coordinate {} out of range {}: {}", @@ -55,18 +79,18 @@ impl From for Error { ), data: None, }, - OledError::InvalidFontSize { font } => Error { + OledError::InvalidFontSize { font } => JsonRpcError { code: ErrorCode::ServerError(-32002), message: format!("Validation error: {} is not an accepted font size. Use 6x8, 6x12, 8x16 or 12x16 instead", font), data: None, }, - OledError::InvalidString { len } => Error { + OledError::InvalidString { len } => JsonRpcError { code: ErrorCode::ServerError(-32003), message: format!("Validation error: string length {} out of range 0-21", len), data: None, }, - OledError::MissingParameter { e } => e.clone(), - OledError::ParseError { e } => e.clone(), + OledError::MissingParameter { source } => source.clone(), + OledError::ParseError { source } => source.clone(), } } } From 7344d6f4e06703b25975cd4610d159093195a892 Mon Sep 17 00:00:00 2001 From: glyph Date: Tue, 23 Nov 2021 10:52:26 +0200 Subject: [PATCH 3/5] minor variable name changes and updated error handling --- peach-oled/src/lib.rs | 169 ++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 98 deletions(-) diff --git a/peach-oled/src/lib.rs b/peach-oled/src/lib.rs index d66dc5f..eb012e6 100644 --- a/peach-oled/src/lib.rs +++ b/peach-oled/src/lib.rs @@ -6,23 +6,23 @@ use std::{ sync::{Arc, Mutex}, }; -use embedded_graphics::coord::Coord; -use embedded_graphics::fonts::{Font12x16, Font6x12, Font6x8, Font8x16}; -use embedded_graphics::image::Image1BPP; -use embedded_graphics::prelude::*; +use embedded_graphics::{ + coord::Coord, + fonts::{Font12x16, Font6x12, Font6x8, Font8x16}, + image::Image1BPP, + prelude::*, +}; use hal::I2cdev; use jsonrpc_core::{types::error::Error, IoHandler, Params, Value}; use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; use linux_embedded_hal as hal; use log::{debug, error, info}; use serde::Deserialize; -use snafu::{ensure, ResultExt}; -use ssd1306::prelude::*; -use ssd1306::Builder; +use ssd1306::{prelude::*, Builder}; -use crate::error::{BoxError, I2CError, InvalidCoordinate, InvalidString, OledError}; +use crate::error::OledError; -//define the Graphic struct for receiving draw commands +// define the Graphic struct for receiving draw commands #[derive(Debug, Deserialize)] pub struct Graphic { bytes: Vec, @@ -32,7 +32,7 @@ pub struct Graphic { y_coord: i32, } -//define the Msg struct for receiving write commands +// define the Msg struct for receiving write commands #[derive(Debug, Deserialize)] pub struct Msg { x_coord: i32, @@ -41,86 +41,61 @@ pub struct Msg { font_size: String, } -//definte the On struct for receiving power on/off commands +// definte the On struct for receiving power on/off commands #[derive(Debug, Deserialize)] pub struct On { on: bool, } -fn validate(m: &Msg) -> Result<(), OledError> { - ensure!( - m.string.len() <= 21, - InvalidString { - len: m.string.len() - } - ); - - ensure!( - m.x_coord >= 0, - InvalidCoordinate { +fn validate(msg: &Msg) -> Result<(), OledError> { + if msg.string.len() > 21 { + Err(OledError::InvalidString { + len: msg.string.len(), + }) + } else if msg.x_coord < 0 || msg.x_coord > 128 { + Err(OledError::InvalidCoordinate { coord: "x".to_string(), range: "0-128".to_string(), - value: m.x_coord, - } - ); - - ensure!( - m.x_coord < 129, - InvalidCoordinate { - coord: "x".to_string(), - range: "0-128".to_string(), - value: m.x_coord, - } - ); - - ensure!( - m.y_coord >= 0, - InvalidCoordinate { + value: msg.x_coord, + }) + } else if msg.y_coord < 0 || msg.y_coord > 147 { + Err(OledError::InvalidCoordinate { coord: "y".to_string(), range: "0-47".to_string(), - value: m.y_coord, - } - ); - - ensure!( - m.y_coord < 148, - InvalidCoordinate { - coord: "y".to_string(), - range: "0-47".to_string(), - value: m.y_coord, - } - ); - - Ok(()) + value: msg.y_coord, + }) + } else { + Ok(()) + } } -pub fn run() -> Result<(), BoxError> { +pub fn run() -> Result<(), OledError> { info!("Starting up."); debug!("Creating interface for I2C device."); - let i2c = I2cdev::new("/dev/i2c-1").context(I2CError)?; + let i2c = I2cdev::new("/dev/i2c-1").map_err(|source| OledError::I2CError { source })?; - let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into(); + let mut display: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into(); info!("Initializing the display."); - disp.init().unwrap_or_else(|_| { + display.init().unwrap_or_else(|_| { error!("Problem initializing the OLED display."); process::exit(1); }); debug!("Flushing the display."); - disp.flush().unwrap_or_else(|_| { + display.flush().unwrap_or_else(|_| { error!("Problem flushing the OLED display."); process::exit(1); }); - let oled = Arc::new(Mutex::new(disp)); + let oled = Arc::new(Mutex::new(display)); let oled_clone = Arc::clone(&oled); info!("Creating JSON-RPC I/O handler."); let mut io = IoHandler::default(); - io.add_method("clear", move |_| { + io.add_sync_method("clear", move |_| { let mut oled = oled_clone.lock().unwrap(); info!("Clearing the display."); oled.clear(); @@ -134,21 +109,20 @@ pub fn run() -> Result<(), BoxError> { let oled_clone = Arc::clone(&oled); - io.add_method("draw", move |params: Params| { - let g: Result = params.parse(); - let g: Graphic = g?; + io.add_sync_method("draw", move |params: Params| { + let graphic: Graphic = params.parse()?; // TODO: add simple byte validation function let mut oled = oled_clone.lock().unwrap(); info!("Drawing image to the display."); - let im = - Image1BPP::new(&g.bytes, g.width, g.height).translate(Coord::new(g.x_coord, g.y_coord)); - oled.draw(im.into_iter()); + let image = Image1BPP::new(&graphic.bytes, graphic.width, graphic.height) + .translate(Coord::new(graphic.x_coord, graphic.y_coord)); + oled.draw(image.into_iter()); Ok(Value::String("success".into())) }); let oled_clone = Arc::clone(&oled); - io.add_method("flush", move |_| { + io.add_sync_method("flush", move |_| { let mut oled = oled_clone.lock().unwrap(); info!("Flushing the display."); oled.flush().unwrap_or_else(|_| { @@ -160,9 +134,9 @@ pub fn run() -> Result<(), BoxError> { let oled_clone = Arc::clone(&oled); - io.add_method("ping", |_| Ok(Value::String("success".to_string()))); + io.add_sync_method("ping", |_| Ok(Value::String("success".to_string()))); - io.add_method("power", move |params: Params| { + io.add_sync_method("power", move |params: Params| { let o: Result = params.parse(); let o: On = o?; let mut oled = oled_clone.lock().unwrap(); @@ -180,37 +154,36 @@ pub fn run() -> Result<(), BoxError> { let oled_clone = Arc::clone(&oled); - io.add_method("write", move |params: Params| { + io.add_sync_method("write", move |params: Params| { info!("Received a 'write' request."); - let m: Result = params.parse(); - let m: Msg = m?; - validate(&m)?; + let msg = params.parse()?; + validate(&msg)?; let mut oled = oled_clone.lock().unwrap(); info!("Writing to the display."); - if m.font_size == "6x8" { + if msg.font_size == "6x8" { oled.draw( - Font6x8::render_str(&m.string) - .translate(Coord::new(m.x_coord, m.y_coord)) + Font6x8::render_str(&msg.string) + .translate(Coord::new(msg.x_coord, msg.y_coord)) .into_iter(), ); - } else if m.font_size == "6x12" { + } else if msg.font_size == "6x12" { oled.draw( - Font6x12::render_str(&m.string) - .translate(Coord::new(m.x_coord, m.y_coord)) + Font6x12::render_str(&msg.string) + .translate(Coord::new(msg.x_coord, msg.y_coord)) .into_iter(), ); - } else if m.font_size == "8x16" { + } else if msg.font_size == "8x16" { oled.draw( - Font8x16::render_str(&m.string) - .translate(Coord::new(m.x_coord, m.y_coord)) + Font8x16::render_str(&msg.string) + .translate(Coord::new(msg.x_coord, msg.y_coord)) .into_iter(), ); - } else if m.font_size == "12x16" { + } else if msg.font_size == "12x16" { oled.draw( - Font12x16::render_str(&m.string) - .translate(Coord::new(m.x_coord, m.y_coord)) + Font12x16::render_str(&msg.string) + .translate(Coord::new(msg.x_coord, msg.y_coord)) .into_iter(), ); } @@ -255,7 +228,7 @@ mod tests { fn rpc_success() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_success_response", |_| { + io.add_sync_method("rpc_success_response", |_| { Ok(Value::String("success".into())) }); test_rpc::Rpc::from(io) @@ -269,7 +242,7 @@ mod tests { fn rpc_internal_error() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_internal_error", |_| Err(Error::internal_error())); + io.add_sync_method("rpc_internal_error", |_| Err(Error::internal_error())); test_rpc::Rpc::from(io) }; @@ -287,7 +260,7 @@ mod tests { fn rpc_i2c_io_error() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_i2c_io_error", |_| { + io.add_sync_method("rpc_i2c_io_error", |_| { let io_err = IoError::new(ErrorKind::PermissionDenied, "oh no!"); let source = LinuxI2CError::Io(io_err); Err(Error::from(OledError::I2CError { source })) @@ -310,7 +283,7 @@ mod tests { fn rpc_i2c_nix_error() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_i2c_nix_error", |_| { + io.add_sync_method("rpc_i2c_nix_error", |_| { let nix_err = NixError::InvalidPath; let source = LinuxI2CError::Nix(nix_err); Err(Error::from(OledError::I2CError { source })) @@ -326,14 +299,14 @@ mod tests { }"# ); } - */ + */ // test to ensure correct InvalidCoordinate error response #[test] fn rpc_invalid_coord() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_invalid_coord", |_| { + io.add_sync_method("rpc_invalid_coord", |_| { Err(Error::from(OledError::InvalidCoordinate { coord: "x".to_string(), range: "0-128".to_string(), @@ -357,7 +330,7 @@ mod tests { fn rpc_invalid_fontsize() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_invalid_fontsize", |_| { + io.add_sync_method("rpc_invalid_fontsize", |_| { Err(Error::from(OledError::InvalidFontSize { font: "24x32".to_string(), })) @@ -379,7 +352,7 @@ mod tests { fn rpc_invalid_string() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_invalid_string", |_| { + io.add_sync_method("rpc_invalid_string", |_| { Err(Error::from(OledError::InvalidString { len: 22 })) }); test_rpc::Rpc::from(io) @@ -399,15 +372,15 @@ mod tests { fn rpc_invalid_params() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_invalid_params", |_| { - let e = Error { + io.add_sync_method("rpc_invalid_params", |_| { + let source = Error { code: ErrorCode::InvalidParams, message: String::from("invalid params"), data: Some(Value::String( "Invalid params: invalid type: null, expected struct Msg.".into(), )), }; - Err(Error::from(OledError::MissingParameter { e })) + Err(Error::from(OledError::MissingParameter { source })) }); test_rpc::Rpc::from(io) }; @@ -427,13 +400,13 @@ mod tests { fn rpc_parse_error() { let rpc = { let mut io = IoHandler::new(); - io.add_method("rpc_parse_error", |_| { - let e = Error { + io.add_sync_method("rpc_parse_error", |_| { + let source = Error { code: ErrorCode::ParseError, message: String::from("Parse error"), data: None, }; - Err(Error::from(OledError::ParseError { e })) + Err(Error::from(OledError::ParseError { source })) }); test_rpc::Rpc::from(io) }; From 361b15929956417bfe5009ab33ae16182046587a Mon Sep 17 00:00:00 2001 From: glyph Date: Tue, 23 Nov 2021 10:52:40 +0200 Subject: [PATCH 4/5] update version number --- peach-oled/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peach-oled/README.md b/peach-oled/README.md index 9829aea..b41aade 100644 --- a/peach-oled/README.md +++ b/peach-oled/README.md @@ -1,6 +1,6 @@ # peach-oled -[![Build Status](https://travis-ci.com/peachcloud/peach-oled.svg?branch=master)](https://travis-ci.com/peachcloud/peach-oled) ![Generic badge](https://img.shields.io/badge/version-0.1.3-.svg) +[![Build Status](https://travis-ci.com/peachcloud/peach-oled.svg?branch=master)](https://travis-ci.com/peachcloud/peach-oled) ![Generic badge](https://img.shields.io/badge/version-0.1.4-.svg) OLED microservice module for PeachCloud. Write to a 128x64 OLED display with SDD1306 driver (I2C) using [JSON-RPC](https://www.jsonrpc.org/specification) over http. From ac98bde760378cb2fe1251de41165180555447b5 Mon Sep 17 00:00:00 2001 From: glyph Date: Tue, 23 Nov 2021 10:53:09 +0200 Subject: [PATCH 5/5] lockfile update --- Cargo.lock | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be13e3b..37f6447 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2640,19 +2640,17 @@ dependencies = [ [[package]] name = "peach-oled" -version = "0.1.3" +version = "0.1.4" dependencies = [ "embedded-graphics", - "env_logger 0.6.2", - "jsonrpc-core 11.0.0", - "jsonrpc-http-server 11.0.0", - "jsonrpc-test 11.0.0", + "env_logger 0.9.0", + "jsonrpc-core 18.0.0", + "jsonrpc-http-server 18.0.0", + "jsonrpc-test 18.0.0", "linux-embedded-hal", "log 0.4.14", "nix 0.11.1", "serde 1.0.130", - "serde_json", - "snafu 0.4.4", "ssd1306", "tinybmp", ]