openpql_pql_parser/ast/
from_clause.rs

1use super::{
2    Entry, Error, FxHashMap, Ident, Loc, ResultE, Str, String, fmt, user_err,
3};
4
5#[derive(PartialEq, Eq, Default)]
6pub struct FromClause<'i> {
7    pub inner: FxHashMap<String, FromItem<'i>>,
8    pub loc: (Loc, Loc),
9}
10
11impl<'i> FromClause<'i> {
12    const BOARD_KEY: &'static str = "board";
13    const GAME_KEY: &'static str = "game";
14    const DEADCARD_KEY: &'static str = "dead";
15    const NON_PLAYER_KEYS: [&'static str; 3] =
16        [Self::BOARD_KEY, Self::GAME_KEY, Self::DEADCARD_KEY];
17
18    pub(crate) fn new<T: IntoIterator<Item = FromItem<'i>>>(
19        items: T,
20        loc: (Loc, Loc),
21    ) -> ResultE<'i, Self> {
22        let mut res = Self::default();
23
24        for item in items {
25            let key = item.key.inner.to_ascii_lowercase();
26
27            if let Entry::Vacant(e) = res.inner.entry(key) {
28                e.insert(item);
29            } else {
30                return Err(user_err(Error::DuplicatedKeyInFrom(item.key.loc)));
31            }
32        }
33
34        res.loc = loc;
35
36        Ok(res)
37    }
38
39    fn get_val(&self, key: &str) -> Option<&Str<'_>> {
40        self.inner.get(key).as_ref().map(|item| &item.value)
41    }
42
43    pub fn get_board_range(&self) -> Option<&Str<'_>> {
44        self.get_val(Self::BOARD_KEY)
45    }
46
47    pub fn get_game(&self) -> Option<&Str<'_>> {
48        self.get_val(Self::GAME_KEY)
49    }
50
51    pub fn get_dead(&self) -> Option<&Str<'_>> {
52        self.get_val(Self::DEADCARD_KEY)
53    }
54
55    pub fn get_players(&self) -> Vec<(&Ident<'_>, &Str<'_>)> {
56        self.inner
57            .keys()
58            .filter(|k| !Self::NON_PLAYER_KEYS.contains(&k.as_str()))
59            .map(|k| &self.inner[k])
60            .map(|item| (&item.key, &item.value))
61            .collect()
62    }
63}
64
65impl fmt::Debug for FromClause<'_> {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        f.debug_map()
68            .entries(
69                self.inner
70                    .values()
71                    .map(|item| (item.key.inner, item.value.inner)),
72            )
73            .finish()
74    }
75}
76
77#[derive(PartialEq, Eq, Debug)]
78pub struct FromItem<'i> {
79    pub key: Ident<'i>,
80    pub value: Str<'i>,
81}
82
83impl<'i, U, V> From<(U, V)> for FromItem<'i>
84where
85    U: Into<Ident<'i>>,
86    V: Into<Str<'i>>,
87{
88    fn from(t: (U, V)) -> Self {
89        Self {
90            key: t.0.into(),
91            value: t.1.into(),
92        }
93    }
94}
95
96#[cfg(test)]
97#[cfg_attr(coverage_nightly, coverage(off))]
98mod tests {
99    use super::*;
100    use crate::*;
101
102    fn mk_inner<'s>(
103        src: &'s str,
104        kvs: &[(&'static str, &'static str)],
105    ) -> FxHashMap<String, FromItem<'s>> {
106        let mut res = FxHashMap::default();
107        for (key, val) in kvs {
108            let id = Ident::from((*key, loc(src, key)));
109            let s = Str::from((strip_str(val), loc(src, val)));
110            res.insert((*key).to_string(), FromItem::from((id, s)));
111        }
112
113        res
114    }
115
116    fn assert_from_clause(src: &str, kvs: &[(&'static str, &'static str)]) {
117        assert_eq!(parse_from_clause(src).unwrap().inner, mk_inner(src, kvs));
118    }
119
120    #[test]
121    fn test_from_clause() {
122        let src = "from game='holdem', hero='AA'";
123
124        assert_from_clause(src, &[("game", "'holdem'"), ("hero", "'AA'")]);
125    }
126
127    #[test]
128    fn test_from_clause_norm_key() {
129        let obj = parse_from_clause("from GAME=''").unwrap().inner;
130        assert!(obj.contains_key("game"), "should use lowercase for keys");
131        assert!(!obj.contains_key("GAME"));
132    }
133
134    #[test]
135    fn test_values() {
136        let obj = parse_from_clause("from hero='AA'").unwrap();
137        assert_eq!(obj.get_game(), None);
138        assert_eq!(obj.get_board_range(), None);
139        assert_eq!(obj.get_dead(), None);
140        //assert_eq!(obj.get_players(), &[("hero", "AA")]);
141    }
142
143    fn assert_err(src: &str, expected: Error) {
144        assert_eq!(parse_from_clause(src).unwrap_err(), expected);
145    }
146
147    #[test]
148    fn test_from_clause_dup_key() {
149        let src = "from GAME='', game=''";
150        assert_err(src, Error::DuplicatedKeyInFrom(loc(src, "game")));
151    }
152
153    #[test]
154    fn test_debug() {
155        let obj = parse_from_clause("from game='holdem', hero='AA'").unwrap();
156
157        assert!(format!("{obj:?}").find(r#"hero": "AA"#).is_some());
158    }
159}