1use std::fmt;
34
35#[derive(Debug, Clone, PartialEq)]
40pub enum Value {
41 Bool(bool),
43 Int(i64),
45 UInt(u64),
47 Float(f64),
49 String(String),
51 Ident(String),
53 Duration {
55 millis: u64,
57 },
58 EnumVariant {
60 path: String,
62 variant: String,
64 },
65 EnvVar {
69 name: String,
71 by_ref: bool,
73 },
74 Try(Box<Value>),
78 Builder(Box<BuilderSpec>),
80 Block(Box<Block>),
82}
83
84impl Value {
85 pub fn bool(v: bool) -> Self {
87 Self::Bool(v)
88 }
89
90 pub fn int(v: i64) -> Self {
92 Self::Int(v)
93 }
94
95 pub fn uint(v: u64) -> Self {
97 Self::UInt(v)
98 }
99
100 pub fn float(v: f64) -> Self {
102 Self::Float(v)
103 }
104
105 pub fn string(v: impl Into<String>) -> Self {
107 Self::String(v.into())
108 }
109
110 pub fn ident(v: impl Into<String>) -> Self {
112 Self::Ident(v.into())
113 }
114
115 pub fn duration_secs(secs: u64) -> Self {
117 Self::Duration {
118 millis: secs * 1000,
119 }
120 }
121
122 pub fn duration_millis(millis: u64) -> Self {
124 Self::Duration { millis }
125 }
126
127 pub fn enum_variant(path: impl Into<String>, variant: impl Into<String>) -> Self {
129 Self::EnumVariant {
130 path: path.into(),
131 variant: variant.into(),
132 }
133 }
134
135 pub fn builder(spec: BuilderSpec) -> Self {
137 Self::Builder(Box::new(spec))
138 }
139
140 pub fn block(block: Block) -> Self {
142 Self::Block(Box::new(block))
143 }
144
145 pub fn env_var(name: impl Into<String>) -> Self {
150 Self::EnvVar {
151 name: name.into(),
152 by_ref: true,
153 }
154 }
155
156 pub fn env_var_owned(name: impl Into<String>) -> Self {
161 Self::EnvVar {
162 name: name.into(),
163 by_ref: false,
164 }
165 }
166
167 pub fn try_(value: Value) -> Self {
171 Self::Try(Box::new(value))
172 }
173
174 pub fn render(&self, renderer: &dyn Renderer) -> String {
176 renderer.render_value(self, &RenderOptions::default())
177 }
178
179 pub fn render_inline(&self, renderer: &dyn Renderer) -> String {
181 renderer.render_value(self, &RenderOptions::inline())
182 }
183
184 pub fn render_with(&self, renderer: &dyn Renderer, opts: &RenderOptions) -> String {
186 renderer.render_value(self, opts)
187 }
188}
189
190#[derive(Debug, Clone, PartialEq)]
192pub enum Constructor {
193 StaticNew {
195 type_path: String,
197 },
198 StaticMethod {
200 type_path: String,
202 method: String,
204 args: Vec<Value>,
206 },
207 ClassNew {
209 type_name: String,
211 },
212 Factory {
214 name: String,
216 },
217}
218
219impl Constructor {
220 pub fn static_new(type_path: impl Into<String>) -> Self {
222 Self::StaticNew {
223 type_path: type_path.into(),
224 }
225 }
226
227 pub fn static_method(
239 type_path: impl Into<String>,
240 method: impl Into<String>,
241 args: Vec<Value>,
242 ) -> Self {
243 Self::StaticMethod {
244 type_path: type_path.into(),
245 method: method.into(),
246 args,
247 }
248 }
249
250 pub fn class_new(type_name: impl Into<String>) -> Self {
252 Self::ClassNew {
253 type_name: type_name.into(),
254 }
255 }
256
257 pub fn factory(name: impl Into<String>) -> Self {
259 Self::Factory { name: name.into() }
260 }
261}
262
263#[derive(Debug, Clone, PartialEq)]
265pub struct MethodCall {
266 pub name: String,
268 pub args: Vec<Value>,
270}
271
272impl MethodCall {
273 pub fn new(name: impl Into<String>) -> Self {
275 Self {
276 name: name.into(),
277 args: Vec::new(),
278 }
279 }
280
281 pub fn arg(mut self, value: Value) -> Self {
283 self.args.push(value);
284 self
285 }
286
287 pub fn args(mut self, values: impl IntoIterator<Item = Value>) -> Self {
289 self.args.extend(values);
290 self
291 }
292}
293
294#[derive(Debug, Clone, PartialEq, Default)]
296pub struct Terminal {
297 pub is_async: bool,
299 pub is_try: bool,
301 pub method: Option<String>,
303}
304
305impl Terminal {
306 pub fn new() -> Self {
308 Self::default()
309 }
310
311 pub fn async_(mut self) -> Self {
313 self.is_async = true;
314 self
315 }
316
317 pub fn try_(mut self) -> Self {
319 self.is_try = true;
320 self
321 }
322
323 pub fn method(mut self, name: impl Into<String>) -> Self {
325 self.method = Some(name.into());
326 self
327 }
328}
329
330#[derive(Debug, Clone, PartialEq)]
335pub struct BuilderSpec {
336 pub constructor: Constructor,
338 pub calls: Vec<MethodCall>,
340 pub terminal: Terminal,
342}
343
344impl BuilderSpec {
345 pub fn new(type_path: impl Into<String>) -> Self {
347 Self {
348 constructor: Constructor::static_new(type_path),
349 calls: Vec::new(),
350 terminal: Terminal::default(),
351 }
352 }
353
354 pub fn with_constructor(constructor: Constructor) -> Self {
356 Self {
357 constructor,
358 calls: Vec::new(),
359 terminal: Terminal::default(),
360 }
361 }
362
363 pub fn call(mut self, name: impl Into<String>) -> Self {
365 self.calls.push(MethodCall::new(name));
366 self
367 }
368
369 pub fn call_arg(mut self, name: impl Into<String>, value: Value) -> Self {
371 self.calls.push(MethodCall::new(name).arg(value));
372 self
373 }
374
375 pub fn call_args(
377 mut self,
378 name: impl Into<String>,
379 values: impl IntoIterator<Item = Value>,
380 ) -> Self {
381 self.calls.push(MethodCall::new(name).args(values));
382 self
383 }
384
385 pub fn call_if(self, condition: bool, name: impl Into<String>) -> Self {
387 if condition { self.call(name) } else { self }
388 }
389
390 pub fn call_arg_if(self, condition: bool, name: impl Into<String>, value: Value) -> Self {
392 if condition {
393 self.call_arg(name, value)
394 } else {
395 self
396 }
397 }
398
399 pub fn call_opt(self, name: impl Into<String>, value: Option<Value>) -> Self {
401 match value {
402 Some(v) => self.call_arg(name, v),
403 None => self,
404 }
405 }
406
407 pub fn async_(mut self) -> Self {
409 self.terminal.is_async = true;
410 self
411 }
412
413 pub fn try_(mut self) -> Self {
415 self.terminal.is_try = true;
416 self
417 }
418
419 pub fn terminal_method(mut self, name: impl Into<String>) -> Self {
421 self.terminal.method = Some(name.into());
422 self
423 }
424
425 pub fn has_calls(&self) -> bool {
427 !self.calls.is_empty()
428 }
429
430 pub fn apply_config<I, S>(mut self, config: I) -> Self
440 where
441 I: IntoIterator<Item = (S, Option<Value>)>,
442 S: Into<String>,
443 {
444 for (name, value) in config {
445 if let Some(v) = value {
446 self.calls.push(MethodCall::new(name).arg(v));
447 }
448 }
449 self
450 }
451
452 pub fn render(&self, renderer: &dyn Renderer) -> String {
454 renderer.render_builder(self, &RenderOptions::default())
455 }
456
457 pub fn render_inline(&self, renderer: &dyn Renderer) -> String {
459 renderer.render_builder(self, &RenderOptions::inline())
460 }
461
462 pub fn render_with(&self, renderer: &dyn Renderer, opts: &RenderOptions) -> String {
464 renderer.render_builder(self, opts)
465 }
466}
467
468#[derive(Debug, Clone, PartialEq)]
470pub struct Binding {
471 pub name: String,
473 pub value: Value,
475 pub mutable: bool,
477}
478
479impl Binding {
480 pub fn new(name: impl Into<String>, value: Value) -> Self {
482 Self {
483 name: name.into(),
484 value,
485 mutable: false,
486 }
487 }
488
489 pub fn new_mut(name: impl Into<String>, value: Value) -> Self {
491 Self {
492 name: name.into(),
493 value,
494 mutable: true,
495 }
496 }
497}
498
499#[derive(Debug, Clone, PartialEq)]
504pub struct Block {
505 pub bindings: Vec<Binding>,
507 pub body: Value,
509}
510
511impl Block {
512 pub fn new(body: Value) -> Self {
514 Self {
515 bindings: Vec::new(),
516 body,
517 }
518 }
519
520 pub fn binding(mut self, name: impl Into<String>, value: Value) -> Self {
522 self.bindings.push(Binding::new(name, value));
523 self
524 }
525
526 pub fn binding_mut(mut self, name: impl Into<String>, value: Value) -> Self {
528 self.bindings.push(Binding::new_mut(name, value));
529 self
530 }
531
532 pub fn render(&self, renderer: &dyn Renderer) -> String {
534 renderer.render_block(self, &RenderOptions::default())
535 }
536
537 pub fn render_inline(&self, renderer: &dyn Renderer) -> String {
539 renderer.render_block(self, &RenderOptions::inline())
540 }
541
542 pub fn render_with(&self, renderer: &dyn Renderer, opts: &RenderOptions) -> String {
544 renderer.render_block(self, opts)
545 }
546}
547
548#[derive(Debug, Clone)]
550pub struct RenderOptions {
551 pub indent: usize,
553 pub indent_size: usize,
555 pub inline: bool,
557}
558
559impl Default for RenderOptions {
560 fn default() -> Self {
561 Self {
562 indent: 0,
563 indent_size: 4,
564 inline: false,
565 }
566 }
567}
568
569impl RenderOptions {
570 pub fn inline() -> Self {
572 Self {
573 inline: true,
574 ..Default::default()
575 }
576 }
577
578 pub fn with_indent(mut self, indent: usize) -> Self {
580 self.indent = indent;
581 self
582 }
583
584 pub fn with_indent_size(mut self, size: usize) -> Self {
586 self.indent_size = size;
587 self
588 }
589
590 pub fn indent_str(&self) -> String {
592 " ".repeat(self.indent * self.indent_size)
593 }
594
595 pub fn nested(&self) -> Self {
597 Self {
598 indent: self.indent + 1,
599 ..*self
600 }
601 }
602}
603
604pub trait Renderer {
608 fn render_value(&self, value: &Value, opts: &RenderOptions) -> String;
610
611 fn render_builder(&self, spec: &BuilderSpec, opts: &RenderOptions) -> String;
613
614 fn render_block(&self, block: &Block, opts: &RenderOptions) -> String;
616
617 fn transform_method_name(&self, name: &str) -> String {
621 name.to_string()
622 }
623
624 fn render_constructor(&self, ctor: &Constructor) -> String;
626
627 fn render_terminal(&self, terminal: &Terminal) -> String;
629}
630
631impl fmt::Display for Value {
632 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
633 match self {
635 Value::Bool(v) => write!(f, "{}", v),
636 Value::Int(v) => write!(f, "{}", v),
637 Value::UInt(v) => write!(f, "{}", v),
638 Value::Float(v) => write!(f, "{}", v),
639 Value::String(v) => write!(f, "\"{}\"", v),
640 Value::Ident(v) => write!(f, "{}", v),
641 Value::Duration { millis } => write!(f, "{}ms", millis),
642 Value::EnumVariant { path, variant } => write!(f, "{}::{}", path, variant),
643 Value::EnvVar { name, .. } => write!(f, "env({})", name),
644 Value::Try(inner) => write!(f, "try({})", inner),
645 Value::Builder(_) => write!(f, "<builder>"),
646 Value::Block(_) => write!(f, "<block>"),
647 }
648 }
649}
650
651#[cfg(test)]
652mod tests {
653 use super::*;
654
655 #[test]
656 fn test_value_constructors() {
657 assert_eq!(Value::bool(true), Value::Bool(true));
658 assert_eq!(Value::int(42), Value::Int(42));
659 assert_eq!(Value::uint(100), Value::UInt(100));
660 assert_eq!(Value::string("hello"), Value::String("hello".into()));
661 assert_eq!(Value::ident("foo"), Value::Ident("foo".into()));
662 assert_eq!(Value::duration_secs(5), Value::Duration { millis: 5000 });
663 assert_eq!(Value::duration_millis(100), Value::Duration { millis: 100 });
664 }
665
666 #[test]
667 fn test_builder_spec_basic() {
668 let spec = BuilderSpec::new("PoolOptions")
669 .call_arg("max_connections", Value::uint(10))
670 .call_arg("min_connections", Value::uint(5));
671
672 assert_eq!(spec.calls.len(), 2);
673 assert_eq!(spec.calls[0].name, "max_connections");
674 assert_eq!(spec.calls[1].name, "min_connections");
675 }
676
677 #[test]
678 fn test_builder_spec_conditional() {
679 let spec = BuilderSpec::new("Options")
680 .call_opt("present", Some(Value::bool(true)))
681 .call_opt("missing", None)
682 .call_if(true, "enabled")
683 .call_if(false, "disabled");
684
685 assert_eq!(spec.calls.len(), 2);
686 assert_eq!(spec.calls[0].name, "present");
687 assert_eq!(spec.calls[1].name, "enabled");
688 }
689
690 #[test]
691 fn test_builder_spec_apply_config() {
692 let max: Option<u64> = Some(10);
693 let min: Option<u64> = None;
694 let timeout: Option<u64> = Some(30);
695
696 let spec = BuilderSpec::new("PoolOptions").apply_config([
697 ("max_connections", max.map(Value::uint)),
698 ("min_connections", min.map(Value::uint)),
699 ("timeout", timeout.map(Value::duration_secs)),
700 ]);
701
702 assert_eq!(spec.calls.len(), 2);
703 assert_eq!(spec.calls[0].name, "max_connections");
704 assert_eq!(spec.calls[1].name, "timeout");
705 }
706
707 #[test]
708 fn test_block_with_bindings() {
709 let block = Block::new(Value::ident("pool"))
710 .binding("options", Value::builder(BuilderSpec::new("SqliteOptions")))
711 .binding_mut("counter", Value::int(0));
712
713 assert_eq!(block.bindings.len(), 2);
714 assert!(!block.bindings[0].mutable);
715 assert!(block.bindings[1].mutable);
716 }
717
718 #[test]
719 fn test_constructor_variants() {
720 let static_new = Constructor::static_new("sqlx::pool::PoolOptions");
721 let static_method = Constructor::static_method(
722 "sqlx::sqlite::SqliteConnectOptions",
723 "from_str",
724 vec![Value::env_var("DATABASE_URL")],
725 );
726 let class_new = Constructor::class_new("PoolOptions");
727 let factory = Constructor::factory("NewPoolOptions");
728
729 assert!(matches!(static_new, Constructor::StaticNew { .. }));
730 assert!(matches!(static_method, Constructor::StaticMethod { .. }));
731 assert!(matches!(class_new, Constructor::ClassNew { .. }));
732 assert!(matches!(factory, Constructor::Factory { .. }));
733 }
734
735 #[test]
736 fn test_terminal_operations() {
737 let terminal = Terminal::new().async_().try_().method("build");
738
739 assert!(terminal.is_async);
740 assert!(terminal.is_try);
741 assert_eq!(terminal.method, Some("build".into()));
742 }
743
744 #[test]
745 fn test_render_options() {
746 let opts = RenderOptions::default().with_indent(2).with_indent_size(2);
747 assert_eq!(opts.indent_str(), " ");
748
749 let nested = opts.nested();
750 assert_eq!(nested.indent_str(), " ");
751 }
752}