forge_doc/
helpers.rs

1use itertools::Itertools;
2use solang_parser::pt::FunctionDefinition;
3use toml::{Value, value::Table};
4
5/// Generates a function signature with parameter types (e.g., "functionName(type1,type2)").
6/// Returns the function name without parameters if the function has no parameters.
7pub fn function_signature(func: &FunctionDefinition) -> String {
8    let func_name = func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.to_owned());
9    if func.params.is_empty() {
10        return func_name;
11    }
12
13    format!(
14        "{}({})",
15        func_name,
16        func.params
17            .iter()
18            .map(|p| p.1.as_ref().map(|p| p.ty.to_string()).unwrap_or_default())
19            .join(",")
20    )
21}
22
23/// Merge original toml table with the override.
24pub(crate) fn merge_toml_table(table: &mut Table, override_table: Table) {
25    for (key, override_value) in override_table {
26        match table.get_mut(&key) {
27            Some(Value::Table(inner_table)) => {
28                // Override value must be a table, otherwise discard
29                if let Value::Table(inner_override) = override_value {
30                    merge_toml_table(inner_table, inner_override);
31                }
32            }
33            Some(Value::Array(inner_array)) => {
34                // Override value must be an array, otherwise discard
35                if let Value::Array(inner_override) = override_value {
36                    for entry in inner_override {
37                        if !inner_array.contains(&entry) {
38                            inner_array.push(entry);
39                        }
40                    }
41                }
42            }
43            _ => {
44                table.insert(key, override_value);
45            }
46        };
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use solang_parser::{
54        parse,
55        pt::{ContractPart, SourceUnit, SourceUnitPart},
56    };
57
58    #[test]
59    fn test_function_signature_no_params() {
60        let (source_unit, _) = parse(
61            r#"
62            contract Test {
63                function foo() public {}
64            }
65            "#,
66            0,
67        )
68        .unwrap();
69
70        let func = extract_function(&source_unit);
71        assert_eq!(function_signature(func), "foo");
72    }
73
74    #[test]
75    fn test_function_signature_with_params() {
76        let (source_unit, _) = parse(
77            r#"
78            contract Test {
79                function transfer(address to, uint256 amount) public {}
80            }
81            "#,
82            0,
83        )
84        .unwrap();
85
86        let func = extract_function(&source_unit);
87        assert_eq!(function_signature(func), "transfer(address,uint256)");
88    }
89
90    #[test]
91    fn test_function_signature_constructor() {
92        let (source_unit, _) = parse(
93            r#"
94            contract Test {
95                constructor(address owner) {}
96            }
97            "#,
98            0,
99        )
100        .unwrap();
101
102        let func = extract_function(&source_unit);
103        assert_eq!(function_signature(func), "constructor(address)");
104    }
105
106    /// Helper to extract the first function from a parsed source unit
107    fn extract_function(source_unit: &SourceUnit) -> &FunctionDefinition {
108        for part in &source_unit.0 {
109            if let SourceUnitPart::ContractDefinition(contract) = part {
110                for part in &contract.parts {
111                    if let ContractPart::FunctionDefinition(func) = part {
112                        return func;
113                    }
114                }
115            }
116        }
117        panic!("No function found in source unit");
118    }
119}