substrait_validator/input/
yaml.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Module for representing YAML input.
4//!
5//! We (ab)use [`serde_json::value::Value`] for this; the primary reason being
6//! that a [JSON schema](https://json-schema.org/) is used for basic schema
7//! validation of the YAML files, and the [`jsonschema`] crate we use for that
8//! uses [`serde_json`]'s representation). [`yaml_to_json()`] may be used to
9//! convert the output from [`serde_yaml`] to this structure.
10
11use crate::output::diagnostic;
12use crate::output::path;
13use crate::output::primitive_data;
14use crate::output::tree;
15use crate::parse::traversal;
16
17use serde_yaml::value::Value as Yaml;
18
19/// Type for the type used for arbitrary YAML values.
20pub type Value = serde_json::value::Value;
21
22/// Typedef for the type used for YAML arrays.
23pub type Array = Vec<Value>;
24
25/// Typedef for the type used for YAML maps.
26pub type Map = serde_json::map::Map<String, Value>;
27
28/// Converts a [`serde_yaml`] YAML structure into its equivalent JSON object
29/// model using [`serde_json`]'s types.
30pub fn yaml_to_json(y: Yaml, path: &path::Path) -> diagnostic::DiagResult<Value> {
31    match y {
32        Yaml::Null => Ok(Value::Null),
33        Yaml::Bool(b) => Ok(Value::Bool(b)),
34        Yaml::Number(n) => {
35            if let Some(u) = n.as_u64() {
36                Ok(Value::Number(u.into()))
37            } else if let Some(i) = n.as_i64() {
38                Ok(Value::Number(i.into()))
39            } else if let Some(f) = n.as_f64() {
40                Ok(Value::Number(
41                    serde_json::value::Number::from_f64(f).ok_or_else(|| {
42                        diag!(
43                            path.to_path_buf(),
44                            Error,
45                            YamlParseFailed,
46                            "{f} float is not supported"
47                        )
48                    })?,
49                ))
50            } else {
51                panic!("encountered serde_yaml::number::Number that cannot be represented as u64, i64, of f64");
52            }
53        }
54        Yaml::String(s) => Ok(Value::String(s)),
55        Yaml::Sequence(a) => Ok(Value::Array(
56            a.into_iter()
57                .enumerate()
58                .map(|(index, value)| yaml_to_json(value, &path.with_index(index)))
59                .collect::<diagnostic::DiagResult<Vec<Value>>>()?,
60        )),
61        Yaml::Mapping(m) => Ok(Value::Object(
62            m.into_iter()
63                .map(|(key, value)| {
64                    let key = key
65                        .as_str()
66                        .ok_or_else(|| {
67                            diag!(
68                                path.to_path_buf(),
69                                Error,
70                                YamlParseFailed,
71                                "non-string map keys are not supported"
72                            )
73                        })?
74                        .to_string();
75                    let path = path.with_field(&key);
76                    let value = yaml_to_json(value, &path)?;
77                    Ok((key, value))
78                })
79                .collect::<diagnostic::DiagResult<serde_json::value::Map<String, Value>>>()?,
80        )),
81        Yaml::Tagged(_) => Err(diag!(
82            path.to_path_buf(),
83            Error,
84            YamlParseFailed,
85            "YAML tagged values are not supported"
86        )),
87    }
88}
89
90impl crate::input::traits::InputNode for Value {
91    fn type_to_node() -> tree::Node {
92        tree::NodeType::YamlMap.into()
93    }
94
95    fn data_to_node(&self) -> tree::Node {
96        match self {
97            Value::Null => tree::NodeType::YamlPrimitive(primitive_data::PrimitiveData::Null),
98            Value::Bool(b) => {
99                tree::NodeType::YamlPrimitive(primitive_data::PrimitiveData::Bool(*b))
100            }
101            Value::Number(n) => tree::NodeType::YamlPrimitive(
102                n.as_u64()
103                    .map(primitive_data::PrimitiveData::Unsigned)
104                    .or_else(|| n.as_i64().map(primitive_data::PrimitiveData::Signed))
105                    .or_else(|| n.as_f64().map(primitive_data::PrimitiveData::Float))
106                    .unwrap(),
107            ),
108            Value::String(s) => {
109                tree::NodeType::YamlPrimitive(primitive_data::PrimitiveData::String(s.clone()))
110            }
111            Value::Array(_) => tree::NodeType::YamlArray,
112            Value::Object(_) => tree::NodeType::YamlMap,
113        }
114        .into()
115    }
116
117    fn oneof_variant(&self) -> Option<&'static str> {
118        None
119    }
120
121    fn parse_unknown(&self, context: &mut crate::parse::context::Context<'_>) -> bool {
122        match self {
123            Value::Array(array) => {
124                let mut any = false;
125                for (index, _) in array.iter().enumerate() {
126                    if !context.field_parsed(index.to_string()) {
127                        traversal::push_yaml_element(array, context, index, true, |_, _| Ok(()));
128                        any = true;
129                    }
130                }
131                any
132            }
133            Value::Object(object) => {
134                let mut any = false;
135                let mut keys: Vec<_> = object.keys().collect();
136                keys.sort();
137                for field_name in keys {
138                    if !context.field_parsed(field_name) {
139                        traversal::push_yaml_field(self, context, field_name, true, |_, _| Ok(()))
140                            .unwrap();
141                        any = true;
142                    }
143                }
144                any
145            }
146            _ => false,
147        }
148    }
149}