rusty_yaml/
yaml.rs

1use std::fmt::{Display, Error, Formatter};
2use yaml_rust::{YamlEmitter, YamlLoader};
3
4
5/// Yaml struct definition
6#[derive(Clone, Debug, PartialEq)]
7pub struct Yaml {
8    name: String,
9    contents: String,
10}
11
12
13// Implementation of yaml struct
14impl Yaml {
15    /// Get the name of the current section
16    /// Requires immutable access
17    pub fn get_name(&self) -> String {
18        self.name.clone()
19    }
20
21    /// Get a section of the current yaml
22    /// Requires immutable access
23    pub fn get_section<S: Display>(&self, index: S) -> Result<Self, String> {
24        match YamlLoader::load_from_str(&self.contents) {
25            Ok(reader) => {
26                // let result = if lenreader[0];
27                let result = if reader.len() > 0 {
28                    reader[0][index.to_string().as_str()].clone()
29                } else {
30                    return Err(format!(
31                        "Could not get section '{}' because the parent section is empty",
32                        index
33                    ));
34                };
35
36                let mut s = Self::from(result);
37                s.name = index.to_string();
38                Ok(s)
39            }
40
41            Err(e) => {
42                return Err(format!(
43                    "Could not get section '{}' because {}",
44                    index,
45                    e.to_string()
46                ));
47            }
48        }
49    }
50
51
52    /// Get the names of all the sections in the current section
53    /// Requires immutable access
54    pub fn get_section_names(&self) -> Result<Vec<String>, String> {
55        // Iterator over section names and collect them into a vec of strings
56        match YamlLoader::load_from_str(&self.contents) {
57            Ok(y) => match &y[0] {
58                yaml_rust::Yaml::Hash(h) => Ok(h
59                    .keys()
60                    .map(|k| match k {
61                        yaml_rust::Yaml::String(s) => s.clone(),
62                        _ => String::from(""),
63                    })
64                    .collect()),
65                _ => Ok(vec![]),
66            },
67            Err(_) => {
68                return Err(format!("Malformed yaml section '{}'", self.name));
69            }
70        }
71    }
72
73    /// Does this yaml section have a section with this name?
74    /// Requires immutable access
75    pub fn has_section<S: Display>(&self, index: S) -> bool {
76        self.get_section_names()
77            .unwrap()
78            .contains(&index.to_string())
79    }
80
81    pub fn nth<N: Into<i32>>(&self, n: N) -> Self {
82        let vec = self.clone().into_iter().collect::<Vec<Yaml>>();
83
84        let index = n.into() as usize;
85        if index >= vec.len() {
86            Yaml::from(self.to_string())
87        } else {
88            vec[index].clone()
89        }
90    }
91}
92
93/// Converts a yaml object into an iterator
94/// Iterates over members of the section
95impl IntoIterator for Yaml {
96    type Item = Self;
97    type IntoIter = std::vec::IntoIter<Self::Item>;
98
99    fn into_iter(self) -> Self::IntoIter {
100        match YamlLoader::load_from_str(&self.contents) {
101            Ok(y) => match &y[0] {
102                // If children are sections, iterate over sections
103                yaml_rust::Yaml::Hash(h) => h
104                    .clone()
105                    .keys()
106                    .map(|k| match k {
107                        yaml_rust::Yaml::String(s) => Self::from(y[0].clone()).get_section(s),
108                        _ => self.get_section(""),
109                    }) // This map returns a Vec<Result<Self>>
110                    .filter(|y| y.is_ok()) // Filter out all Errs
111                    .map(|y| y.unwrap()) // Unwrap all results
112                    .collect::<Vec<Self>>(), // Collect Vec<Self>
113
114                // If children are values, iterate over values
115                yaml_rust::Yaml::Array(a) => a
116                    .iter()
117                    .map(|y| Self::from(y.clone()))
118                    .collect::<Vec<Self>>(),
119
120                _ => vec![Yaml::from(self.to_string())],
121            },
122            Err(_) => {
123                // This Yaml isnt a list, so make a vector of length one of our contents
124                vec![Yaml::from(self.to_string())]
125            }
126        }
127        .into_iter()
128    }
129}
130
131// Yaml object from string
132impl From<String> for Yaml {
133    fn from(s: String) -> Self {
134        Self {
135            contents: s.to_string(),
136            name: String::from(""),
137        }
138    }
139}
140
141// Yaml object from &str (using String implementation)
142impl From<&str> for Yaml {
143    fn from(s: &str) -> Self {
144        Self::from(s.to_string())
145    }
146}
147
148
149// Yaml object from &str
150impl From<yaml_rust::Yaml> for Yaml {
151    fn from(yaml: yaml_rust::Yaml) -> Self {
152        let mut out_str = String::new();
153        let mut emitter = YamlEmitter::new(&mut out_str);
154        match emitter.dump(&yaml) {
155            _ => {}
156        };
157
158
159        let lines = out_str.lines().collect::<Vec<&str>>();
160        out_str = if lines.len() > 0 {
161            lines[1..].join("\n").to_string()
162        } else {
163            "".to_string()
164        };
165
166
167        Self::from(out_str)
168    }
169}
170
171
172impl From<Yaml> for String {
173    fn from(yaml: Yaml) -> Self {
174        format!("{}", yaml.to_string())
175    }
176}
177
178// How to display a Yaml object
179impl Display for Yaml {
180    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
181        write!(f, "{}", self.contents)
182    }
183}