1use std::collections::HashMap;
2use std::fmt::{Debug, Display, Formatter};
3use std::net::Ipv4Addr;
4
5use orion_error::OperationContext;
6use orion_error::conversion::{ErrorWith, SourceErr};
7use wp_model_core::model::FNameStr;
8
9use crate::parser::error::{WplCodeReason, WplCodeResult};
10
11#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)]
12pub struct FieldsGenRule {
13 pub items: NamedFieldGF,
14}
15
16impl FieldsGenRule {
17 pub fn load(path: &str) -> WplCodeResult<Self> {
18 let mut ctx = OperationContext::doing("load gen rule");
19 ctx.record("path", path);
20
21 let content = std::fs::read_to_string(path)
22 .source_err(WplCodeReason::core_conf(), "read gen rule file")
23 .with_context(&ctx)?;
24 let res: Self = toml::from_str(&content)
25 .source_err(WplCodeReason::core_conf(), "parse gen rule toml")
26 .with_context(&ctx)?;
27 Ok(res)
28 }
29 pub fn new() -> Self {
30 Self {
31 items: NamedFieldGF::default(),
32 }
33 }
34 pub fn add(&mut self, key: &str, value: FieldGenConf) {
35 self.items.insert(key.into(), value);
36 }
37}
38
39impl Default for FieldsGenRule {
40 fn default() -> Self {
41 Self::new()
42 }
43}
44
45#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
46pub struct FieldGenConf {
47 pub scope: Option<GenScopeEnum>,
48 pub gen_type: String,
49 pub gen_fmt: Option<String>,
50}
51
52impl Default for FieldGenConf {
53 fn default() -> Self {
54 Self {
55 scope: Some(GenScopeEnum::Digit(GenScope { beg: 0, end: 100 })),
56 gen_type: "digit".to_string(),
57 gen_fmt: Some("SN-{val}".to_string()),
58 }
59 }
60}
61
62impl FieldGenConf {
63 pub fn digit_of(beg: i64, end: i64) -> Self {
66 Self {
67 scope: Some(GenScopeEnum::Digit(GenScope { beg, end })),
68 gen_type: "digit".to_string(),
69 gen_fmt: None,
70 }
71 }
72
73 pub fn float_of(beg: f64, end: f64) -> Self {
75 Self {
76 scope: Some(GenScopeEnum::Float(GenScope { beg, end })),
77 gen_type: "float".to_string(),
78 gen_fmt: None,
79 }
80 }
81
82 pub fn ip_of(beg: Ipv4Addr, end: Ipv4Addr) -> Self {
84 Self {
85 scope: Some(GenScopeEnum::Ip(GenScope { beg, end })),
86 gen_type: "ip".to_string(),
87 gen_fmt: None,
88 }
89 }
90
91 pub fn chars_from_list(values: Vec<String>) -> Self {
93 Self {
94 scope: Some(GenScopeEnum::Chars(values)),
95 gen_type: "chars".to_string(),
96 gen_fmt: None,
97 }
98 }
99
100 pub fn chars_from(values: &[&str]) -> Self {
102 Self::chars_from_list(values.iter().map(|s| s.to_string()).collect())
103 }
104
105 pub fn with_fmt(mut self, fmt: impl Into<String>) -> Self {
107 self.gen_fmt = Some(fmt.into());
108 self
109 }
110 pub fn example_1() -> Self {
111 Self {
112 scope: Some(GenScopeEnum::Digit(GenScope { beg: 100, end: 200 })),
113 gen_type: "digit".to_string(),
114 gen_fmt: None,
115 }
116 }
117 pub fn example_2() -> Self {
118 Self {
119 scope: Some(GenScopeEnum::Digit(GenScope { beg: 0, end: 100 })),
120 gen_type: "chars".to_string(),
121 gen_fmt: Some("SN-{val}".to_string()),
122 }
123 }
124 pub fn example_3() -> Self {
125 Self {
126 scope: Some(GenScopeEnum::Ip(GenScope {
127 beg: Ipv4Addr::new(10, 0, 10, 0),
128 end: Ipv4Addr::new(10, 0, 100, 255),
129 })),
130 gen_type: "ip".to_string(),
131 gen_fmt: None,
132 }
133 }
134}
135
136impl Display for FieldGenConf {
137 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
138 write!(f, "{}", self.gen_type)?;
139 if let Some(scope) = &self.scope {
140 write!(f, ":rand{}", scope)?;
141 }
142 if let Some(fmt) = &self.gen_fmt {
143 write!(f, " > {}", fmt)?;
144 }
145 Ok(())
146 }
147}
148
149#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
150pub struct GenScope<T>
151where
152 T: Clone + Debug + PartialEq + Display,
153{
154 pub beg: T,
155 pub end: T,
156}
157
158impl<T> Display for GenScope<T>
159where
160 T: Clone + Debug + PartialEq + Display,
161{
162 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
163 write!(f, "({},{})", self.beg, self.end)
164 }
165}
166
167impl Display for GenScopeEnum {
168 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
169 match self {
170 GenScopeEnum::Ip(scope) => write!(f, "{}", scope),
171 GenScopeEnum::Digit(scope) => write!(f, "{}", scope),
172 GenScopeEnum::Float(scope) => write!(f, "{}", scope),
173 GenScopeEnum::Chars(scope) => write!(f, "{:?}", scope),
174 }
175 }
176}
177
178pub type NamedFieldGF = HashMap<FNameStr, FieldGenConf>;
179
180#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
181pub enum GenScopeEnum {
182 #[serde(rename = "ip")]
183 Ip(GenScope<Ipv4Addr>),
184 #[serde(rename = "digit")]
185 Digit(GenScope<i64>),
186 #[serde(rename = "float")]
187 Float(GenScope<f64>),
188 #[serde(rename = "chars")]
189 Chars(Vec<String>),
190}
191
192#[derive(Default, Debug, Clone)]
194pub struct FieldGenBuilder {
195 items: NamedFieldGF,
196}
197
198impl FieldGenBuilder {
199 pub fn new() -> Self {
201 Self {
202 items: NamedFieldGF::default(),
203 }
204 }
205
206 pub fn insert_conf(mut self, name: impl Into<FNameStr>, conf: FieldGenConf) -> Self {
208 self.items.insert(name.into(), conf);
209 self
210 }
211
212 pub fn digit(self, name: impl Into<FNameStr>, beg: i64, end: i64) -> Self {
214 self.insert_conf(name, FieldGenConf::digit_of(beg, end))
215 }
216
217 pub fn float(self, name: impl Into<FNameStr>, beg: f64, end: f64) -> Self {
219 self.insert_conf(name, FieldGenConf::float_of(beg, end))
220 }
221
222 pub fn ip(self, name: impl Into<FNameStr>, beg: Ipv4Addr, end: Ipv4Addr) -> Self {
224 self.insert_conf(name, FieldGenConf::ip_of(beg, end))
225 }
226
227 pub fn chars(self, name: impl Into<FNameStr>) -> Self {
229 self.insert_conf(
230 name,
231 FieldGenConf {
232 scope: None,
233 gen_type: "chars".to_string(),
234 gen_fmt: None,
235 },
236 )
237 }
238
239 pub fn chars_from(self, name: impl Into<FNameStr>, values: &[&str]) -> Self {
241 self.insert_conf(name, FieldGenConf::chars_from(values))
242 }
243
244 pub fn with_fmt(mut self, name: &str, fmt: impl Into<String>) -> Self {
246 if let Some(conf) = self.items.get_mut(name) {
247 conf.gen_fmt = Some(fmt.into());
248 }
249 self
250 }
251
252 pub fn build(self) -> NamedFieldGF {
254 self.items
255 }
256}
257
258impl From<FieldGenBuilder> for NamedFieldGF {
259 fn from(b: FieldGenBuilder) -> Self {
260 b.items
261 }
262}