zcfg_flag_parser/
lib.rs

1extern crate zcfg;
2extern crate itertools;
3
4use std::collections::HashMap;
5use itertools::Itertools;
6use zcfg::ConfigMetadata;
7use zcfg::InitErr;
8
9#[derive(Debug, PartialEq, Eq, Clone)]
10pub enum FlagInitErr {
11  UndefinedArg(String),
12  InitErr(InitErr),
13}
14
15pub struct FlagParser;
16
17impl FlagParser {
18  pub fn new() -> FlagParser {
19    FlagParser
20  }
21
22  pub fn parse_from_args<I: Iterator<Item = String>>(&self, args: I) -> Result<(), Vec<FlagInitErr>> {
23    let initializers = zcfg::STATIC_CONFIG_INITIALIZERS.read()
24      .expect("initializers were poisoned");
25
26    let mut initializer_meta_sorted =
27      initializers.iter()
28        .map(|i| i.metadata().clone())
29        .collect::<Vec<ConfigMetadata>>();
30    initializer_meta_sorted.sort_by(|a, b| a.config_name().cmp(b.config_name()));
31
32    let flag_name_conflicts: Vec<(String, Vec<ConfigMetadata>)> =
33      initializer_meta_sorted.into_iter()
34        .group_by(|i| i.config_name().to_owned())
35        .into_iter()
36        .map(|(key, metadata_objects)| (key, metadata_objects.collect::<Vec<ConfigMetadata>>()))
37        .filter(|&(_, ref metadata_objects)| metadata_objects.len() > 1)
38        .collect();
39
40    // TODO: Something more user friendly
41    assert_eq!(flag_name_conflicts, Vec::new());
42
43    let arg_elements = args.map(|content| {
44      if content == "--".to_owned() {
45        ArgComponent::Terminator
46      } else if content.starts_with("--") {
47        // TODO: This isn't perfect -- more robust parsing later
48        if content.contains('=') {
49          ArgComponent::CompleteArg(content)
50        } else {
51          ArgComponent::ArgPrefix(content)
52        }
53      } else {
54        ArgComponent::ArgSuffix(content)
55      }
56    })
57    .peekable()
58    .batching(|mut it| {
59      let first = it.next();
60      if first.is_none() { return None }
61      match first {
62        None => None,
63        Some(ArgComponent::Terminator) => None,
64        Some(ArgComponent::ArgSuffix(value)) => {
65          Some(Err(format!("Arg element [{}] did not have a corresponding key", value)))
66        },
67        Some(ArgComponent::CompleteArg(name_and_value)) =>  {
68          let eq_byte_idx = name_and_value.find('=').unwrap();
69          let (name, value_plus_eq) = name_and_value.split_at(eq_byte_idx);
70          Some(Ok(ArgCapture {
71            label: name.chars().skip(2 /* -- */).collect::<String>(),
72            value: Some(value_plus_eq.chars().skip(1 /* = */).collect::<String>()),
73          }))
74        }
75        Some(ArgComponent::ArgPrefix(name)) => {
76          let mut value_opt = None;
77          if let Some(&ArgComponent::ArgSuffix(ref s)) = it.peek() {
78            // TODO: Fix janky clone
79            value_opt = Some(s.clone())
80          }
81          match value_opt {
82            Some(value) => {
83              // Toss next element
84              it.next();
85              Some(Ok(ArgCapture {
86                label: name.chars().skip(2 /* == */).collect::<String>(),
87                value: Some(value.to_owned()),
88              }))
89            },
90            None => {
91              Some(Ok(ArgCapture {
92                label: name.chars().skip(2 /* == */).collect::<String>(),
93                value: None,
94              }))
95            }
96          }
97        }
98      }
99    })
100    .collect::<Vec<Result<ArgCapture, String>>>();
101
102
103    let mut captures = Vec::new();
104    for arg in arg_elements.into_iter() {
105      if let Err(e) = arg {
106        panic!(e)
107      }
108      captures.push(arg.unwrap());
109    }
110
111
112    let mut config_name_to_idx = HashMap::new();
113    for (idx, e) in initializers.iter().enumerate() {
114      config_name_to_idx.insert(e.config_name().clone(), idx);
115    }
116
117    let mut set_errs = Vec::new();
118    for capture in captures.into_iter() {
119      let label: &str = &capture.label;
120      if !config_name_to_idx.contains_key(label) {
121        set_errs.push(FlagInitErr::UndefinedArg(capture.label.clone()))
122      } else {
123        let config_idx = config_name_to_idx.get(label).unwrap();
124        let initializer_ref = initializers.get(*config_idx).unwrap();
125        let result = match capture.value {
126          None => initializer_ref.set_statically("True"),
127          Some(ref v) => initializer_ref.set_statically(v)
128        };
129
130        if result.is_err() {
131          set_errs.push(FlagInitErr::InitErr(result.err().unwrap()))
132        }
133      }
134    }
135    if set_errs.is_empty() {
136      Ok(())
137    } else {
138      Err(set_errs)
139    }
140  }
141}
142
143#[derive(Clone, Debug, PartialEq, Eq)]
144enum ArgComponent {
145  CompleteArg(String),
146  ArgPrefix(String),
147  ArgSuffix(String),
148  Terminator,
149}
150
151#[derive(Clone,Debug, PartialEq, Eq)]
152struct ArgCapture {
153  pub label: String,
154  pub value: Option<String>
155}