Skip to main content

helm_schema_syntax/
literal.rs

1use serde_json::{Map, Value};
2
3use crate::{
4    BlockScalar, MappingEntry, Node, ScalarPart, ScalarParts, SequenceItem, TemplatedDocument,
5};
6
7impl TemplatedDocument<'_> {
8    /// Projects the literal portion of a mapping entry's value.
9    #[must_use]
10    pub fn literal_mapping_value(&self, entry: &MappingEntry) -> Option<Value> {
11        if let Some(value) = &entry.value {
12            return self.literal_scalar(value);
13        }
14        if let Some(block) = &entry.block {
15            return self.literal_block(block);
16        }
17        if entry.children.is_empty() {
18            return Some(Value::Null);
19        }
20        self.literal_nodes(&entry.children)
21    }
22
23    /// Projects the literal portion of a sequence item's value.
24    #[must_use]
25    pub fn literal_sequence_value(&self, item: &SequenceItem) -> Option<Value> {
26        if let Some(value) = &item.value {
27            return self.literal_scalar(value);
28        }
29        if let Some(block) = &item.block {
30            return self.literal_block(block);
31        }
32        if item.children.is_empty() {
33            return Some(Value::Null);
34        }
35        self.literal_nodes(&item.children)
36    }
37
38    /// Projects scalar parts when they contain no template-action hole.
39    #[must_use]
40    pub fn literal_scalar(&self, scalar: &ScalarParts) -> Option<Value> {
41        if scalar
42            .parts
43            .iter()
44            .any(|part| matches!(part, ScalarPart::Hole(_)))
45        {
46            return None;
47        }
48        self.literal_source(scalar.span.start, scalar.span.end)
49    }
50
51    fn literal_nodes(&self, nodes: &[Node]) -> Option<Value> {
52        let mut mapping = Map::new();
53        let mut sequence = Vec::new();
54        let mut shape = None;
55
56        for node in nodes {
57            match node {
58                Node::Mapping(entry) => {
59                    if shape == Some(false) {
60                        return None;
61                    }
62                    shape = Some(true);
63                    let key = self.literal_scalar(&entry.key)?;
64                    mapping.insert(
65                        key.as_str()?.to_string(),
66                        self.literal_mapping_value(entry)?,
67                    );
68                }
69                Node::Sequence(item) => {
70                    if shape == Some(true) {
71                        return None;
72                    }
73                    shape = Some(false);
74                    sequence.push(self.literal_sequence_value(item)?);
75                }
76                Node::Comment(_) | Node::Control(_) | Node::Output(_) | Node::Opaque(_) => {}
77                Node::Scalar(_) => return None,
78            }
79        }
80
81        match shape {
82            Some(true) => Some(Value::Object(mapping)),
83            Some(false) => Some(Value::Array(sequence)),
84            None => Some(Value::Null),
85        }
86    }
87
88    fn literal_block(&self, block: &BlockScalar) -> Option<Value> {
89        if !block.holes.is_empty() {
90            return None;
91        }
92        self.literal_source(block.header.start, block.body.end.max(block.header.end))
93    }
94
95    fn literal_source(&self, start: usize, end: usize) -> Option<Value> {
96        let source = self.source.get(start..end)?.trim();
97        let yaml = serde_yaml::from_str::<serde_yaml::Value>(source).ok()?;
98        serde_json::to_value(yaml).ok()
99    }
100}