diff --git a/src/lib/config.rs b/src/lib/config.rs index 708ebf2..f0a9579 100644 --- a/src/lib/config.rs +++ b/src/lib/config.rs @@ -113,7 +113,8 @@ impl std::fmt::Display for ProgramStartError { write!(f, "preset {} is not currently supported", name) } ProgramStartError::Presets(errors) => { - let res = errors.iter() + let res = errors + .iter() .map(|x| x.to_string()) .collect::>() .join("\n"); diff --git a/src/lib/preset.rs b/src/lib/preset.rs index c3a459e..a15e981 100644 --- a/src/lib/preset.rs +++ b/src/lib/preset.rs @@ -17,9 +17,30 @@ pub trait Preset { } } +/// +/// Presets have the chance to validate their own options +/// +pub trait PresetOptions { + fn validate(_options: serde_json::Value) -> Result<(), PresetError> { + Ok(()) + } +} + /// /// The following are just aliases /// pub type RewriteFns = Vec String>; pub type ResourceDef = (String, Method, fn(&HttpRequest) -> HttpResponse); pub type AsyncResourceDef = (String, Method, fn(&HttpRequest) -> FutResp); + +pub enum PresetError { + ValidationFailed(String), +} + +impl std::fmt::Display for PresetError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + PresetError::ValidationFailed(s) => write!(f, "{}", s), + } + } +} diff --git a/src/lib/presets/m2/handlers/config_capture.rs b/src/lib/presets/m2/handlers/config_capture.rs index 1f7f94b..bfc199e 100644 --- a/src/lib/presets/m2/handlers/config_capture.rs +++ b/src/lib/presets/m2/handlers/config_capture.rs @@ -1,11 +1,11 @@ +use actix_web::http::Method; use actix_web::HttpRequest; use app_state::AppState; +use preset::AsyncResourceDef; +use preset::ResourceDef; use presets::m2::preset_m2::FutResp; use proxy_utils::apply_to_proxy_body; use rjs::RequireJsClientConfig; -use actix_web::http::Method; -use preset::ResourceDef; -use preset::AsyncResourceDef; /// /// This handler will record the incoming string from the Magento-generated diff --git a/src/lib/presets/m2/handlers/serve_r_js.rs b/src/lib/presets/m2/handlers/serve_r_js.rs index f445bf2..fe425c4 100644 --- a/src/lib/presets/m2/handlers/serve_r_js.rs +++ b/src/lib/presets/m2/handlers/serve_r_js.rs @@ -1,9 +1,9 @@ +use actix_web::http::Method; use actix_web::HttpRequest; use actix_web::HttpResponse; use app_state::AppState; -use presets::m2::opts::M2PresetOptions; -use actix_web::http::Method; use preset::ResourceDef; +use presets::m2::opts::M2PresetOptions; const INSTRUMENTED_REQUIRE_JS: &'static str = include_str!("../static/requirejs.js"); diff --git a/src/lib/presets/m2/opts.rs b/src/lib/presets/m2/opts.rs index 055466a..67a229a 100644 --- a/src/lib/presets/m2/opts.rs +++ b/src/lib/presets/m2/opts.rs @@ -1,9 +1,10 @@ use config::ProgramConfig; +use preset::PresetError; +use preset::PresetOptions; use serde_json; #[derive(Deserialize, Debug)] pub struct M2PresetOptions { - #[serde(default = "default_require_path")] pub require_path: Option, @@ -56,6 +57,14 @@ impl M2PresetOptions { } } +impl PresetOptions for M2PresetOptions { + fn validate(options: serde_json::Value) -> Result<(), PresetError> { + serde_json::from_value::(options) + .map_err(|e| PresetError::ValidationFailed(e.to_string())) + .map(|_o| ()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/lib/setup.rs b/src/lib/setup.rs index 05a82de..051a746 100644 --- a/src/lib/setup.rs +++ b/src/lib/setup.rs @@ -1,9 +1,11 @@ use actix_web::App; use app_state::AppState; use config::ProgramConfig; +use config::ProgramStartError; use from_file::FromFile; use options::ProgramOptions; use preset::Preset; +use preset::PresetOptions; use presets::m2::opts::M2PresetOptions; use presets::m2::preset_m2::M2Preset; use presets::m2::seed::SeedData; @@ -13,7 +15,6 @@ use serde_json; use std::collections::HashMap; use std::sync::Arc; use std::sync::Mutex; -use config::ProgramStartError; pub type PresetsMap = HashMap>>; @@ -37,33 +38,46 @@ pub fn apply_presets( app.default_resource(|r| r.f(proxy_transform)) } -pub fn validate_presets(presets: Vec<&str>, program_config: &ProgramConfig) -> Result<(), ProgramStartError> { +/// +/// Validate given preset options. This is dynamic in nature since +/// we cannot use serde to validate all nested presets by itself. +/// +/// Instead we partially validate (json/yaml) the data structure, +/// +pub fn validate_presets( + presets: Vec<&str>, + program_config: &ProgramConfig, +) -> Result<(), ProgramStartError> { let mut errors = vec![]; - program_config.presets.iter().enumerate().for_each(|(i, preset)| { - match preset.name.as_str() { - "m2" => { - let preset_opts: Result - = serde_json::from_value(preset.options.clone()); + let mut maps = HashMap::new(); - match preset_opts { + maps.insert("m2", |o: serde_json::Value| M2PresetOptions::validate(o)); + + program_config + .presets + .iter() + .enumerate() + .for_each(|(i, preset)| { + let name = preset.name.as_str(); + let preset_validate = maps.get(name); + + match preset_validate { + Some(validator) => match validator(preset.options.clone()) { Err(e) => { errors.push(ProgramStartError::PresetOptions { error: e.to_string(), - name: "m2".to_string() - }) - }, - _ => {} - } - } - _ => { - errors.push( - ProgramStartError::PresetNotSupported { - name: "m2".to_string() + name: name.to_string(), + }); } - ); - }, - } - }); + _ => { + // nothing! + } + }, + None => errors.push(ProgramStartError::PresetNotSupported { + name: name.to_string(), + }), + } + }); if errors.len() > 0 { Err(ProgramStartError::Presets(errors)) diff --git a/src/lib/system.rs b/src/lib/system.rs index e6994ec..fb83e0a 100644 --- a/src/lib/system.rs +++ b/src/lib/system.rs @@ -6,10 +6,10 @@ use options::ProgramOptions; use options::ProxyScheme; use setup::apply_presets; use setup::state_and_presets; +use setup::validate_presets; use ssl; use std::net::SocketAddr; use std::net::SocketAddrV4; -use setup::validate_presets; pub fn create(opts: ProgramOptions) -> Result<(actix::SystemRunner, String), ProgramStartError> { //