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