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