Skip to main content

wp_model_core/model/data/
record.rs

1use crate::model::Maker;
2use crate::model::format::LevelFormatAble;
3use crate::model::{DataType, FNameStr, FValueStr, Value};
4use crate::traits::AsValueRef;
5use serde_derive::{Deserialize, Serialize};
6use std::convert::TryFrom;
7use std::fmt::{Display, Formatter};
8use std::net::{IpAddr, Ipv4Addr};
9
10use super::field::Field;
11pub const WP_EVENT_ID: &str = "wp_event_id";
12/// 记录中每一项需要暴露的行为
13pub trait RecordItem {
14    fn get_name(&self) -> &str;
15    fn get_meta(&self) -> &DataType;
16    fn get_value(&self) -> &Value;
17    fn get_value_mut(&mut self) -> &mut Value;
18}
19
20/// 为 Record 生成字段所需的工厂方法
21pub trait RecordItemFactory {
22    fn from_digit<S: Into<FNameStr>>(name: S, val: i64) -> Self;
23    fn from_ip<S: Into<FNameStr>>(name: S, ip: IpAddr) -> Self;
24    fn from_chars<N: Into<FNameStr>, Val: Into<FValueStr>>(name: N, val: Val) -> Self;
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
28pub struct Record<T> {
29    pub id: u64,
30    pub items: Vec<T>,
31}
32
33impl<T> Default for Record<T> {
34    fn default() -> Self {
35        Self {
36            id: 0,
37            items: Vec::with_capacity(10),
38        }
39    }
40}
41
42impl<T> From<Vec<T>> for Record<T> {
43    fn from(value: Vec<T>) -> Self {
44        Self {
45            id: 0,
46            items: value,
47        }
48    }
49}
50
51impl<T> Display for Record<T>
52where
53    T: RecordItem + LevelFormatAble,
54{
55    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56        writeln!(f)?;
57        for (i, o) in self.items.iter().enumerate() {
58            if *o.get_meta() != DataType::Ignore {
59                write!(f, "NO:{:<5}", i + 1)?;
60                o.level_fmt(f, 1)?;
61            }
62        }
63        Ok(())
64    }
65}
66
67impl<T> Record<T>
68where
69    T: RecordItem + RecordItemFactory,
70{
71    pub fn set_id(&mut self, id: u64) {
72        // 设置 id 字段
73        self.id = id;
74
75        // 如果已存在 wp_event_id 字段,避免重复追加
76        if self.items.iter().any(|f| f.get_name() == WP_EVENT_ID) {
77            return;
78        }
79        let Ok(id_i64) = i64::try_from(id) else {
80            // 事件 ID 超出 i64 无法表示,保持记录原状
81            return;
82        };
83        self.items.insert(0, T::from_digit(WP_EVENT_ID, id_i64));
84    }
85    pub fn test_value() -> Self {
86        let data = vec![
87            T::from_ip("ip", IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
88            T::from_chars("chars", "test"),
89        ];
90        Self {
91            id: 0,
92            items: data,
93        }
94    }
95}
96
97impl<T> Record<T>
98where
99    T: RecordItem,
100{
101    pub fn get_value(&self, key: &str) -> Option<&Value> {
102        self.items
103            .iter()
104            .find(|x| x.get_name() == key)
105            .map(|x| x.get_value())
106    }
107}
108
109impl<T> Record<T> {
110    pub fn append(&mut self, data: T) {
111        self.items.push(data);
112    }
113    pub fn merge(&mut self, mut other: Self) {
114        self.items.append(&mut other.items);
115    }
116}
117
118impl<T> Record<T>
119where
120    T: RecordItem,
121{
122    // 存在同名字段时取第一个字段返回值
123    pub fn field(&self, key: &str) -> Option<&T> {
124        self.items.iter().find(|item| item.get_name() == key)
125    }
126
127    pub fn get2(&self, name: &str) -> Option<&T> {
128        self.items.iter().find(|x| x.get_name() == name)
129    }
130    pub fn get_value_mut(&mut self, name: &str) -> Option<&mut T> {
131        self.items.iter_mut().find(|x| x.get_name() == name)
132    }
133    pub fn remove_field(&mut self, name: &str) -> bool {
134        let pos = self.items.iter().position(|x| x.get_name() == name);
135        if let Some(pos) = pos {
136            self.items.remove(pos);
137            true
138        } else {
139            false
140        }
141    }
142}
143
144impl<V> RecordItem for Field<V>
145where
146    V: AsValueRef<Value>,
147{
148    fn get_name(&self) -> &str {
149        Field::get_name(self)
150    }
151
152    fn get_meta(&self) -> &DataType {
153        Field::get_meta(self)
154    }
155
156    fn get_value(&self) -> &Value {
157        Field::get_value(self)
158    }
159
160    fn get_value_mut(&mut self) -> &mut Value {
161        Field::get_value_mut(self)
162    }
163}
164
165impl<V> RecordItemFactory for Field<V>
166where
167    V: Maker<i64> + Maker<FValueStr> + Maker<IpAddr>,
168{
169    fn from_digit<S: Into<FNameStr>>(name: S, val: i64) -> Self {
170        Field::from_digit(name, val)
171    }
172
173    fn from_ip<S: Into<FNameStr>>(name: S, ip: IpAddr) -> Self {
174        Field::from_ip(name, ip)
175    }
176
177    fn from_chars<N: Into<FNameStr>, Val: Into<FValueStr>>(name: N, val: Val) -> Self {
178        Field::from_chars(name, val)
179    }
180}
181
182// ValueGetter impl removed from core; use function-style adapters in extension crates.
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use crate::model::{DataField, DataRecord};
188    use std::net::Ipv4Addr;
189
190    fn make_test_record() -> DataRecord {
191        let fields = vec![
192            Field::from_chars("name", "Alice"),
193            Field::from_digit("age", 30),
194            Field::from_ip("ip", IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))),
195        ];
196        Record::from(fields)
197    }
198
199    // ========== Record creation tests ==========
200
201    #[test]
202    fn test_record_default() {
203        let record: DataRecord = Record::default();
204        assert!(record.items.is_empty());
205    }
206
207    #[test]
208    fn test_record_from_vec() {
209        let fields: Vec<DataField> = vec![Field::from_digit("x", 1), Field::from_digit("y", 2)];
210        let record: DataRecord = Record::from(fields);
211        assert_eq!(record.items.len(), 2);
212    }
213
214    #[test]
215    fn test_record_test_value() {
216        let record: DataRecord = Record::test_value();
217        assert_eq!(record.items.len(), 2);
218        assert!(record.field("ip").is_some());
219        assert!(record.field("chars").is_some());
220    }
221
222    // ========== Record field access tests ==========
223
224    #[test]
225    fn test_record_field() {
226        let record = make_test_record();
227
228        let name_field = record.field("name");
229        assert!(name_field.is_some());
230        assert_eq!(name_field.unwrap().get_name(), "name");
231
232        let missing = record.field("missing");
233        assert!(missing.is_none());
234    }
235
236    #[test]
237    fn test_record_get2() {
238        let record = make_test_record();
239
240        let age_field = record.get2("age");
241        assert!(age_field.is_some());
242        assert_eq!(age_field.unwrap().get_meta(), &DataType::Digit);
243    }
244
245    #[test]
246    fn test_record_get_value() {
247        let record = make_test_record();
248
249        let age_value = record.get_value("age");
250        assert!(age_value.is_some());
251        assert_eq!(age_value.unwrap(), &Value::Digit(30));
252
253        let missing = record.get_value("missing");
254        assert!(missing.is_none());
255    }
256
257    #[test]
258    fn test_record_get_value_mut() {
259        let mut record = make_test_record();
260
261        let field = record.get_value_mut("age");
262        assert!(field.is_some());
263
264        // Modify the value through mutable reference
265        if let Some(f) = field {
266            *f.get_value_mut() = Value::Digit(31);
267        }
268
269        assert_eq!(record.get_value("age"), Some(&Value::Digit(31)));
270    }
271
272    // ========== Record mutation tests ==========
273
274    #[test]
275    fn test_record_append() {
276        let mut record: DataRecord = Record::default();
277        assert_eq!(record.items.len(), 0);
278
279        record.append(Field::from_digit("count", 100));
280        assert_eq!(record.items.len(), 1);
281
282        record.append(Field::from_chars("msg", "hello"));
283        assert_eq!(record.items.len(), 2);
284    }
285
286    #[test]
287    fn test_record_merge() {
288        let mut record1: DataRecord = Record::from(vec![Field::from_digit("a", 1)]);
289        let record2: DataRecord =
290            Record::from(vec![Field::from_digit("b", 2), Field::from_digit("c", 3)]);
291
292        record1.merge(record2);
293        assert_eq!(record1.items.len(), 3);
294        assert!(record1.field("a").is_some());
295        assert!(record1.field("b").is_some());
296        assert!(record1.field("c").is_some());
297    }
298
299    #[test]
300    fn test_record_remove_field() {
301        let mut record = make_test_record();
302        assert_eq!(record.items.len(), 3);
303
304        let removed = record.remove_field("age");
305        assert!(removed);
306        assert_eq!(record.items.len(), 2);
307        assert!(record.field("age").is_none());
308
309        let not_found = record.remove_field("nonexistent");
310        assert!(!not_found);
311        assert_eq!(record.items.len(), 2);
312    }
313
314    // ========== set_id tests ==========
315
316    #[test]
317    fn test_record_set_id() {
318        let mut record = make_test_record();
319        let original_len = record.items.len();
320
321        record.set_id(12345);
322
323        // ID should be set in both the id field and added to items
324        assert_eq!(record.id, 12345);
325        assert_eq!(record.items.len(), original_len + 1);
326        // ID should be inserted at position 0
327        assert_eq!(record.items[0].get_name(), WP_EVENT_ID);
328        assert_eq!(record.items[0].get_value(), &Value::Digit(12345));
329    }
330
331    #[test]
332    fn test_record_set_id_no_duplicate() {
333        let mut record = make_test_record();
334
335        record.set_id(100);
336        assert_eq!(record.id, 100);
337        let len_after_first = record.items.len();
338
339        // Try to set ID again - should update id field but not add duplicate to items
340        record.set_id(200);
341        assert_eq!(record.id, 200);
342        assert_eq!(record.items.len(), len_after_first);
343        // Original ID in items should remain
344        assert_eq!(record.get_value(WP_EVENT_ID), Some(&Value::Digit(100)));
345    }
346
347    // ========== RecordItem trait tests ==========
348
349    #[test]
350    fn test_field_as_record_item() {
351        let field: DataField = Field::from_chars("key", "value");
352
353        // Test RecordItem trait methods
354        assert_eq!(field.get_name(), "key");
355        assert_eq!(field.get_meta(), &DataType::Chars);
356        assert_eq!(field.get_value(), &Value::Chars("value".into()));
357    }
358
359    #[test]
360    fn test_field_record_item_get_value_mut() {
361        let mut field: DataField = Field::from_digit("num", 10);
362
363        *field.get_value_mut() = Value::Digit(20);
364        assert_eq!(field.get_value(), &Value::Digit(20));
365    }
366
367    // ========== RecordItemFactory trait tests ==========
368
369    #[test]
370    fn test_record_item_factory() {
371        let digit: DataField = <DataField as RecordItemFactory>::from_digit("n", 42);
372        assert_eq!(digit.get_meta(), &DataType::Digit);
373
374        let ip: DataField = <DataField as RecordItemFactory>::from_ip(
375            "addr",
376            IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)),
377        );
378        assert_eq!(ip.get_meta(), &DataType::IP);
379
380        let chars: DataField = <DataField as RecordItemFactory>::from_chars("s", "hello");
381        assert_eq!(chars.get_meta(), &DataType::Chars);
382    }
383
384    // ========== Display test ==========
385
386    #[test]
387    fn test_record_display() {
388        let record = make_test_record();
389        let display = format!("{}", record);
390
391        assert!(display.contains("name"));
392        assert!(display.contains("age"));
393        assert!(display.contains("ip"));
394    }
395}