baobao_codegen_typescript/ast/
chain.rs1#[derive(Debug, Clone)]
5struct Call {
6 method: String,
7 args: Vec<String>,
8}
9
10#[derive(Debug, Clone)]
12pub struct MethodChain {
13 base: String,
14 base_args: Vec<String>,
15 calls: Vec<Call>,
16}
17
18impl MethodChain {
19 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 pub fn arg(mut self, arg: impl Into<String>) -> Self {
30 self.base_args.push(arg.into());
31 self
32 }
33
34 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 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 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 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 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 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 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}