1use crate::Result;
3use hashlink::linked_hash_map;
4use saphyr::{MarkedYaml, Scalar, YamlData};
5use std::borrow::Cow;
6use std::collections::HashMap;
7use std::hash::Hash;
8
9pub fn hash_map<K, V>(key: K, value: V) -> HashMap<K, V>
11where
12 K: Hash + Eq + Clone,
13{
14 let mut hash_map = HashMap::with_capacity(1);
15 hash_map.insert(key, value);
16 hash_map
17}
18
19pub fn linked_hash_map<K, V>(key: K, value: V) -> linked_hash_map::LinkedHashMap<K, V>
21where
22 K: Hash + Eq + Clone,
23{
24 let mut linked_hash_map = linked_hash_map::LinkedHashMap::new();
25 linked_hash_map.insert(key, value);
26 linked_hash_map
27}
28
29pub const fn saphyr_yaml_string(s: &str) -> saphyr::Yaml<'_> {
31 saphyr::Yaml::Value(saphyr::Scalar::String(Cow::Borrowed(s)))
32}
33
34pub fn try_unwrap_saphyr_scalar<'a>(yaml: &'a saphyr::Yaml) -> Result<&'a saphyr::Scalar<'a>> {
36 if let saphyr::Yaml::Value(scalar) = yaml {
37 Ok(scalar)
38 } else {
39 Err(expected_scalar!("Expected a scalar, got: {:?}", yaml))
40 }
41}
42
43pub fn scalar_to_string(scalar: &saphyr::Scalar) -> String {
46 match scalar {
47 saphyr::Scalar::Null => "null".to_string(),
48 saphyr::Scalar::Boolean(b) => b.to_string(),
49 saphyr::Scalar::Integer(i) => i.to_string(),
50 saphyr::Scalar::FloatingPoint(o) => o.to_string(),
51 saphyr::Scalar::String(s) => s.to_string(),
52 }
53}
54
55pub fn format_scalar(scalar: &saphyr::Scalar) -> String {
57 match scalar {
58 saphyr::Scalar::String(s) => format!("\"{s}\""),
59 _ => scalar_to_string(scalar),
60 }
61}
62
63pub fn format_yaml_data<'a>(data: &saphyr::YamlData<'a, saphyr::MarkedYaml<'a>>) -> String {
65 match data {
66 saphyr::YamlData::Value(scalar) => format_scalar(scalar),
67 saphyr::YamlData::Sequence(seq) => {
68 let items: Vec<String> = seq.iter().map(|v| format_yaml_data(&v.data)).collect();
69 format!("[{}]", items.join(", "))
70 }
71 saphyr::YamlData::Mapping(mapping) => {
72 let items: Vec<String> = mapping
73 .iter()
74 .map(|(k, v)| {
75 format!(
76 "{}: {}",
77 format_yaml_data(&k.data),
78 format_yaml_data(&v.data)
79 )
80 })
81 .collect();
82 format!("[{}]", items.join(", "))
83 }
84 _ => format!("<unsupported type: {data:?}>"),
85 }
86}
87
88pub fn format_marker(marker: &saphyr::Marker) -> String {
90 format!("[{}, {}]", marker.line(), marker.col())
91}
92
93pub fn format_vec<V>(vec: &[V]) -> String
95where
96 V: std::fmt::Display,
97{
98 let items: Vec<String> = vec.iter().map(|v| format!("{v}")).collect();
99 format!("[{}]", items.join(", "))
100}
101
102pub fn format_hash_map<K, V>(hash_map: &HashMap<K, V>) -> String
104where
105 K: std::fmt::Display,
106 V: std::fmt::Display,
107{
108 let items: Vec<String> = hash_map
109 .iter()
110 .map(|(k, v)| format!("{}: {}", k, v))
111 .collect();
112 format!("{{ {} }}", items.join(", "))
113}
114pub fn collect_keys(a: &'static [&'static str], b: &'static [&'static str]) -> Vec<&'static str> {
116 let mut keys = Vec::with_capacity(a.len() + b.len());
117 keys.extend_from_slice(a);
118 keys.extend_from_slice(b);
119 keys.sort();
120 keys.dedup();
121 keys
122}
123
124pub fn filter_mapping<'a>(
126 mapping: &saphyr::AnnotatedMapping<'a, saphyr::MarkedYaml<'a>>,
127 keys: Vec<&'static str>,
128 override_type: &'a str,
129) -> Result<saphyr::AnnotatedMapping<'a, saphyr::MarkedYaml<'a>>> {
130 let mut filtered_mapping = saphyr::AnnotatedMapping::new();
131 for (k, v) in mapping.iter() {
132 if let YamlData::Value(Scalar::String(key)) = &k.data {
133 if keys.contains(&key.as_ref()) {
134 match key.as_ref() {
135 "type" => {
136 filtered_mapping
137 .insert(k.clone(), MarkedYaml::value_from_str(override_type));
138 }
139 _ => {
140 filtered_mapping.insert(k.clone(), v.clone());
141 }
142 }
143 }
144 } else {
145 return Err(expected_scalar!("Expected a string key, got: {:?}", k.data));
146 }
147 }
148 Ok(filtered_mapping.into_iter().collect())
149}
150
151#[cfg(test)]
152mod tests {
153 use crate::utils::{format_scalar, hash_map, scalar_to_string};
154 use ordered_float::OrderedFloat;
155 use std::collections::HashMap;
156
157 #[test]
158 fn test_hash_map() {
159 let expected = vec![("foo".to_string(), "bar".to_string())]
160 .into_iter()
161 .collect::<HashMap<String, String>>();
162
163 let actual = hash_map("foo".to_string(), "bar".to_string());
164 assert_eq!(expected, actual);
165 }
166
167 #[test]
168 fn test_scalar_to_string() {
169 assert_eq!("null", scalar_to_string(&saphyr::Scalar::Null));
170 assert_eq!("true", scalar_to_string(&saphyr::Scalar::Boolean(true)));
171 assert_eq!("false", scalar_to_string(&saphyr::Scalar::Boolean(false)));
172 assert_eq!("42", scalar_to_string(&saphyr::Scalar::Integer(42)));
173 assert_eq!("-1", scalar_to_string(&saphyr::Scalar::Integer(-1)));
174 assert_eq!(
175 "3.14",
176 scalar_to_string(&saphyr::Scalar::FloatingPoint(OrderedFloat::from(3.14)))
177 );
178 assert_eq!(
179 "foo",
180 scalar_to_string(&saphyr::Scalar::String("foo".into()))
181 );
182 }
183
184 #[test]
185 fn test_format_scalar() {
186 assert_eq!("null", format_scalar(&saphyr::Scalar::Null));
187 assert_eq!("true", format_scalar(&saphyr::Scalar::Boolean(true)));
188 assert_eq!("false", format_scalar(&saphyr::Scalar::Boolean(false)));
189 assert_eq!("42", format_scalar(&saphyr::Scalar::Integer(42)));
190 assert_eq!("-1", format_scalar(&saphyr::Scalar::Integer(-1)));
191 assert_eq!(
192 "3.14",
193 format_scalar(&saphyr::Scalar::FloatingPoint(OrderedFloat::from(3.14)))
194 );
195 assert_eq!(
196 "\"foo\"",
197 format_scalar(&saphyr::Scalar::String("foo".into()))
198 );
199 }
200}