1use std::{
2 collections::VecDeque,
3 fmt::{Display, Formatter},
4 io::Write,
5};
6
7use contracts::debug_requires;
8use derive_getters::Getters;
9use smol_str::SmolStr;
10
11use crate::{
12 ast::{
13 AnnFun, WplField,
14 debug::{DebugFormat, DepIndent},
15 group::WplGroup,
16 },
17 compat::New1,
18 parser::MergeTags,
19};
20
21#[derive(Default, Clone, Getters, Debug)]
22pub struct WplRule {
23 pub name: SmolStr,
24 pub statement: WplStatementType,
25}
26
27#[derive(Debug, PartialEq, Clone)]
28pub enum WplStatementType {
29 Express(WplExpress),
30}
31
32impl DebugFormat for WplStatementType {
33 fn write<W>(&self, w: &mut W) -> std::io::Result<()>
34 where
35 W: ?Sized + Write + DepIndent,
36 {
37 match self {
38 WplStatementType::Express(define) => define.write(w),
39 }
40 }
41}
42
43impl Display for WplStatementType {
44 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45 write!(f, "{}", self.fmt_string().unwrap_or_default())
46 }
47}
48
49impl Default for WplStatementType {
50 fn default() -> Self {
51 WplStatementType::Express(WplExpress::default())
52 }
53}
54
55impl WplStatementType {
65 pub fn first_group(&self) -> Option<&WplGroup> {
66 match self {
67 WplStatementType::Express(rule) => rule.group.first(),
68 }
69 }
70 pub fn first_field(&self) -> Option<&WplField> {
71 match self {
72 WplStatementType::Express(rule) => rule.group.first().and_then(|x| x.first()),
73 }
74 }
75
76 pub fn tags(&self) -> &Option<AnnFun> {
77 match self {
78 WplStatementType::Express(rule) => &rule.tags,
79 }
80 }
81}
82
83#[derive(Default, Debug, PartialEq, Clone)]
84pub struct WplExpress {
85 pub pipe_process: Vec<SmolStr>,
87 pub group: Vec<WplGroup>,
88 pub tags: Option<AnnFun>,
89}
90
91impl MergeTags for WplExpress {
92 fn merge_tags(&mut self, other_tags: &Option<AnnFun>) {
93 if let Some(tags) = &mut self.tags {
94 tags.merge_tags(other_tags)
95 } else {
96 self.tags = other_tags.clone()
97 }
98 }
99}
100
101impl Display for WplExpress {
102 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
103 write!(f, "{}", self.fmt_string().unwrap_or_default())
104 }
105}
106
107impl DebugFormat for WplExpress {
108 fn write<W>(&self, w: &mut W) -> std::io::Result<()>
109 where
110 W: ?Sized + Write + DepIndent,
111 {
112 let depth = w.add_indent();
113 for (index, pipe) in self.pipe_process.iter().enumerate() {
114 if index == 0 {
115 self.write_indent(w, depth)?;
116 write!(w, "|")?;
117 }
118 write!(w, "{}|", pipe)?;
119 }
120
121 for (index, field) in self.group.iter().enumerate() {
122 if index != 0 {
123 write!(w, ",")?;
124 self.write_new_line(w)?;
125 }
126 self.write_indent(w, depth)?;
127 field.write(w)?;
128 }
129 w.sub_indent();
130 Ok(())
131 }
132}
133
134impl New1<Vec<WplGroup>> for WplExpress {
135 fn new(group: Vec<WplGroup>) -> Self {
136 WplExpress {
137 pipe_process: Vec::new(),
138 group,
139 tags: None,
140 }
141 }
142}
143
144impl New1<Vec<WplField>> for WplExpress {
145 fn new(fields: Vec<WplField>) -> Self {
146 let group = vec![WplGroup::new(fields)];
147 WplExpress {
148 pipe_process: Vec::new(),
149 group,
150 tags: None,
151 }
152 }
153}
154
155impl WplRule {
156 pub fn add_tags(mut self, tags: Option<AnnFun>) -> Self {
157 match self.statement {
158 WplStatementType::Express(mut define) => {
159 define.tags = tags;
160 self.statement = WplStatementType::Express(define);
161 self
162 }
163 }
164 }
165}
166
167impl DebugFormat for WplRule {
168 fn write<W>(&self, w: &mut W) -> std::io::Result<()>
169 where
170 W: ?Sized + Write + DepIndent,
171 {
172 let depth = w.add_indent();
173
174 if let Some(tags) = &self.statement.tags() {
175 self.write_indent(w, depth)?;
176 tags.write(w)?;
177 }
178 self.write_indent(w, depth)?;
179
180 write!(w, "rule {} ", &self.name)?;
181 self.write_open_brace(w)?;
182 self.write_new_line(w)?;
183 self.statement.write(w)?;
184 self.write_new_line(w)?;
185 self.write_indent(w, depth)?;
186 self.write_close_brace(w)?;
187 w.sub_indent();
188 Ok(())
189 }
190}
191
192impl Display for WplRule {
193 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
194 write!(f, "{}", self.fmt_string().unwrap_or_default())
195 }
196}
197
198impl PartialEq for WplRule {
199 fn eq(&self, other: &Self) -> bool {
200 self.name == other.name && self.statement == other.statement
201 }
202}
203
204impl WplRule {
205 #[debug_requires(! name.is_empty(), "lang rule name is empty")]
206 pub fn new(name: String, rule: WplStatementType) -> Self {
207 WplRule {
208 name: SmolStr::from(name),
209 statement: rule,
210 }
211 }
212
213 pub fn get_name(&self) -> &SmolStr {
214 &self.name
215 }
216 pub fn path(&self, pkg_name: &str) -> String {
217 format!("{}/{}", pkg_name, self.get_name())
218 }
219}
220
221impl MergeTags for VecDeque<WplRule> {
222 fn merge_tags(&mut self, other_tags: &Option<AnnFun>) {
223 for r in self.iter_mut() {
224 match &mut r.statement {
225 WplStatementType::Express(define) => {
226 if let Some(tags) = &mut define.tags {
227 tags.merge_tags(other_tags);
228 } else {
229 define.tags = other_tags.clone()
230 }
231 }
232 }
233 }
234 }
235}
236
237#[test]
238fn test_lang_rule() {
239 let rule = WplRule {
240 statement: WplStatementType::Express(WplExpress {
241 pipe_process: vec![SmolStr::from("decode/base64"), SmolStr::from("zip")],
242 group: vec![],
243 tags: None,
244 }),
245 name: SmolStr::from("hello"),
246 };
247 assert_eq!(
248 rule.to_string(),
249 " rule hello {\n |decode/base64|zip|\n }"
250 );
251}