1use crate::args::Args;
4use crate::flavor::Flavor;
5use crate::modifiers::{Arg, Builder, escape, named};
6
7#[derive(Debug, Clone)]
8struct CompiledBuilder {
9 args: Args,
10 format: String,
11}
12
13impl CompiledBuilder {
14 fn new(args: Args, format: String) -> Self {
15 Self { args, format }
16 }
17}
18
19impl Builder for CompiledBuilder {
20 fn build_with_flavor(&self, flavor: Flavor, initial_arg: &[Arg]) -> (String, Vec<Arg>) {
21 self.args
22 .compile_with_flavor(&self.format, flavor, initial_arg)
23 }
24
25 fn flavor(&self) -> Flavor {
26 self.args.flavor
27 }
28}
29
30#[derive(Clone)]
31struct FlavoredBuilder {
32 inner: Box<dyn Builder>,
33 flavor: Flavor,
34}
35
36impl Builder for FlavoredBuilder {
37 fn build_with_flavor(&self, flavor: Flavor, initial_arg: &[Arg]) -> (String, Vec<Arg>) {
38 self.inner.build_with_flavor(flavor, initial_arg)
39 }
40
41 fn flavor(&self) -> Flavor {
42 self.flavor
43 }
44}
45
46pub fn with_flavor(builder: impl Builder + 'static, flavor: Flavor) -> Box<dyn Builder> {
48 Box::new(FlavoredBuilder {
49 inner: Box::new(builder),
50 flavor,
51 })
52}
53
54pub fn build(
56 format: impl Into<String>,
57 args_in: impl IntoIterator<Item = impl Into<Arg>>,
58) -> Box<dyn Builder> {
59 let mut args = Args::default();
60 for a in args_in {
61 args.add(a);
62 }
63 Box::new(CompiledBuilder::new(args, format.into()))
64}
65
66pub fn build_named(
68 format: impl Into<String>,
69 named_map: impl IntoIterator<Item = (String, Arg)>,
70) -> Box<dyn Builder> {
71 let mut args = Args {
72 only_named: true,
73 ..Args::default()
74 };
75
76 for (k, v) in named_map {
77 args.add(named(k, v));
78 }
79
80 Box::new(CompiledBuilder::new(args, format.into()))
81}
82
83pub fn buildf(format: &str, args_in: impl IntoIterator<Item = impl Into<Arg>>) -> Box<dyn Builder> {
85 let mut args = Args::default();
86 let escaped = escape(format);
87 let mut out = String::new();
88
89 let mut it = args_in.into_iter();
90 let mut chars = escaped.chars().peekable();
91 while let Some(c) = chars.next() {
92 if c == '%' {
93 match chars.peek().copied() {
94 Some('v') | Some('s') => {
95 chars.next();
96 if let Some(a) = it.next() {
97 let ph = args.add(a.into());
98 out.push_str(&ph);
99 } else {
100 out.push('%');
102 out.push('v');
103 }
104 }
105 Some('%') => {
106 chars.next();
107 out.push('%');
108 }
109 _ => out.push('%'),
110 }
111 } else {
112 out.push(c);
113 }
114 }
115
116 Box::new(CompiledBuilder::new(args, out))
118}