baobao_codegen_typescript/ast/
chain.rs

1//! TypeScript method chain builder for fluent APIs.
2
3/// A method call in a chain.
4#[derive(Debug, Clone)]
5struct Call {
6    method: String,
7    args: Vec<String>,
8}
9
10/// Builder for method chains (fluent API patterns).
11#[derive(Debug, Clone)]
12pub struct MethodChain {
13    base: String,
14    base_args: Vec<String>,
15    calls: Vec<Call>,
16}
17
18impl MethodChain {
19    /// Create a new method chain starting with a function call.
20    pub fn new(base: impl Into<String>) -> Self {
21        Self {
22            base: base.into(),
23            base_args: Vec::new(),
24            calls: Vec::new(),
25        }
26    }
27
28    /// Add an argument to the base function call.
29    pub fn arg(mut self, arg: impl Into<String>) -> Self {
30        self.base_args.push(arg.into());
31        self
32    }
33
34    /// Add a method call to the chain.
35    pub fn call(mut self, method: impl Into<String>, arg: impl Into<String>) -> Self {
36        self.calls.push(Call {
37            method: method.into(),
38            args: vec![arg.into()],
39        });
40        self
41    }
42
43    /// Add a method call with multiple arguments.
44    pub fn call_args(mut self, method: impl Into<String>, args: Vec<String>) -> Self {
45        self.calls.push(Call {
46            method: method.into(),
47            args,
48        });
49        self
50    }
51
52    /// Add a method call with no arguments.
53    pub fn call_empty(mut self, method: impl Into<String>) -> Self {
54        self.calls.push(Call {
55            method: method.into(),
56            args: Vec::new(),
57        });
58        self
59    }
60
61    /// Conditionally add a method call.
62    pub fn call_if(
63        self,
64        condition: bool,
65        method: impl Into<String>,
66        arg: impl Into<String>,
67    ) -> Self {
68        if condition {
69            self.call(method, arg)
70        } else {
71            self
72        }
73    }
74
75    /// Conditionally add a method call using an Option.
76    pub fn call_opt(self, method: impl Into<String>, arg: Option<impl Into<String>>) -> Self {
77        match arg {
78            Some(a) => self.call(method, a),
79            None => self,
80        }
81    }
82
83    /// Build the chain as a single-line string.
84    pub fn build_inline(&self) -> String {
85        let mut result = format!("{}({})", self.base, self.base_args.join(", "));
86
87        for call in &self.calls {
88            result.push_str(&format!(".{}({})", call.method, call.args.join(", ")));
89        }
90
91        result
92    }
93
94    /// Build the chain as a multi-line string with each call on its own line.
95    pub fn build(&self) -> String {
96        let mut result = format!("{}({})", self.base, self.base_args.join(", "));
97
98        for call in &self.calls {
99            result.push_str(&format!("\n  .{}({})", call.method, call.args.join(", ")));
100        }
101
102        result
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn test_simple_chain() {
112        let chain = MethodChain::new("cli")
113            .arg("\"myapp\"")
114            .call("version", "\"1.0.0\"")
115            .build();
116        assert_eq!(chain, "cli(\"myapp\")\n  .version(\"1.0.0\")");
117    }
118
119    #[test]
120    fn test_chain_inline() {
121        let chain = MethodChain::new("builder")
122            .call("foo", "1")
123            .call("bar", "2")
124            .build_inline();
125        assert_eq!(chain, "builder().foo(1).bar(2)");
126    }
127
128    #[test]
129    fn test_chain_with_multiple_args() {
130        let chain = MethodChain::new("create")
131            .arg("\"name\"")
132            .arg("42")
133            .call("configure", "true")
134            .build();
135        assert_eq!(chain, "create(\"name\", 42)\n  .configure(true)");
136    }
137
138    #[test]
139    fn test_call_if_true() {
140        let chain = MethodChain::new("cli")
141            .arg("\"app\"")
142            .call_if(true, "description", "\"A CLI app\"")
143            .build();
144        assert_eq!(chain, "cli(\"app\")\n  .description(\"A CLI app\")");
145    }
146
147    #[test]
148    fn test_call_if_false() {
149        let chain = MethodChain::new("cli")
150            .arg("\"app\"")
151            .call_if(false, "description", "\"A CLI app\"")
152            .build();
153        assert_eq!(chain, "cli(\"app\")");
154    }
155
156    #[test]
157    fn test_call_opt_some() {
158        let desc = Some("\"A CLI app\"");
159        let chain = MethodChain::new("cli")
160            .arg("\"app\"")
161            .call_opt("description", desc)
162            .build();
163        assert_eq!(chain, "cli(\"app\")\n  .description(\"A CLI app\")");
164    }
165
166    #[test]
167    fn test_call_opt_none() {
168        let desc: Option<&str> = None;
169        let chain = MethodChain::new("cli")
170            .arg("\"app\"")
171            .call_opt("description", desc)
172            .build();
173        assert_eq!(chain, "cli(\"app\")");
174    }
175
176    #[test]
177    fn test_call_empty() {
178        let chain = MethodChain::new("builder")
179            .call_empty("build")
180            .build_inline();
181        assert_eq!(chain, "builder().build()");
182    }
183
184    #[test]
185    fn test_call_args() {
186        let chain = MethodChain::new("obj")
187            .call_args("method", vec!["a".into(), "b".into(), "c".into()])
188            .build_inline();
189        assert_eq!(chain, "obj().method(a, b, c)");
190    }
191}