1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
use pest::iterators::Pairs;

use crate::{convention_struct_tag_parser::Rule, StructTagParseError};

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum JsonStructTag {
    // https://github.com/golang/go/blob/go1.16.3/src/encoding/json/encode.go#L1259
    Ignored,
    // https://github.com/golang/go/blob/go1.16.3/src/encoding/json/encode.go#L1262
    Normal(JsonStructTagName, Vec<JsonStructTagOption>),
}

pub type JsonStructTagName = Option<String>;

#[derive(PartialEq, Eq, Debug, Clone)]
pub enum JsonStructTagOption {
    // https://github.com/golang/go/blob/go1.16.3/src/encoding/json/encode.go#L1278
    String,
    // https://github.com/golang/go/blob/go1.16.3/src/encoding/json/encode.go#L1300
    Omitempty,
    //
    Unknown(String),
}
impl From<&str> for JsonStructTagOption {
    fn from(s: &str) -> Self {
        match s {
            "string" => Self::String,
            "omitempty" => Self::Omitempty,
            _ => Self::Unknown(s.to_owned()),
        }
    }
}

impl JsonStructTag {
    pub(crate) fn from_json_pairs(mut pairs: Pairs<'_, Rule>) -> Result<Self, StructTagParseError> {
        let name_pair = pairs.next().ok_or(StructTagParseError::Unknown)?;
        let name = name_pair.as_str();

        if name == "-" && pairs.peek().is_none() {
            return Ok(Self::Ignored);
        }

        let name = if name.is_empty() {
            None
        } else {
            Some(name.to_owned())
        };

        let options = pairs
            .filter(|pair| !pair.as_str().is_empty())
            .map(|pair| JsonStructTagOption::from(pair.as_str()))
            .collect();

        Ok(Self::Normal(name, options))
    }
}