From 94a1206617cf3421ae859a67964244d7bec073b0 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sat, 5 Oct 2019 10:44:30 +0300 Subject: Add: copy of GPL-3 --- src/#parser.rs# | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/.#parser.rs | 1 + 2 files changed, 171 insertions(+) create mode 100644 src/#parser.rs# create mode 120000 src/.#parser.rs (limited to 'src') diff --git a/src/#parser.rs# b/src/#parser.rs# new file mode 100644 index 0000000..32cdd73 --- /dev/null +++ b/src/#parser.rs# @@ -0,0 +1,170 @@ +use serde_yaml; +use serde_yaml::Value; +use std::collections::HashMap +pub use crate::Roadmap; +pub use crate::RoadmapResult; +pub use crate::Status; +pub use crate::Step; + +/// Create a new roadmap from a textual YAML representation. +pub fn from_yaml(yaml: &str) -> RoadmapResult { + let mut roadmap = Roadmap::new(); + let map: HashMap = serde_yaml::from_str(yaml)?; + + for (name, value) in map { + let step = step_from_value(&name, &value)?; + roadmap.add_step(step); + } + + roadmap.validate()?; + Ok(roadmap) +} + +// Convert a Value into a Step, if possible. +fn step_from_value(name: &str, value: &Value) -> RoadmapResult { + match value { + Value::Mapping(_) => { + let label = parse_label(&value); + let status = parse_status(&value)?; + + let mut step = Step::new(name, label); + step.set_status(status); + + for depname in parse_depends(&value)?.iter() { + step.add_dependency(depname); + } + + Ok(step) + } + _ => Err("step is not a mapping".to_string().into()), + } +} + +// Get a sequence of depenencies. +fn parse_depends(map: &Value) -> RoadmapResult> { + let key_name = "depends"; + let key = Value::String(key_name.to_string()); + let mut depends: Vec<&str> = vec![]; + let need_list_of_names = format!("'depends' must be a list of step names"); + + match map.get(&key) { + None => (), + Some(Value::Sequence(deps)) => { + for depname in deps.iter() { + match depname { + Value::String(depname) => depends.push(depname), + _ => return Err(need_list_of_names.into()), + } + } + } + _ => return Err(need_list_of_names.into()), + } + + Ok(depends) +} + +// Get label string from a Mapping element, or empty string. +fn parse_label(map: &Value) -> &str { + parse_string("label", map) +} + +// Get status string from a Mapping element. Default to unknown status. +fn parse_status<'a>(map: &'a Value) -> RoadmapResult { + let text = parse_string("status", map); + match Status::from_text(text) { + Some(status) => Ok(status), + _ => Err(format!("unknown status: {:?}", text).into()), + } +} + +// Get string value from a Mapping field, or empty string if field +// isn't there. +fn parse_string<'a>(key_name: &str, map: &'a Value) -> &'a str { + let key = Value::String(key_name.to_string()); + match map.get(&key) { + Some(Value::String(s)) => s, + _ => "", + } +} + +#[cfg(test)] +mod tests { + use super::from_yaml; + + #[test] + fn yaml_is_empty() { + let r = from_yaml(""); + match r { + Ok(_) => panic!("expected a parse error"), + _ => (), + } + } + + #[test] + fn yaml_is_list() { + let r = from_yaml("[]"); + match r { + Ok(_) => panic!("expected a parse error"), + _ => (), + } + } + + #[test] + fn yaml_map_entries_not_maps() { + let r = from_yaml("foo: []"); + match r { + Ok(_) => panic!("expected a parse error"), + _ => (), + } + } + + #[test] + fn yaml_unknown_dep() { + let r = from_yaml("foo: {depends: [bar]}"); + match r { + Ok(_) => panic!("expected a parse error"), + _ => (), + } + } + + #[test] + fn yaml_unknown_status() { + let r = from_yaml(r#"foo: {status: "bar"}"#); + match r { + Ok(_) => panic!("expected a parse error"), + _ => (), + } + } + + #[test] + fn yaml_happy() { + let roadmap = from_yaml( + " +first: + label: the first step +second: + label: the second step + depends: + - first +", + ) + .unwrap(); + + let names: Vec<&str> = roadmap.step_names().collect(); + assert_eq!(names.len(), 2); + assert!(names.contains(&"first")); + assert!(names.contains(&"second")); + + let first = roadmap.get_step("first").unwrap(); + assert_eq!(first.name(), "first"); + assert_eq!(first.label(), "the first step"); + let deps: Vec<&str> = first.dependencies().collect(); + assert_eq!(deps.len(), 0); + + let second = roadmap.get_step("second").unwrap(); + assert_eq!(second.name(), "second"); + assert_eq!(second.label(), "the second step"); + let deps: Vec<&str> = second.dependencies().collect(); + assert_eq!(deps, vec!["first"]); + } +} diff --git a/src/.#parser.rs b/src/.#parser.rs new file mode 120000 index 0000000..5d257a5 --- /dev/null +++ b/src/.#parser.rs @@ -0,0 +1 @@ +liw@exolobe1.5169:1568966750 \ No newline at end of file -- cgit v1.2.1