Skip to main content

openpql_pql_parser/ast/
from_clause.rs

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