Skip to main content

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(&section_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(&section_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(&section_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(&section_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}