From dc0cf21403fd2f1fccfc8d5194d71a0700a37d42 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio <jorge@japaric.io> Date: Fri, 14 Jul 2017 20:46:10 -0500 Subject: [PATCH] split parsing from syntax checking --- src/check.rs | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 19 +++---- src/parse.rs | 20 ++++--- 3 files changed, 169 insertions(+), 20 deletions(-) create mode 100644 src/check.rs diff --git a/src/check.rs b/src/check.rs new file mode 100644 index 0000000..6988783 --- /dev/null +++ b/src/check.rs @@ -0,0 +1,150 @@ +use std::collections::HashMap; + +use quote::Tokens; +use syn::Ident; + +use error::*; +use {Idents, Statics}; + +pub type Tasks = HashMap<Ident, Task>; + +pub struct App { + pub device: Tokens, + pub idle: Idle, + pub init: Init, + pub resources: Statics, + pub tasks: Tasks, +} + +pub struct Idle { + pub locals: Statics, + pub path: Tokens, + pub resources: Idents, +} + +pub struct Init { + pub path: Tokens, +} + +pub struct Task { + pub enabled: Option<bool>, + pub priority: Option<u8>, + pub resources: Idents, +} + +pub fn app(app: ::App) -> Result<App> { + Ok(App { + device: app.device, + idle: ::check::idle(app.idle).chain_err(|| "checking `idle`")?, + init: ::check::init(app.init).chain_err(|| "checking `init`")?, + resources: ::check::statics("resources", app.resources) + .chain_err(|| "checking `resources`")?, + tasks: ::check::tasks(app.tasks).chain_err(|| "checking `tasks`")?, + }) +} + +fn idents(field: &str, idents: Option<Idents>) -> Result<Idents> { + Ok(if let Some(idents) = idents { + ensure!( + !idents.is_empty(), + "empty `{}` field. It should be removed.", + field + ); + + idents + } else { + Idents::new() + }) +} + +fn idle(idle: Option<::Idle>) -> Result<Idle> { + Ok(if let Some(idle) = idle { + ensure!( + idle.locals.is_some() || idle.path.is_some() || + idle.resources.is_some(), + "empty `idle` field. It should be removed." + ); + + Idle { + locals: ::check::statics("locals", idle.locals)?, + path: ::check::path("idle", idle.path) + .chain_err(|| "checking `path`")?, + resources: ::check::idents("resources", idle.resources)?, + } + } else { + Idle { + locals: Statics::new(), + path: quote!(idle), + resources: Idents::new(), + } + }) +} + +fn init(init: Option<::Init>) -> Result<Init> { + Ok(if let Some(init) = init { + if let Some(path) = init.path { + Init { + path: ::check::path("init", Some(path)) + .chain_err(|| "checking `path`")?, + } + } else { + bail!("empty `init` field. It should be removed."); + } + } else { + Init { path: quote!(init) } + }) +} + +fn path(default: &str, path: Option<Tokens>) -> Result<Tokens> { + Ok(if let Some(path) = path { + ensure!( + path.as_str() != default, + "this is the default value. It should be omitted." + ); + + path + } else { + let default = Ident::new(default); + quote!(#default) + }) +} + +fn statics(field: &str, statics: Option<Statics>) -> Result<Statics> { + Ok(if let Some(statics) = statics { + ensure!( + !statics.is_empty(), + "empty `{}` field. It should be removed.", + field + ); + + statics + } else { + Statics::new() + }) +} + +fn tasks(tasks: Option<::Tasks>) -> Result<Tasks> { + Ok(if let Some(tasks) = tasks { + ensure!( + !tasks.is_empty(), + "empty `tasks` field. It should be removed" + ); + + tasks + .into_iter() + .map(|(name, task)| { + Ok(( + name.clone(), + Task { + enabled: task.enabled, + priority: task.priority, + resources: ::check::idents("resources", task.resources) + .chain_err(|| format!("checking task `{}`", name))?, + }, + )) + }) + .collect::<Result<_>>()? + } else { + Tasks::new() + }) +} diff --git a/src/lib.rs b/src/lib.rs index 015a3a9..f010d68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ extern crate error_chain; extern crate quote; extern crate syn; +pub mod check; pub mod error; mod parse; @@ -26,31 +27,31 @@ pub type Tasks = HashMap<Ident, Task>; #[derive(Debug)] pub struct App { pub device: Tokens, - pub idle: Idle, - pub init: Init, - pub resources: Statics, - pub tasks: Tasks, + pub idle: Option<Idle>, + pub init: Option<Init>, + pub resources: Option<Statics>, + pub tasks: Option<Tasks>, } /// `init` #[derive(Debug)] pub struct Init { - pub path: Tokens, + pub path: Option<Tokens>, } /// `idle` #[derive(Debug)] pub struct Idle { - pub locals: Statics, - pub path: Tokens, - pub resources: Idents, + pub locals: Option<Statics>, + pub path: Option<Tokens>, + pub resources: Option<Idents>, } #[derive(Debug)] pub struct Task { pub enabled: Option<bool>, pub priority: Option<u8>, - pub resources: Idents, + pub resources: Option<Idents>, } // `$ident: $ty = $expr;` diff --git a/src/parse.rs b/src/parse.rs index 98a94be..b77b7d3 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -57,10 +57,10 @@ pub fn app(input: &str) -> Result<App> { Ok(App { device: device.ok_or("`device` field is missing")?, - idle: idle.ok_or("`idle` field is missing")?, - init: init.ok_or("`init` field is missing")?, - resources: resources.unwrap_or(HashMap::new()), - tasks: tasks.unwrap_or(HashMap::new()), + idle, + init, + resources, + tasks, }) } @@ -199,9 +199,9 @@ fn idle(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Idle> { })?; Ok(Idle { - locals: locals.unwrap_or(HashMap::new()), - path: path.ok_or("`path` field missing")?, - resources: resources.unwrap_or(HashSet::new()), + locals, + path, + resources, }) }) } @@ -223,9 +223,7 @@ fn init(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Init> { Ok(()) })?; - Ok(Init { - path: path.ok_or("`path` field missing")?, - }) + Ok(Init { path }) }) } @@ -360,7 +358,7 @@ fn task(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Task> { Ok(Task { enabled, priority, - resources: resources.unwrap_or(HashSet::new()), + resources, }) }) } -- GitLab