hyper_scripter/
tag.rs

1use crate::error::{DisplayError, DisplayResult, FormatCode::Tag as TagCode};
2use crate::script_type::ScriptType;
3use crate::util::illegal_name;
4use crate::util::{impl_de_by_from_str, impl_ser_by_to_string};
5use fxhash::FxHashSet as HashSet;
6use std::fmt::{Display, Formatter, Result as FmtResult};
7use std::str::FromStr;
8
9pub type TagSet = HashSet<Tag>;
10
11#[derive(Debug, Clone, Eq, PartialEq, Default)]
12pub struct TagSelectorGroup(Vec<TagSelector>);
13impl TagSelectorGroup {
14    pub fn push(&mut self, selector: TagSelector) {
15        if selector.append {
16            self.0.push(selector);
17        } else {
18            self.0 = vec![selector];
19        }
20    }
21    pub fn select(&self, tags: &TagSet, ty: &ScriptType) -> bool {
22        let mut pass = false;
23        for f in self.0.iter() {
24            let res = f.select(tags, ty);
25            match res {
26                SelectResult::MandatoryFalse => return false,
27                SelectResult::Normal(res) => pass = res,
28                SelectResult::None => (),
29            }
30        }
31        pass
32    }
33}
34impl From<TagSelector> for TagSelectorGroup {
35    fn from(t: TagSelector) -> Self {
36        TagSelectorGroup(vec![t])
37    }
38}
39
40#[derive(Debug, Clone, Eq, PartialEq)]
41pub struct TagSelector {
42    tags: TagGroup,
43    pub append: bool,
44}
45impl_de_by_from_str!(TagSelector);
46impl_ser_by_to_string!(TagSelector);
47
48#[derive(Debug, Clone, Eq, PartialEq, Default)]
49pub struct TagGroup(Vec<TagControl>);
50impl_de_by_from_str!(TagGroup);
51impl_ser_by_to_string!(TagGroup);
52
53#[derive(Debug, Clone, Eq, PartialEq)]
54pub struct TagControl {
55    allow: bool,
56    mandatory: bool,
57    tag: TagOrType,
58}
59
60#[derive(Debug, Clone, Eq, PartialEq, Display)]
61pub enum TagOrType {
62    #[display(fmt = "@{}", _0)]
63    Type(ScriptType),
64    #[display(fmt = "{}", _0)]
65    Tag(Tag),
66}
67impl_de_by_from_str!(TagOrType);
68impl_ser_by_to_string!(TagOrType);
69impl FromStr for TagOrType {
70    type Err = DisplayError;
71    fn from_str(s: &str) -> DisplayResult<Self> {
72        Ok(if s.starts_with('@') {
73            TagOrType::Type(s[1..].parse()?)
74        } else {
75            TagOrType::Tag(s.parse()?)
76        })
77    }
78}
79
80#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
81pub struct Tag(String);
82impl AsRef<str> for Tag {
83    fn as_ref(&self) -> &str {
84        &self.0
85    }
86}
87impl Tag {
88    pub fn match_all(&self) -> bool {
89        // TODO: loop invariant 優化
90        &self.0 == "all"
91    }
92    pub fn new_unchecked(s: String) -> Self {
93        Tag(s)
94    }
95}
96impl FromStr for Tag {
97    type Err = DisplayError;
98    fn from_str(s: &str) -> DisplayResult<Self> {
99        if illegal_name(s) {
100            log::error!("標籤格式不符:{}", s);
101            return TagCode.to_display_res(s.to_owned());
102        }
103        Ok(Tag(s.to_owned()))
104    }
105}
106impl FromStr for TagControl {
107    type Err = DisplayError;
108    fn from_str(mut s: &str) -> DisplayResult<Self> {
109        let allow = if s.starts_with('^') {
110            s = &s[1..s.len()];
111            false
112        } else {
113            true
114        };
115        let mandatory = if s.ends_with(MANDATORY_SUFFIX) {
116            s = &s[0..(s.len() - MANDATORY_SUFFIX.len())];
117            true
118        } else {
119            false
120        };
121        Ok(TagControl {
122            tag: s.parse()?,
123            allow,
124            mandatory,
125        })
126    }
127}
128const MANDATORY_SUFFIX: &str = "!";
129const APPEND_PREFIX: &str = "+";
130impl FromStr for TagSelector {
131    type Err = DisplayError;
132    fn from_str(mut s: &str) -> DisplayResult<Self> {
133        let append = if s.starts_with(APPEND_PREFIX) {
134            s = &s[APPEND_PREFIX.len()..];
135            true
136        } else {
137            false
138        };
139
140        Ok(TagSelector {
141            tags: s.parse()?,
142            append,
143        })
144    }
145}
146
147impl Display for TagSelector {
148    fn fmt(&self, w: &mut Formatter<'_>) -> FmtResult {
149        if self.append {
150            write!(w, "{}", APPEND_PREFIX)?;
151        }
152        write!(w, "{}", self.tags)?;
153        Ok(())
154    }
155}
156
157impl TagSelector {
158    pub fn push(&mut self, other: Self) {
159        if other.append {
160            self.tags.0.extend(other.tags.0.into_iter());
161        } else {
162            *self = other
163        }
164    }
165    pub fn fill_allowed_map<U>(self, set: &mut std::collections::HashSet<Tag, U>)
166    where
167        U: std::hash::BuildHasher,
168    {
169        for control in self.tags.0.into_iter() {
170            let tag = match control.tag {
171                TagOrType::Type(_) => continue, // 類型篩選,跳過
172                TagOrType::Tag(t) => t,
173            };
174            if control.allow {
175                // NOTE: `match_all` 是特殊的,不用被外界知道,雖然知道了也不會怎樣
176                if tag.match_all() {
177                    continue;
178                }
179                set.insert(tag);
180            } else {
181                if tag.match_all() {
182                    set.clear(); // XXX: is this the right thing to do?
183                    continue;
184                }
185                set.remove(&tag);
186            }
187        }
188    }
189    pub fn into_allowed_iter(self) -> impl Iterator<Item = Tag> {
190        let mut set = HashSet::default();
191        self.fill_allowed_map(&mut set);
192        set.into_iter()
193    }
194    pub fn select(&self, tags: &TagSet, ty: &ScriptType) -> SelectResult {
195        self.tags.select(tags, ty)
196    }
197}
198
199impl FromStr for TagGroup {
200    type Err = DisplayError;
201    fn from_str(s: &str) -> DisplayResult<Self> {
202        let mut tags = vec![];
203        if !s.is_empty() {
204            for ctrl in s.split(',') {
205                tags.push(ctrl.parse()?);
206            }
207        }
208        Ok(TagGroup(tags))
209    }
210}
211
212impl Display for TagGroup {
213    fn fmt(&self, w: &mut Formatter<'_>) -> FmtResult {
214        let mut first = true;
215        for f in self.0.iter() {
216            if !first {
217                write!(w, ",")?;
218            }
219            first = false;
220            if !f.allow {
221                write!(w, "^")?;
222            }
223            write!(w, "{}", f.tag)?;
224            if f.mandatory {
225                write!(w, "{}", MANDATORY_SUFFIX)?;
226            }
227        }
228        Ok(())
229    }
230}
231
232pub enum SelectResult {
233    None,
234    MandatoryFalse,
235    Normal(bool),
236}
237impl SelectResult {
238    pub fn is_true(&self) -> bool {
239        matches!(self, SelectResult::Normal(true))
240    }
241}
242
243impl TagGroup {
244    pub fn select(&self, tags: &TagSet, ty: &ScriptType) -> SelectResult {
245        let mut pass = SelectResult::None;
246        for ctrl in self.0.iter() {
247            let hit = match &ctrl.tag {
248                TagOrType::Type(t) => ty == t,
249                TagOrType::Tag(t) => t.match_all() || tags.contains(t),
250            };
251            if ctrl.mandatory {
252                if ctrl.allow {
253                    if !hit {
254                        return SelectResult::MandatoryFalse;
255                    }
256                } else {
257                    if hit {
258                        return SelectResult::MandatoryFalse;
259                    }
260                }
261            }
262            if hit {
263                pass = SelectResult::Normal(ctrl.allow);
264            }
265        }
266        pass
267    }
268}