1use std::cmp::Ordering;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Default, Serialize, Deserialize, Clone, Debug)]
6#[serde(default)]
7pub struct LogLine {
10 pub log: String,
11 pub index: String,
12 pub date: String,
13 pub timestamp: String,
14 pub app: String,
15 pub severity: String,
16 pub function: String,
17 pub payload: String,
18 pub color: Option<(u8, u8, u8)>,
19}
20
21impl LogLine {
22 pub fn columns() -> Vec<String> {
24 vec![
25 "Log".to_string(),
26 "Index".to_string(),
27 "Date".to_string(),
28 "Timestamp".to_string(),
29 "App".to_string(),
30 "Severity".to_string(),
31 "Function".to_string(),
32 "Payload".to_string(),
33 ]
34 }
35
36 pub fn get(&self, key: &str) -> Option<&String> {
38 match key {
39 "Log" => Some(&self.log),
40 "Index" => Some(&self.index),
41 "Date" => Some(&self.date),
42 "Timestamp" => Some(&self.timestamp),
43 "App" => Some(&self.app),
44 "Severity" => Some(&self.severity),
45 "Function" => Some(&self.function),
46 "Payload" => Some(&self.payload),
47 _ => None,
48 }
49 }
50
51 pub fn values(&self) -> Vec<(&str, &String)> {
53 vec![
54 ("Log", &self.log),
55 ("Date", &self.date),
56 ("Timestamp", &self.timestamp),
57 ("App", &self.app),
58 ("Severity", &self.severity),
59 ("Function", &self.function),
60 ("Payload", &self.payload),
61 ]
62 }
63
64 pub fn is_formated(&self) -> bool {
66 self.into_iter()
67 .any(|field| serde_json::from_str::<Vec<(Option<&str>, &str)>>(field).is_ok())
68 }
69
70 pub fn unformat(&self) -> Self {
72 let unformat = |field: &str| {
73 let groups = serde_json::from_str::<Vec<(Option<&str>, &str)>>(field);
74
75 match groups {
76 Ok(groups) => groups.into_iter().fold(String::new(), |acc, g| acc + g.1),
77 _ => field.to_string(),
78 }
79 };
80
81 LogLine {
82 log: unformat(&self.log),
83 index: unformat(&self.index),
84 date: unformat(&self.date),
85 timestamp: unformat(&self.timestamp),
86 app: unformat(&self.app),
87 severity: unformat(&self.severity),
88 function: unformat(&self.function),
89 payload: unformat(&self.payload),
90 color: self.color,
91 }
92 }
93}
94
95impl IntoIterator for LogLine {
96 type Item = String;
97 type IntoIter = std::array::IntoIter<String, 7>;
98
99 fn into_iter(self) -> Self::IntoIter {
100 IntoIterator::into_iter([
101 self.log,
102 self.date,
103 self.timestamp,
104 self.app,
105 self.severity,
106 self.function,
107 self.payload,
108 ])
109 }
110}
111
112impl<'a> IntoIterator for &'a LogLine {
113 type Item = &'a String;
114 type IntoIter = std::array::IntoIter<&'a String, 7>;
115
116 fn into_iter(self) -> Self::IntoIter {
117 IntoIterator::into_iter([
118 &self.log,
119 &self.date,
120 &self.timestamp,
121 &self.app,
122 &self.severity,
123 &self.function,
124 &self.payload,
125 ])
126 }
127}
128
129impl<'a> IntoIterator for &'a mut LogLine {
130 type Item = &'a String;
131 type IntoIter = std::array::IntoIter<&'a String, 7>;
132
133 fn into_iter(self) -> Self::IntoIter {
134 IntoIterator::into_iter([
135 &self.log,
136 &self.date,
137 &self.timestamp,
138 &self.app,
139 &self.severity,
140 &self.function,
141 &self.payload,
142 ])
143 }
144}
145
146impl<'a> IntoIterator for &'a &'a mut LogLine {
147 type Item = &'a String;
148 type IntoIter = std::array::IntoIter<&'a String, 7>;
149
150 fn into_iter(self) -> Self::IntoIter {
151 IntoIterator::into_iter([
152 &self.log,
153 &self.date,
154 &self.timestamp,
155 &self.app,
156 &self.severity,
157 &self.function,
158 &self.payload,
159 ])
160 }
161}
162impl<'a> IntoIterator for &'a &'a LogLine {
163 type Item = &'a String;
164 type IntoIter = std::array::IntoIter<&'a String, 7>;
165
166 fn into_iter(self) -> Self::IntoIter {
167 IntoIterator::into_iter([
168 &self.log,
169 &self.date,
170 &self.timestamp,
171 &self.app,
172 &self.severity,
173 &self.function,
174 &self.payload,
175 ])
176 }
177}
178
179impl Ord for LogLine {
180 fn cmp(&self, other: &Self) -> Ordering {
181 match (self.index.parse::<usize>(), other.index.parse::<usize>()) {
182 (Ok(index), Ok(other)) => match (index, other) {
183 (index, other) if index < other => Ordering::Less,
184 (index, other) if index == other => Ordering::Equal,
185 _ => Ordering::Greater,
186 },
187 _ => Ordering::Equal,
188 }
189 }
190}
191
192impl PartialOrd for LogLine {
193 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
194 match (self.index.parse::<usize>(), other.index.parse::<usize>()) {
195 (Ok(index), Ok(other)) => match (index, other) {
196 (index, other) if index < other => Some(Ordering::Less),
197 (index, other) if index == other => Some(Ordering::Equal),
198 _ => Some(Ordering::Greater),
199 },
200 _ => None,
201 }
202 }
203}
204
205impl PartialEq for LogLine {
206 fn eq(&self, other: &Self) -> bool {
207 self.index == other.index
208 && self.date == other.date
209 && self.timestamp == other.timestamp
210 && self.app == other.app
211 && self.severity == other.severity
212 && self.function == other.function
213 && self.payload == other.payload
214 && self.color == other.color
215 }
216}
217
218impl Eq for LogLine {}