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