1use std;
2use std::collections::BTreeSet;
3use std::error::Error;
4use std::io::Write;
5use std::rc::Rc;
6use std::result::Result;
7
8use serde_yaml;
9
10use super::traits::{ConvertResult, Converter, ImportResult, Importer};
11use crate::build::Val;
12
13pub struct YamlConverter {}
14
15impl YamlConverter {
16 pub fn new() -> Self {
17 YamlConverter {}
18 }
19
20 fn convert_list(&self, items: &Vec<Rc<Val>>) -> std::io::Result<serde_yaml::Value> {
21 let mut v = Vec::new();
22 for val in items.iter() {
23 v.push(self.convert_value(val)?);
24 }
25 Ok(serde_yaml::Value::Sequence(v))
26 }
27
28 fn convert_env(&self, items: &Vec<(Rc<str>, Rc<str>)>) -> std::io::Result<serde_yaml::Value> {
29 let mut mp = serde_yaml::Mapping::new();
30 for &(ref k, ref v) in items.iter() {
31 mp.insert(
32 serde_yaml::Value::String(k.to_string()),
33 serde_yaml::Value::String(v.to_string()),
34 );
35 }
36 Ok(serde_yaml::Value::Mapping(mp))
37 }
38
39 fn convert_tuple(&self, items: &Vec<(Rc<str>, Rc<Val>)>) -> std::io::Result<serde_yaml::Value> {
40 let mut mapping = serde_yaml::Mapping::new();
41 for &(ref k, ref v) in items.iter() {
42 mapping.insert(
43 serde_yaml::Value::String(k.to_string()),
44 self.convert_value(v)?,
45 );
46 }
47 Ok(serde_yaml::Value::Mapping(mapping))
48 }
49
50 fn convert_value(&self, v: &Val) -> std::io::Result<serde_yaml::Value> {
51 let yaml_val = match v {
52 &Val::Boolean(b) => serde_yaml::Value::Bool(b),
53 &Val::Empty => serde_yaml::Value::Null,
54 &Val::Float(f) => match serde_yaml::to_value(f) {
55 Ok(v) => v,
56 _ => panic!("Float is too large or not a Number {}", f),
57 },
58 &Val::Int(i) => match serde_yaml::to_value(i) {
59 Ok(v) => v,
60 _ => panic!("Int is too large or not a Number {}", i),
61 },
62 &Val::Str(ref s) => serde_yaml::Value::String(s.to_string()),
63 &Val::Env(ref fs) => self.convert_env(fs)?,
64 &Val::List(ref l) => self.convert_list(l)?,
65 &Val::Tuple(ref t) => self.convert_tuple(t)?,
66 };
67 Ok(yaml_val)
68 }
69
70 fn merge_mapping_keys(
71 &self,
72 mut fs: &mut Vec<(String, Rc<Val>)>,
73 m: &serde_yaml::Mapping,
74 ) -> Result<(), Box<dyn Error>> {
75 for (key, value) in m {
76 let key = match key {
79 serde_yaml::Value::Bool(b) => b.to_string(),
80 serde_yaml::Value::Null => "null".to_string(),
81 serde_yaml::Value::Number(n) => n.to_string(),
82 serde_yaml::Value::String(s) => s.clone(),
83 serde_yaml::Value::Sequence(_)
84 | serde_yaml::Value::Mapping(_)
85 | serde_yaml::Value::Tagged(_) => {
86 eprintln!("Unsupported key type in yaml map key import skipping");
87 continue;
88 }
89 };
90 if key == "<<" {
91 if let serde_yaml::Value::Mapping(merge_map) = value {
92 self.merge_mapping_keys(&mut fs, merge_map)?;
93 }
94 } else {
95 fs.push((key, Rc::new(self.convert_yaml_val(&value)?)));
96 }
97 }
98 Ok(())
99 }
100
101 fn convert_yaml_val(&self, v: &serde_yaml::Value) -> Result<Val, Box<dyn Error>> {
102 Ok(match v {
103 serde_yaml::Value::String(s) => Val::Str(s.clone().into()),
104 serde_yaml::Value::Number(n) => {
105 if let Some(i) = n.as_i64() {
106 Val::Int(i)
107 } else {
108 Val::Float(n.as_f64().expect("Number was not an int or a float!!"))
109 }
110 }
111 serde_yaml::Value::Bool(b) => Val::Boolean(*b),
112 serde_yaml::Value::Null => Val::Empty,
113 serde_yaml::Value::Sequence(l) => {
114 let mut vs = Vec::with_capacity(l.len());
115 for aval in l {
116 vs.push(Rc::new(self.convert_yaml_val(aval)?));
117 }
118 Val::List(vs)
119 }
120 serde_yaml::Value::Mapping(m) => {
121 let mut fs = Vec::with_capacity(m.len());
122 self.merge_mapping_keys(&mut fs, m)?;
123 fs.reverse();
124 let mut seen_keys = BTreeSet::new();
125 let mut collapsed = Vec::with_capacity(fs.len());
126 for (k, val) in fs {
127 if !seen_keys.contains(&k) {
128 collapsed.push((k.clone().into(), val));
129 seen_keys.insert(k);
130 }
131 }
132 collapsed.reverse();
133 Val::Tuple(collapsed)
134 }
135 serde_yaml::Value::Tagged(_) => {
136 eprintln!(
137 "Tagged value types are not supported in yaml imports. Replacing with Empty..."
138 );
139 Val::Empty
140 }
141 })
142 }
143
144 pub fn write(&self, v: &Val, mut w: &mut dyn Write) -> ConvertResult {
145 let jsn_val = self.convert_value(v)?;
146 serde_yaml::to_writer(&mut w, &jsn_val)?;
147 writeln!(w, "")?;
148 Ok(())
149 }
150}
151
152impl Converter for YamlConverter {
153 fn convert(&self, v: Rc<Val>, mut w: &mut dyn Write) -> ConvertResult {
154 self.write(&v, &mut w)
155 }
156
157 fn file_ext(&self) -> String {
158 String::from("yaml")
159 }
160
161 fn description(&self) -> String {
162 "Convert ucg Vals into valid yaml.".to_string()
163 }
164
165 #[allow(unused_must_use)]
166 fn help(&self) -> String {
167 include_str!("yaml_help.txt").to_string()
168 }
169}
170
171impl Importer for YamlConverter {
172 fn import(&self, bytes: &[u8]) -> ImportResult {
173 let json_val = serde_yaml::from_slice(bytes)?;
174 Ok(Rc::new(self.convert_yaml_val(&json_val)?))
175 }
176}