Skip to main content

dbt_yaml/value/
tagged.rs

1use crate::value::de::{MapRefDeserializer, SeqRefDeserializer};
2use crate::value::Value;
3use crate::Error;
4use serde::de::value::{BorrowedStrDeserializer, StrDeserializer};
5use serde::de::{
6    Deserialize, DeserializeSeed, Deserializer, EnumAccess, Error as _, VariantAccess, Visitor,
7};
8use serde::forward_to_deserialize_any;
9use serde::ser::{Serialize, SerializeMap, Serializer};
10use std::cmp::Ordering;
11use std::fmt::{self, Debug, Display};
12use std::hash::{Hash, Hasher};
13use std::mem;
14
15use super::de::ValueDeserializer;
16
17/// A representation of YAML's `!Tag` syntax, used for enums.
18///
19/// Refer to the example code on [`TaggedValue`] for an example of deserializing
20/// tagged values.
21#[derive(Clone)]
22pub struct Tag {
23    pub(crate) string: String,
24}
25
26/// A `Tag` + `Value` representing a tagged YAML scalar, sequence, or mapping.
27///
28/// ```
29/// use dbt_yaml::value::TaggedValue;
30/// use std::collections::BTreeMap;
31///
32/// let yaml = "
33///     scalar: !Thing x
34///     sequence_flow: !Thing [first]
35///     sequence_block: !Thing
36///       - first
37///     mapping_flow: !Thing {k: v}
38///     mapping_block: !Thing
39///       k: v
40/// ";
41///
42/// let data: BTreeMap<String, TaggedValue> = dbt_yaml::from_str(yaml).unwrap();
43/// assert!(data["scalar"].tag == "Thing");
44/// assert!(data["sequence_flow"].tag == "Thing");
45/// assert!(data["sequence_block"].tag == "Thing");
46/// assert!(data["mapping_flow"].tag == "Thing");
47/// assert!(data["mapping_block"].tag == "Thing");
48///
49/// // The leading '!' in tags are not significant. The following is also true.
50/// assert!(data["scalar"].tag == "!Thing");
51/// ```
52#[derive(Clone, PartialEq, PartialOrd, Hash, Debug)]
53pub struct TaggedValue {
54    #[allow(missing_docs)]
55    pub tag: Tag,
56    #[allow(missing_docs)]
57    pub value: Value,
58}
59
60impl Tag {
61    /// Create tag.
62    ///
63    /// The leading '!' is not significant. It may be provided, but does not
64    /// have to be. The following are equivalent:
65    ///
66    /// ```
67    /// use dbt_yaml::value::Tag;
68    ///
69    /// assert_eq!(Tag::new("!Thing"), Tag::new("Thing"));
70    ///
71    /// let tag = Tag::new("Thing");
72    /// assert!(tag == "Thing");
73    /// assert!(tag == "!Thing");
74    /// assert!(tag.to_string() == "!Thing");
75    ///
76    /// let tag = Tag::new("!Thing");
77    /// assert!(tag == "Thing");
78    /// assert!(tag == "!Thing");
79    /// assert!(tag.to_string() == "!Thing");
80    /// ```
81    ///
82    /// Such a tag would serialize to `!Thing` in YAML regardless of whether a
83    /// '!' was included in the call to `Tag::new`.
84    ///
85    /// # Panics
86    ///
87    /// Panics if `string.is_empty()`. There is no syntax in YAML for an empty
88    /// tag.
89    pub fn new(string: impl Into<String>) -> Self {
90        let tag: String = string.into();
91        assert!(!tag.is_empty(), "empty YAML tag is not allowed");
92        Tag { string: tag }
93    }
94}
95
96impl Value {
97    pub(crate) fn untag(self) -> Self {
98        let mut cur = self;
99        while let Value::Tagged(tagged, ..) = cur {
100            cur = tagged.value;
101        }
102        cur
103    }
104
105    pub(crate) fn untag_ref(&self) -> &Self {
106        let mut cur = self;
107        while let Value::Tagged(tagged, ..) = cur {
108            cur = &tagged.value;
109        }
110        cur
111    }
112
113    pub(crate) fn untag_mut(&mut self) -> &mut Self {
114        let mut cur = self;
115        while let Value::Tagged(tagged, ..) = cur {
116            cur = &mut tagged.value;
117        }
118        cur
119    }
120}
121
122pub(crate) fn nobang(maybe_banged: &str) -> &str {
123    match maybe_banged.strip_prefix('!') {
124        Some("") | None => maybe_banged,
125        Some(unbanged) => unbanged,
126    }
127}
128
129impl Eq for Tag {}
130
131impl PartialEq for Tag {
132    fn eq(&self, other: &Tag) -> bool {
133        PartialEq::eq(nobang(&self.string), nobang(&other.string))
134    }
135}
136
137impl<T> PartialEq<T> for Tag
138where
139    T: ?Sized + AsRef<str>,
140{
141    fn eq(&self, other: &T) -> bool {
142        PartialEq::eq(nobang(&self.string), nobang(other.as_ref()))
143    }
144}
145
146impl Ord for Tag {
147    fn cmp(&self, other: &Self) -> Ordering {
148        Ord::cmp(nobang(&self.string), nobang(&other.string))
149    }
150}
151
152impl PartialOrd for Tag {
153    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
154        Some(self.cmp(other))
155    }
156}
157
158impl Hash for Tag {
159    fn hash<H: Hasher>(&self, hasher: &mut H) {
160        nobang(&self.string).hash(hasher);
161    }
162}
163
164impl Display for Tag {
165    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
166        write!(formatter, "!{}", nobang(&self.string))
167    }
168}
169
170impl Debug for Tag {
171    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
172        Display::fmt(self, formatter)
173    }
174}
175
176impl Serialize for TaggedValue {
177    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
178    where
179        S: Serializer,
180    {
181        struct SerializeTag<'a>(&'a Tag);
182
183        impl Serialize for SerializeTag<'_> {
184            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
185            where
186                S: Serializer,
187            {
188                serializer.collect_str(self.0)
189            }
190        }
191
192        let mut map = serializer.serialize_map(Some(1))?;
193        map.serialize_entry(&SerializeTag(&self.tag), &self.value)?;
194        map.end()
195    }
196}
197
198impl<'de> Deserialize<'de> for TaggedValue {
199    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
200    where
201        D: Deserializer<'de>,
202    {
203        struct TaggedValueVisitor;
204
205        impl<'de> Visitor<'de> for TaggedValueVisitor {
206            type Value = TaggedValue;
207
208            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
209                formatter.write_str("a YAML value with a !Tag")
210            }
211
212            fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
213            where
214                A: EnumAccess<'de>,
215            {
216                let (tag, contents) = data.variant_seed(TagStringVisitor)?;
217                let value = contents.newtype_variant()?;
218                Ok(TaggedValue { tag, value })
219            }
220        }
221
222        deserializer.deserialize_any(TaggedValueVisitor)
223    }
224}
225
226impl<'de> Deserializer<'de> for TaggedValue {
227    type Error = Error;
228
229    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
230    where
231        V: Visitor<'de>,
232    {
233        visitor.visit_enum(self)
234    }
235
236    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Error>
237    where
238        V: Visitor<'de>,
239    {
240        drop(self);
241        visitor.visit_unit()
242    }
243
244    forward_to_deserialize_any! {
245        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
246        byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct
247        map struct enum identifier
248    }
249}
250
251impl<'de> EnumAccess<'de> for TaggedValue {
252    type Error = Error;
253    type Variant = ValueDeserializer<'static, 'static, 'static>;
254
255    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Error>
256    where
257        V: DeserializeSeed<'de>,
258    {
259        let tag = StrDeserializer::<Error>::new(nobang(&self.tag.string));
260        let value = seed.deserialize(tag)?;
261        Ok((value, ValueDeserializer::new(self.value)))
262    }
263}
264
265impl<'de> Deserializer<'de> for &'de TaggedValue {
266    type Error = Error;
267
268    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
269    where
270        V: Visitor<'de>,
271    {
272        visitor.visit_enum(self)
273    }
274
275    fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Error>
276    where
277        V: Visitor<'de>,
278    {
279        visitor.visit_unit()
280    }
281
282    forward_to_deserialize_any! {
283        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
284        byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct
285        map struct enum identifier
286    }
287}
288
289impl<'de> EnumAccess<'de> for &'de TaggedValue {
290    type Error = Error;
291    type Variant = &'de Value;
292
293    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Error>
294    where
295        V: DeserializeSeed<'de>,
296    {
297        let tag = BorrowedStrDeserializer::<Error>::new(nobang(&self.tag.string));
298        let value = seed.deserialize(tag)?;
299        Ok((value, &self.value))
300    }
301}
302
303impl<'de> VariantAccess<'de> for &'de Value {
304    type Error = Error;
305
306    fn unit_variant(self) -> Result<(), Error> {
307        Deserialize::deserialize(self)
308    }
309
310    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Error>
311    where
312        T: DeserializeSeed<'de>,
313    {
314        seed.deserialize(self)
315    }
316
317    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value, Error>
318    where
319        V: Visitor<'de>,
320    {
321        if let Value::Sequence(v, ..) = self {
322            Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor)
323        } else {
324            Err(Error::invalid_type(self.unexpected(), &"tuple variant"))
325        }
326    }
327
328    fn struct_variant<V>(
329        self,
330        _fields: &'static [&'static str],
331        visitor: V,
332    ) -> Result<V::Value, Error>
333    where
334        V: Visitor<'de>,
335    {
336        if let Value::Mapping(v, ..) = self {
337            Deserializer::deserialize_any(MapRefDeserializer::new(v), visitor)
338        } else {
339            Err(Error::invalid_type(self.unexpected(), &"struct variant"))
340        }
341    }
342}
343
344pub(crate) struct TagStringVisitor;
345
346impl Visitor<'_> for TagStringVisitor {
347    type Value = Tag;
348
349    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
350        formatter.write_str("a YAML tag string")
351    }
352
353    fn visit_str<E>(self, string: &str) -> Result<Self::Value, E>
354    where
355        E: serde::de::Error,
356    {
357        self.visit_string(string.to_owned())
358    }
359
360    fn visit_string<E>(self, string: String) -> Result<Self::Value, E>
361    where
362        E: serde::de::Error,
363    {
364        if string.is_empty() {
365            return Err(E::custom("empty YAML tag is not allowed"));
366        }
367        Ok(Tag::new(string))
368    }
369}
370
371impl<'de> DeserializeSeed<'de> for TagStringVisitor {
372    type Value = Tag;
373
374    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
375    where
376        D: Deserializer<'de>,
377    {
378        deserializer.deserialize_string(self)
379    }
380}
381
382pub(crate) enum MaybeTag<T> {
383    Tag(String),
384    NotTag(T),
385}
386
387pub(crate) fn check_for_tag<T>(value: &T) -> MaybeTag<String>
388where
389    T: ?Sized + Display,
390{
391    enum CheckForTag {
392        Empty,
393        Bang,
394        Tag(String),
395        NotTag(String),
396    }
397
398    impl fmt::Write for CheckForTag {
399        fn write_str(&mut self, s: &str) -> fmt::Result {
400            if s.is_empty() {
401                return Ok(());
402            }
403            match self {
404                CheckForTag::Empty => {
405                    if s == "!" {
406                        *self = CheckForTag::Bang;
407                    } else {
408                        *self = CheckForTag::NotTag(s.to_owned());
409                    }
410                }
411                CheckForTag::Bang => {
412                    *self = CheckForTag::Tag(s.to_owned());
413                }
414                CheckForTag::Tag(string) => {
415                    let mut string = mem::take(string);
416                    string.push_str(s);
417                    *self = CheckForTag::NotTag(string);
418                }
419                CheckForTag::NotTag(string) => {
420                    string.push_str(s);
421                }
422            }
423            Ok(())
424        }
425    }
426
427    let mut check_for_tag = CheckForTag::Empty;
428    fmt::write(&mut check_for_tag, format_args!("{}", value)).unwrap();
429    match check_for_tag {
430        CheckForTag::Empty => MaybeTag::NotTag(String::new()),
431        CheckForTag::Bang => MaybeTag::NotTag("!".to_owned()),
432        CheckForTag::Tag(string) => MaybeTag::Tag(string),
433        CheckForTag::NotTag(string) => MaybeTag::NotTag(string),
434    }
435}