zombienet_configuration/shared/
helpers.rs

1use std::{cell::RefCell, collections::HashSet, rc::Rc};
2
3use support::constants::{BORROWABLE, THIS_IS_A_BUG};
4use tracing::warn;
5
6use super::{
7    errors::ValidationError,
8    types::{ParaId, Port, ValidationContext},
9};
10
11pub fn merge_errors(errors: Vec<anyhow::Error>, new_error: anyhow::Error) -> Vec<anyhow::Error> {
12    let mut errors = errors;
13    errors.push(new_error);
14
15    errors
16}
17
18pub fn merge_errors_vecs(
19    errors: Vec<anyhow::Error>,
20    new_errors: Vec<anyhow::Error>,
21) -> Vec<anyhow::Error> {
22    let mut errors = errors;
23
24    for new_error in new_errors.into_iter() {
25        errors.push(new_error);
26    }
27
28    errors
29}
30
31/// Generates a unique name from a base name and the names already present in a
32/// [`ValidationContext`].
33///
34/// Uses [`generate_unique_node_name_from_names()`] internally to ensure uniqueness.
35/// Logs a warning if the generated name differs from the original due to duplicates.
36pub fn generate_unique_node_name(
37    node_name: impl Into<String>,
38    validation_context: Rc<RefCell<ValidationContext>>,
39) -> String {
40    let mut context = validation_context
41        .try_borrow_mut()
42        .expect(&format!("{BORROWABLE}, {THIS_IS_A_BUG}"));
43
44    generate_unique_node_name_from_names(node_name, &mut context.used_nodes_names)
45}
46
47/// Returns `node_name` if it is not already in `names`.
48///
49/// Otherwise, appends an incrementing `-{counter}` suffix until a unique name is found,
50/// then returns it. Logs a warning when a duplicate is detected.
51pub fn generate_unique_node_name_from_names(
52    node_name: impl Into<String>,
53    names: &mut HashSet<String>,
54) -> String {
55    let node_name = node_name.into();
56
57    if names.insert(node_name.clone()) {
58        return node_name;
59    }
60
61    let mut counter = 1;
62    let mut candidate = node_name.clone();
63    while names.contains(&candidate) {
64        candidate = format!("{node_name}-{counter}");
65        counter += 1;
66    }
67
68    warn!(
69        original = %node_name,
70        adjusted = %candidate,
71        "Duplicate node name detected."
72    );
73
74    names.insert(candidate.clone());
75    candidate
76}
77
78pub fn ensure_value_is_not_empty(value: &str) -> Result<(), anyhow::Error> {
79    if value.is_empty() {
80        Err(ValidationError::CantBeEmpty().into())
81    } else {
82        Ok(())
83    }
84}
85
86pub fn ensure_port_unique(
87    port: Port,
88    validation_context: Rc<RefCell<ValidationContext>>,
89) -> Result<(), anyhow::Error> {
90    let mut context = validation_context
91        .try_borrow_mut()
92        .expect(&format!("{BORROWABLE}, {THIS_IS_A_BUG}"));
93
94    if !context.used_ports.contains(&port) {
95        context.used_ports.push(port);
96        return Ok(());
97    }
98
99    Err(ValidationError::PortAlreadyUsed(port).into())
100}
101
102pub fn generate_unique_para_id(
103    para_id: ParaId,
104    validation_context: Rc<RefCell<ValidationContext>>,
105) -> String {
106    let mut context = validation_context
107        .try_borrow_mut()
108        .expect(&format!("{BORROWABLE}, {THIS_IS_A_BUG}"));
109
110    if let Some(suffix) = context.used_para_ids.get_mut(&para_id) {
111        *suffix += 1;
112        format!("{para_id}-{suffix}")
113    } else {
114        // insert 0, since will be used next time.
115        context.used_para_ids.insert(para_id, 0);
116        para_id.to_string()
117    }
118}