versio/scan/
toml.rs

1//! Utilities to find a mark in a TOML file.
2
3use crate::errors::Result;
4use crate::mark::Mark;
5#[cfg(test)]
6use crate::scan::parts::ToPart;
7use crate::scan::parts::{IntoPartVec, Part};
8use crate::scan::Scanner;
9use serde::de::{self, DeserializeSeed, Deserializer, IgnoredAny, MapAccess, SeqAccess, Unexpected, Visitor};
10use toml::Spanned;
11
12pub struct TomlScanner {
13  target: Vec<Part>
14}
15
16impl TomlScanner {
17  pub fn new(target: &str) -> TomlScanner { TomlScanner { target: target.into_part_vec() } }
18
19  #[cfg(test)]
20  pub fn from_parts(target: &[&dyn ToPart]) -> TomlScanner { TomlScanner { target: target.into_part_vec() } }
21}
22
23impl Scanner for TomlScanner {
24  fn build(parts: Vec<Part>) -> TomlScanner { TomlScanner { target: parts } }
25  fn find(&self, data: &str) -> Result<Mark> { scan_toml(data, self.target.clone()) }
26}
27
28fn scan_toml<P: IntoPartVec>(data: &str, loc: P) -> Result<Mark> {
29  let mut parts = loc.into_part_vec();
30  parts.reverse();
31
32  let value = pop(parts).deserialize(toml::Deserializer::new(data))?;
33  let index = value.span().start;
34
35  Ok(Mark::new(value.into_inner(), index + 1))
36}
37
38fn pop(mut parts: Vec<Part>) -> NthElement {
39  let part = parts.pop().unwrap();
40  NthElement::new(part, parts)
41}
42
43pub struct NthElement {
44  part: Part,
45  remains: Vec<Part>
46}
47
48impl NthElement {
49  pub fn new(part: Part, remains: Vec<Part>) -> NthElement { NthElement { part, remains } }
50}
51
52impl<'de> Visitor<'de> for NthElement {
53  type Value = Spanned<String>;
54
55  fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
56    write!(formatter, "a part that is {:?}", self.part)
57  }
58
59  fn visit_map<V>(mut self, mut map: V) -> std::result::Result<Self::Value, V::Error>
60  where
61    V: MapAccess<'de>
62  {
63    let expected_key: String = match &self.part {
64      Part::Map(key) => key.clone(),
65      _ => return Err(de::Error::invalid_type(Unexpected::Map, &self))
66    };
67
68    let mut got_val: Option<Spanned<String>> = None;
69
70    while let Some(key) = map.next_key::<String>()? {
71      if key == expected_key {
72        let nth = if self.remains.is_empty() {
73          map.next_value()?
74        } else {
75          let next = pop(std::mem::take(&mut self.remains));
76          map.next_value_seed(next)?
77        };
78
79        got_val = Some(nth);
80        break;
81      } else {
82        map.next_value::<IgnoredAny>()?;
83      }
84    }
85
86    while let Some((IgnoredAny, IgnoredAny)) = map.next_entry()? {}
87
88    let ista = got_val.ok_or_else(|| de::Error::missing_field("<missing field>"))?;
89    Ok(ista)
90  }
91
92  fn visit_seq<V>(mut self, mut seq: V) -> std::result::Result<Self::Value, V::Error>
93  where
94    V: SeqAccess<'de>
95  {
96    let n = match &self.part {
97      Part::Seq(n) => *n,
98      _ => return Err(de::Error::invalid_type(Unexpected::Seq, &self))
99    };
100
101    for i in 0 .. n {
102      if seq.next_element::<IgnoredAny>()?.is_none() {
103        return Err(de::Error::invalid_length(i, &self));
104      }
105    }
106
107    let nth = if self.remains.is_empty() {
108      seq.next_element()?.ok_or_else(|| de::Error::invalid_length(n, &self))?
109    } else {
110      let next = pop(std::mem::take(&mut self.remains));
111      seq.next_element_seed(next)?.ok_or_else(|| de::Error::invalid_length(n, &self))?
112    };
113
114    while let Some(IgnoredAny) = seq.next_element()? {}
115
116    Ok(nth)
117  }
118}
119
120impl<'de> DeserializeSeed<'de> for NthElement {
121  type Value = Spanned<String>;
122
123  fn deserialize<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
124  where
125    D: Deserializer<'de>
126  {
127    deserializer.deserialize_any(self)
128  }
129}
130
131#[cfg(test)]
132mod test {
133  use super::TomlScanner;
134  use crate::scan::Scanner;
135
136  #[test]
137  fn test_toml() {
138    let doc = r#"
139version = "1.2.3""#;
140
141    let mark = TomlScanner::new("version").find(doc).unwrap();
142    assert_eq!("1.2.3", mark.value());
143    assert_eq!(12, mark.start());
144  }
145
146  #[test]
147  fn test_toml_seq() {
148    let doc = r#"
149thing = [ "thing2", "1.2.3" ]"#;
150
151    let mark = TomlScanner::new("thing.1").find(doc).unwrap();
152    assert_eq!("1.2.3", mark.value());
153    assert_eq!(22, mark.start());
154  }
155
156  #[test]
157  fn test_toml_complex() {
158    let doc = r#"
159[version]
160"thing" = [ "2.4.6", { "version" = "1.2.3" } ]"#;
161
162    let mark = TomlScanner::new("version.thing.1.version").find(doc).unwrap();
163    assert_eq!("1.2.3", mark.value());
164    assert_eq!(47, mark.start());
165  }
166
167  #[test]
168  fn test_toml_clever() {
169    let doc = r#"
170[[0]]
171"the.version" = "1.2.3""#;
172
173    let mark = TomlScanner::from_parts(&[&"0", &0, &"the.version"]).find(doc).unwrap();
174    assert_eq!("1.2.3", mark.value());
175    assert_eq!(24, mark.start());
176  }
177
178  #[test]
179  fn test_toml_utf8() {
180    let doc = r#"
181"thíng" = [ "thíng2", "1.2.3" ]"#;
182
183    let mark = TomlScanner::new("thíng.1").find(doc).unwrap();
184    assert_eq!("1.2.3", mark.value());
185    assert_eq!(26, mark.start());
186  }
187}