sz_configtool_lib/config_sections.rs
1//! Config Section operations
2//!
3//! Functions for managing top-level configuration sections in G2_CONFIG.
4//! These functions allow adding, removing, and querying configuration sections.
5
6use crate::error::{Result, SzConfigError};
7use serde_json::{Value, json};
8
9/// Add a new configuration section
10///
11/// # Arguments
12///
13/// * `config_json` - Configuration JSON string
14/// * `section_name` - Name of the section to add
15///
16/// # Returns
17///
18/// Returns modified configuration JSON on success
19///
20/// # Example
21///
22/// ```
23/// use sz_configtool_lib::config_sections;
24///
25/// let config = r#"{"G2_CONFIG": {}}"#;
26/// let modified = config_sections::add_config_section(config, "CFG_CUSTOM").unwrap();
27/// ```
28pub fn add_config_section(config_json: &str, section_name: &str) -> Result<String> {
29 let section_name = section_name.to_uppercase();
30 let mut config_data: Value = serde_json::from_str(config_json)?;
31
32 // Check if section already exists
33 if let Some(g2_config) = config_data.get("G2_CONFIG") {
34 if g2_config.get(§ion_name).is_some() {
35 return Err(SzConfigError::AlreadyExists(
36 "Configuration section already exists".to_string(),
37 ));
38 }
39 }
40
41 // Add new section as empty array
42 if let Some(g2_config) = config_data.get_mut("G2_CONFIG") {
43 if let Some(obj) = g2_config.as_object_mut() {
44 obj.insert(section_name.clone(), json!([]));
45 }
46 } else {
47 return Err(SzConfigError::NotFound(
48 "G2_CONFIG section not found in configuration".to_string(),
49 ));
50 }
51
52 Ok(serde_json::to_string(&config_data)?)
53}
54
55/// Remove a configuration section
56///
57/// # Arguments
58///
59/// * `config_json` - Configuration JSON string
60/// * `section_name` - Name of the section to remove
61///
62/// # Returns
63///
64/// Returns modified configuration JSON on success
65///
66/// # Example
67///
68/// ```
69/// use sz_configtool_lib::config_sections;
70///
71/// let config = r#"{"G2_CONFIG": {"CFG_CUSTOM": []}}"#;
72/// let modified = config_sections::remove_config_section(config, "CFG_CUSTOM").unwrap();
73/// ```
74pub fn remove_config_section(config_json: &str, section_name: &str) -> Result<String> {
75 let section_name = section_name.to_uppercase();
76 let mut config_data: Value = serde_json::from_str(config_json)?;
77 let mut removed = false;
78
79 if let Some(g2_config) = config_data.get_mut("G2_CONFIG") {
80 if let Some(g2_config_obj) = g2_config.as_object_mut() {
81 if g2_config_obj.remove(§ion_name).is_some() {
82 removed = true;
83 }
84 }
85 }
86
87 if !removed {
88 return Err(SzConfigError::NotFound(format!(
89 "Config section not found: {section_name}"
90 )));
91 }
92
93 Ok(serde_json::to_string(&config_data)?)
94}
95
96/// Get items from a configuration section with optional filtering
97///
98/// # Arguments
99///
100/// * `config_json` - Configuration JSON string
101/// * `section_name` - Name of the section to get
102/// * `filter` - Optional filter string to search in records
103///
104/// # Returns
105///
106/// Returns a vector of items from the section
107///
108/// # Example
109///
110/// ```
111/// use sz_configtool_lib::config_sections;
112///
113/// let config = r#"{"G2_CONFIG": {"CFG_ATTR": [{"ATTR_CODE": "NAME"}]}}"#;
114/// let items = config_sections::get_config_section(config, "CFG_ATTR", None).unwrap();
115/// assert_eq!(items.len(), 1);
116/// ```
117pub fn get_config_section(
118 config_json: &str,
119 section_name: &str,
120 filter: Option<&str>,
121) -> Result<Vec<Value>> {
122 let config_data: Value = serde_json::from_str(config_json)?;
123
124 // Check if section exists
125 let section_data = config_data
126 .get("G2_CONFIG")
127 .and_then(|g2| g2.get(section_name))
128 .ok_or_else(|| {
129 SzConfigError::NotFound(format!("Configuration section '{section_name}' not found"))
130 })?;
131
132 // Handle empty section
133 if section_data.is_null()
134 || (section_data.is_array() && section_data.as_array().unwrap().is_empty())
135 {
136 return Ok(Vec::new());
137 }
138
139 // Apply filter if provided
140 let output_data = if let Some(filter_str) = filter {
141 if let Some(array) = section_data.as_array() {
142 array
143 .iter()
144 .filter(|record| {
145 serde_json::to_string(record)
146 .unwrap_or_default()
147 .to_lowercase()
148 .contains(&filter_str.to_lowercase())
149 })
150 .cloned()
151 .collect()
152 } else {
153 // Not an array, just check the single value
154 let as_string = serde_json::to_string(section_data)
155 .unwrap_or_default()
156 .to_lowercase();
157 if as_string.contains(&filter_str.to_lowercase()) {
158 vec![section_data.clone()]
159 } else {
160 Vec::new()
161 }
162 }
163 } else {
164 // No filter - return all
165 if let Some(array) = section_data.as_array() {
166 array.clone()
167 } else {
168 vec![section_data.clone()]
169 }
170 };
171
172 Ok(output_data)
173}
174
175/// List all configuration section names
176///
177/// # Arguments
178///
179/// * `config_json` - Configuration JSON string
180///
181/// # Returns
182///
183/// Returns a vector of section names
184///
185/// # Example
186///
187/// ```
188/// use sz_configtool_lib::config_sections;
189///
190/// let config = r#"{"G2_CONFIG": {"CFG_ATTR": [], "CFG_DSRC": []}}"#;
191/// let sections = config_sections::list_config_sections(config).unwrap();
192/// assert!(sections.contains(&"CFG_ATTR".to_string()));
193/// ```
194pub fn list_config_sections(config_json: &str) -> Result<Vec<String>> {
195 let config_data: Value = serde_json::from_str(config_json)?;
196
197 let sections = if let Some(g2_config) = config_data.get("G2_CONFIG") {
198 if let Some(obj) = g2_config.as_object() {
199 obj.keys().cloned().collect()
200 } else {
201 Vec::new()
202 }
203 } else {
204 Vec::new()
205 };
206
207 Ok(sections)
208}
209
210/// Add a field to all items in a configuration section
211///
212/// # Arguments
213///
214/// * `config_json` - Configuration JSON string
215/// * `section_name` - Section name
216/// * `field_name` - Field name to add
217/// * `field_value` - Value for the field
218///
219/// # Returns
220///
221/// Returns `(modified_config, item_count)` tuple on success
222///
223/// # Example
224///
225/// ```
226/// use sz_configtool_lib::config_sections;
227/// use serde_json::json;
228///
229/// let config = r#"{"G2_CONFIG": {"CFG_ATTR": [{"ATTR_CODE": "NAME"}]}}"#;
230/// let (modified, count) = config_sections::add_config_section_field(
231/// config,
232/// "CFG_ATTR",
233/// "NEW_FIELD",
234/// &json!("default")
235/// ).unwrap();
236/// assert_eq!(count, 1);
237/// ```
238pub fn add_config_section_field(
239 config_json: &str,
240 section_name: &str,
241 field_name: &str,
242 field_value: &Value,
243) -> Result<(String, usize)> {
244 let section_name = section_name.to_uppercase();
245 let field_name = field_name.to_uppercase();
246 let mut config_data: Value = serde_json::from_str(config_json)?;
247 let mut item_count = 0;
248
249 // Navigate to section and add field to all items in the array
250 if let Some(g2_config) = config_data.get_mut("G2_CONFIG") {
251 if let Some(section_array) = g2_config
252 .get_mut(§ion_name)
253 .and_then(|v| v.as_array_mut())
254 {
255 for item in section_array.iter_mut() {
256 if let Some(item_obj) = item.as_object_mut() {
257 item_obj.insert(field_name.clone(), field_value.clone());
258 item_count += 1;
259 }
260 }
261 } else {
262 return Err(SzConfigError::NotFound(format!(
263 "Section not found or not an array: {section_name}"
264 )));
265 }
266 }
267
268 Ok((serde_json::to_string(&config_data)?, item_count))
269}
270
271/// Remove a field from all items in a configuration section
272///
273/// # Arguments
274///
275/// * `config_json` - Configuration JSON string
276/// * `section_name` - Section name
277/// * `field_name` - Field name to remove
278///
279/// # Returns
280///
281/// Returns `(modified_config, item_count)` tuple on success
282///
283/// # Example
284///
285/// ```
286/// use sz_configtool_lib::config_sections;
287///
288/// let config = r#"{"G2_CONFIG": {"CFG_ATTR": [{"ATTR_CODE": "NAME", "OLD_FIELD": "value"}]}}"#;
289/// let (modified, count) = config_sections::remove_config_section_field(
290/// config,
291/// "CFG_ATTR",
292/// "OLD_FIELD"
293/// ).unwrap();
294/// assert_eq!(count, 1);
295/// ```
296pub fn remove_config_section_field(
297 config_json: &str,
298 section_name: &str,
299 field_name: &str,
300) -> Result<(String, usize)> {
301 let section_name = section_name.to_uppercase();
302 let field_name = field_name.to_uppercase();
303 let mut config_data: Value = serde_json::from_str(config_json)?;
304 let mut item_count = 0;
305
306 // Navigate to section and remove field from all items in the array
307 if let Some(g2_config) = config_data.get_mut("G2_CONFIG") {
308 if let Some(section_array) = g2_config
309 .get_mut(§ion_name)
310 .and_then(|v| v.as_array_mut())
311 {
312 for item in section_array.iter_mut() {
313 if let Some(item_obj) = item.as_object_mut() {
314 if item_obj.remove(&field_name).is_some() {
315 item_count += 1;
316 }
317 }
318 }
319 } else {
320 return Err(SzConfigError::NotFound(format!(
321 "Section not found or not an array: {section_name}"
322 )));
323 }
324 }
325
326 Ok((serde_json::to_string(&config_data)?, item_count))
327}