textfsm_core/parser/
state.rs1use std::collections::HashMap;
4
5
6use crate::template::ValueDef;
7use crate::types::{ListItem, Value, ValueOption};
8
9#[derive(Debug, Clone)]
11pub struct ValueState {
12 pub def: ValueDef,
14
15 pub index: usize,
17
18 current: Value,
20
21 filldown_cache: Option<Value>,
23}
24
25impl ValueState {
26 pub fn new(def: ValueDef, index: usize) -> Self {
28 let initial = if def.has_option(ValueOption::List) {
29 Value::List(Vec::new())
30 } else {
31 Value::Empty
32 };
33
34 Self {
35 def,
36 index,
37 current: initial,
38 filldown_cache: None,
39 }
40 }
41
42 pub fn assign(&mut self, value: String, all_results: &mut [Vec<Value>]) {
44 if self.def.has_option(ValueOption::List) {
45 self.assign_list(value.clone());
46 } else {
47 self.current = Value::Single(value.clone());
48 }
49
50 if self.def.has_option(ValueOption::Filldown) {
52 self.filldown_cache = Some(self.current.clone());
53 }
54
55 if self.def.has_option(ValueOption::Fillup) && !value.is_empty() {
57 self.backfill(&value, all_results);
58 }
59 }
60
61 fn assign_list(&mut self, value: String) {
62 let item = if let Some(ref regex) = self.def.compiled_regex {
63 if let Ok(Some(caps)) = regex.captures(&value) {
65 let dict: HashMap<String, String> = regex
66 .capture_names()
67 .flatten()
68 .filter_map(|name| {
69 caps.name(name)
70 .map(|m| (name.to_string(), m.as_str().to_string()))
71 })
72 .collect();
73
74 if !dict.is_empty() {
75 ListItem::Dict(dict)
76 } else {
77 ListItem::String(value)
78 }
79 } else {
80 ListItem::String(value)
81 }
82 } else {
83 ListItem::String(value)
84 };
85
86 if let Value::List(ref mut list) = self.current {
87 list.push(item);
88 }
89 }
90
91 fn backfill(&self, value: &str, results: &mut [Vec<Value>]) {
92 for record in results.iter_mut().rev() {
94 if self.index < record.len() {
95 if record[self.index].is_empty() {
96 record[self.index] = Value::Single(value.to_string());
97 } else {
98 break;
100 }
101 }
102 }
103 }
104
105 pub fn clear(&mut self) {
107 if self.def.has_option(ValueOption::Filldown) {
108 if let Some(ref cached) = self.filldown_cache {
110 self.current = cached.clone();
111 return;
112 }
113 }
114
115 if self.def.has_option(ValueOption::List) && !self.def.has_option(ValueOption::Filldown) {
117 self.current = Value::List(Vec::new());
118 } else if !self.def.has_option(ValueOption::Filldown) {
119 self.current = Value::Empty;
120 }
121 }
122
123 pub fn clear_all(&mut self) {
125 self.filldown_cache = None;
126 self.current = if self.def.has_option(ValueOption::List) {
127 Value::List(Vec::new())
128 } else {
129 Value::Empty
130 };
131 }
132
133 pub fn satisfies_required(&self) -> bool {
135 if !self.def.has_option(ValueOption::Required) {
136 return true;
137 }
138 !self.current.is_empty()
139 }
140
141 pub fn take_for_record(&mut self) -> Value {
143 if self.def.has_option(ValueOption::List) {
144 self.current.clone()
147 } else {
148 std::mem::take(&mut self.current)
149 }
150 }
151
152 pub fn current(&self) -> &Value {
154 &self.current
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 fn make_def(name: &str, options: &[ValueOption]) -> ValueDef {
162 ValueDef {
163 name: name.to_string(),
164 pattern: "(\\S+)".to_string(),
165 options: options.iter().cloned().collect(),
166 template_pattern: format!("(?P<{}>\\S+)", name),
167 compiled_regex: None,
168 }
169 }
170
171 #[test]
172 fn test_simple_assign() {
173 let def = make_def("Test", &[]);
174 let mut state = ValueState::new(def, 0);
175 let mut results = Vec::new();
176
177 state.assign("hello".to_string(), &mut results);
178 assert_eq!(state.current(), &Value::Single("hello".into()));
179 }
180
181 #[test]
182 fn test_filldown() {
183 let def = make_def("Test", &[ValueOption::Filldown]);
184 let mut state = ValueState::new(def, 0);
185 let mut results = Vec::new();
186
187 state.assign("cached".to_string(), &mut results);
188 assert_eq!(state.current(), &Value::Single("cached".into()));
189
190 state.clear();
191 assert_eq!(state.current(), &Value::Single("cached".into()));
193
194 state.clear_all();
195 assert_eq!(state.current(), &Value::Empty);
197 }
198
199 #[test]
200 fn test_list() {
201 let def = make_def("Items", &[ValueOption::List]);
202 let mut state = ValueState::new(def, 0);
203 let mut results = Vec::new();
204
205 state.assign("one".to_string(), &mut results);
206 state.assign("two".to_string(), &mut results);
207 state.assign("three".to_string(), &mut results);
208
209 match state.current() {
210 Value::List(items) => {
211 assert_eq!(items.len(), 3);
212 }
213 _ => panic!("Expected List value"),
214 }
215 }
216
217 #[test]
218 fn test_required() {
219 let def = make_def("Required", &[ValueOption::Required]);
220 let mut state = ValueState::new(def, 0);
221
222 assert!(!state.satisfies_required()); let mut results = Vec::new();
225 state.assign("value".to_string(), &mut results);
226 assert!(state.satisfies_required()); }
228}