objc_encode/
parse.rs

1//! Parsing encodings from their string representation.
2
3use crate::Encoding;
4
5const QUALIFIERS: &'static [char] = &[
6    'r', // const
7    'n', // in
8    'N', // inout
9    'o', // out
10    'O', // bycopy
11    'R', // byref
12    'V', // oneway
13];
14
15fn rm_enc_prefix<'a>(s: &'a str, enc: &Encoding) -> Option<&'a str> {
16    use Encoding::*;
17    let code = match *enc {
18        Char      => "c",
19        Short     => "s",
20        Int       => "i",
21        Long      => "l",
22        LongLong  => "q",
23        UChar     => "C",
24        UShort    => "S",
25        UInt      => "I",
26        ULong     => "L",
27        ULongLong => "Q",
28        Float     => "f",
29        Double    => "d",
30        Bool      => "B",
31        Void      => "v",
32        String    => "*",
33        Object    => "@",
34        Block     => "@?",
35        Class     => "#",
36        Sel       => ":",
37        Unknown   => "?",
38        BitField(b) => {
39            let s = rm_prefix(s, "b")?;
40            return rm_int_prefix(s, b);
41        }
42        Pointer(t) => {
43            let s = rm_prefix(s, "^")?;
44            return rm_enc_prefix(s, t);
45        }
46        Array(len, item) => {
47            let mut s = s;
48            s = rm_prefix(s, "[")?;
49            s = rm_int_prefix(s, len)?;
50            s = rm_enc_prefix(s, item)?;
51            return rm_prefix(s, "]");
52        }
53        Struct(name, fields) => {
54            let mut s = s;
55            s = rm_prefix(s, "{")?;
56            s = rm_prefix(s, name)?;
57            s = rm_prefix(s, "=")?;
58            for field in fields {
59                s = rm_enc_prefix(s, field)?;
60            }
61            return rm_prefix(s, "}");
62        }
63        Union(name, members) => {
64            let mut s = s;
65            s = rm_prefix(s, "(")?;
66            s = rm_prefix(s, name)?;
67            s = rm_prefix(s, "=")?;
68            for member in members {
69                s = rm_enc_prefix(s, member)?;
70            }
71            return rm_prefix(s, ")");
72        }
73    };
74
75    rm_prefix(s, code)
76}
77
78fn chomp_int(s: &str) -> Option<(u32, &str)> {
79    // Chomp until we hit a non-digit
80    let (num, t) = match s.find(|c: char| !c.is_digit(10)) {
81        Some(i) => s.split_at(i),
82        None => (s, ""),
83    };
84    num.parse().map(|n| (n, t)).ok()
85}
86
87fn rm_int_prefix(s: &str, other: u32) -> Option<&str> {
88    chomp_int(s)
89        .and_then(|(n, t)| if other == n { Some(t) } else { None })
90}
91
92fn rm_prefix<'a>(s: &'a str, other: &str) -> Option<&'a str> {
93    if s.starts_with(other) {
94        Some(&s[other.len()..])
95    } else {
96        None
97    }
98}
99
100pub fn eq_enc(s: &str, enc: &Encoding) -> bool {
101    // strip qualifiers
102    let s = s.trim_start_matches(QUALIFIERS);
103
104    // if the given encoding can be successfully removed from the start
105    // and an empty string remains, they were equal!
106    rm_enc_prefix(s, enc).map_or(false, str::is_empty)
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn test_nested() {
115        let enc = Encoding::Struct("A", &[
116            Encoding::Struct("B", &[
117                Encoding::Char,
118                Encoding::Int,
119            ]),
120            Encoding::Char,
121            Encoding::Int,
122        ]);
123        assert!(eq_enc("{A={B=ci}ci}", &enc));
124        assert!(!eq_enc("{A={B=ci}ci", &enc));
125
126    }
127
128    #[test]
129    fn test_bitfield() {
130        assert!(eq_enc("b32", &Encoding::BitField(32)));
131        assert!(!eq_enc("b", &Encoding::BitField(32)));
132        assert!(!eq_enc("b-32", &Encoding::BitField(32)));
133    }
134
135    #[test]
136    fn test_qualifiers() {
137        assert!(eq_enc("Vv", &Encoding::Void));
138        assert!(eq_enc("r*", &Encoding::String));
139    }
140
141    #[test]
142    fn test_unicode() {
143        let fields = &[Encoding::Char, Encoding::Int];
144        assert!(eq_enc("{☃=ci}", &Encoding::Struct("☃", fields)));
145    }
146}