1# function.
2
3## Usage ##
4
5There are two ways to use this library. You can use the `cl_format!` macro, or generate the control string and format your arguments by yourself for more flexibility.
6
7First, add `cl-format = "0.2"` in your `Cargo.toml`.
8
9### Use macro ###
10
11`~a` is the most common directive I like to use, so let's start from normal `~a`:
12
13```rust
14use cl_format::*;
15
16let a = cl_format!("~a, ~a, ~a", &1_i32, &2, &3);
17assert_eq!(String::from("1, 2, 3"), a.unwrap());
18```
19
20All arguments used for formatting have to be borrowed, and they must implement the `TildeAble` trait. Check the [Implement for custom type](#implement-for-custom-type) section for more details.
21
22Here is more usage of the macro. Escaping the double quote symbol for strings:
23
24```rust
25use cl_format::*;
26let s = String::from("abc");
27let a = cl_format!("~a, ~a, ~a, ~S", &1_i32, &2, &3, &s);
28assert_eq!(String::from("1, 2, 3, \"abc\""), a.unwrap());
29```
30
31Or not:
32
33```rust
34use cl_format::*;
35let a = cl_format!("start ~a, ~a, ~a, ~a, here", &1_i32, &2, &3, &s);
36assert_eq!(String::from("start 1, 2, 3, abc, here"), a.unwrap());
37```
38
39Let's make some loops inside the control string like Lispers do:
40
41```rust
42use cl_format::*;
43let ll: Vec<&dyn TildeAble> = vec![&1, &2, &3];
44let a = cl_format!("~a, ~a, ~a, ~{~a,~}", &1_i32, &2, &3, &ll);
45assert_eq!(String::from("1, 2, 3, 1,2,3,"), a.unwrap());
46```
47
48Wait, we have an unnecessary comma at the end of the result, let's clean it up:
49
50```rust
51use cl_format::*;
52let a = cl_format!("~a, ~a, ~a, ~{~a~^,~}", &1_i32, &2, &3, &ll);
53assert_eq!(String::from("1, 2, 3, 1,2,3"), a.unwrap());
54```
55
56I suddenly don't want to loop the Vec anymore:
57
58```rust
59use cl_format::*;
60let l = vec![&1 as &dyn TildeAble, &2, &3];
61let a = cl_format!("The value is:\n ~a", &l);
62assert_eq!(String::from("The value is:\n [1, 2, 3]"), a.unwrap());
63```
64
65Now, we have some inconsistency between Common Lisp and Rust. In Common Lisp, `~%` in the control string is the new line, but we are in Rust now, so `\n` is going to work.
66
67I think I am a bit tired of showing the type as `&dyn TildeAble` to elements inside Vec. But I haven't found a way to avoid it yet. If you know, let me know. So I added some macros:
68
69
70```rust
71use cl_format::*;
72let l = vec![tilde!(&1), &2, &3];
73let a = cl_format!("The value is:\n ~a", &l);
74assert_eq!(String::from("The value is:\n [1, 2, 3]"), a.unwrap());
75```
76
77As in Common Lisp, we can loop through all arguments instead of putting them inside a Vec:
78
79```rust
80use cl_format::*;
81let a = cl_format!("~@{~a~^, ~}", &1, &2, &3);
82assert_eq!(String::from("1, 2, 3"), a.unwrap());
83```
84
85Now, let's try some condition control (you can get the meaning of the condition control string in the `Conditional Formatting` chapter of [A Few FORMAT Recipes](https://gigamonkeys.com/book/a-few-format-recipes.html)):
86
87```rust
88use cl_format::*;
89let l = vec![tilde!(&1), &2, &3];
90let a = cl_format!("~{~a~#[~;, and ~:;, ~]~}", &l);
91assert_eq!(String::from("1, 2, and 3"), a.unwrap());
92
93let l = vec![tilde!(&1), &2, &3, &4];
94let a = cl_format!("~{~a~#[~;, and ~:;, ~]~}", &l);
95assert_eq!(String::from("1, 2, 3, and 4"), a.unwrap());
96```
97
98### Manually ###
99
100Using macros will generate the control string instance every time. It might be wasteful if you are trying to use a control string everywhere because it is flexible enough for multiple uses.
101
102We can generate it by ourselves:
103
104```rust
105let cs = cl_format::ControlStr::from("~{~#[~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~}").unwrap();
106```
107
108Then we can generate the Args for the control string to reveal:
109
110```rust
111use cl_format::Args;
112let mut list = vec![];
113let args = Args::new(vec![&list]);
114```
115
116Let's use it several times by giving different lengths of arguments:
117
118```rust
119use cl_format::*;
120use cl_format::cl_format;
121
122// this equal cl_format!(cs, &list)
123assert_eq!(cs.reveal(args).unwrap(), "".to_string());
124
125list.push(&1);
126let args = Args::new(vec![&list]);
127assert_eq!(cs.reveal(args).unwrap(), "1".to_string());
128
129list.push(&2);
130let args = Args::new(vec![&list]);
131assert_eq!(cs.reveal(args).unwrap(), "1 and 2".to_string());
132
133list.push(&3);
134let args = Args::new(vec![&list]);
135assert_eq!(cs.reveal(args).unwrap(), "1, 2, and 3".to_string());
136
137list.push(&4);
138let args = Args::new(vec![&list]);
139assert_eq!(cs.reveal(args).unwrap(), "1, 2, 3, and 4".to_string());
140```
141
142Let's try a mixed example:
143
144```rust
145use cl_format::*;
146
147let my_team = String::from("STeam");
148let my_stars = vec![
149 String::from("Adam Lambert"),
150 String::from("Queen"),
151 String::from("snoop dogg"),
152];
153
154let stars = my_stars
155 .iter()
156 .map(|s| tilde!(s))
157 .collect::<Vec<&dyn TildeAble>>();
158
159assert_eq!(
160 String::from("my favorite team \"STeam\" will win the superbowl LVIII. And Adam Lambert, Queen, and snoop dogg will in half time show. And the scores should be 38:35"),
161 cl_format!(
162 "my favorite team ~S will win the superbowl ~@R. And ~{~#[~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~} will in half time show. And the scores should be ~d:~d",
163 &my_team,
164 &58,
165 &stars,
166 &38,
167 &35
168 )
169 .unwrap()
170);
171
172```
173
174### Implement for custom type ###
175
176So far, we have only shown the basic types. It would be better if we could make our type be revealed as well.
177
178Here is a demo on how to implement:
179
180```rust
181use cl_format::*;
182
183// has to derive to Debug
184#[derive(Debug)]
185struct MyStruct {
186 a: usize,
187 b: String,
188}
189
190impl TildeAble for MyStruct {
191 // there are a lot methods inside, but not every of them
192 // we are need.
193
194 // ~a is good enough
195 fn into_tildekind_va(&self) -> Option<&dyn TildeKindVa> {
196 Some(self)
197 }
198
199 // ~d just for show case
200 fn into_tildekind_digit(&self) -> Option<&dyn TildeKindDigit> {
201 Some(self)
202 }
203
204 // how many elements you want cl_format treat this type
205 // 1 is enough. And this one has to implement
206 fn len(&self) -> usize {
207 1
208 }
209}
210
211/// By now, your IDE should give you some errors, letting you implement `TildeKindVa` and `TildeKindDigit`.
212
213impl TildeKindVa for MyStruct {
214 fn format(&self, tkind: &TildeKind, buf: &mut String) -> Result<(), TildeError> {
215 buf.push_str(&format!("a: {}, b: {}", self.a, self.b));
216 Ok(())
217 }
218}
219
220impl TildeKindDigit for MyStruct {
221 fn format(&self, tkind: &TildeKind, buf: &mut String) -> Result<(), TildeError> {
222 buf.push_str(&format!("{}", self.a));
223 Ok(())
224 }
225}
226```
227
228Now `MyStruct` can be used by `cl_format`, but as you guessed, only for `~a` and `~d`
229
230```rust
231use cl_format::*;
232
233let s = MyStruct {
234 a: 1,
235 b: "b".to_string(),
236};
237
238assert_eq!("a: 1, b: b".to_string(), cl_format!("~a", &s).unwrap());
239assert_eq!(
240 "a: 1, b: b lalalal a: 1, b: b".to_string(),
241 cl_format!("~a lalalal ~a", &s, &s).unwrap()
242);
243
244assert_eq!("1".to_string(), cl_format!("~d", &s).unwrap());
245assert_eq!(
246 "First: a: 1, b: b; Second: 1".to_string(),
247 cl_format!("First: ~a; Second: ~d", &s, &s).unwrap()
248);
249```
250
251## Format directives ##
252
253This is the table of which directives have been implemented:
254
255| tilde | rust type |
256|:-------------------------:|:--------------------------------------------------------------------------------------------:|
257| `~a` | f32, f64, char, i8, i16, i32, i64, i128, isize, bool, u8, u16, u32, u64, u128, usize, String |
258| `~s` | f32, f64, char, i32, i64, usize, bool, u32, u64, String |
259| `~d` | i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize |
260| `~C` | char |
261| `~[~]` (normal condition) | bool, usize |
262| `~R` | i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize |
263
264"#]
265#![feature(let_chains)]
266#![feature(pattern)]
267
268mod control_str;
269mod tildes;
270
271pub use control_str::*;
272pub use tildes::*;
273
274#[doc = r#"Helper macro for implementing type with specific Tilde traits
275
276For example `multi_tilde_impl!(TildeKindVa, [float, char, String], self, {Err("un-implenmented yet".into())})`
277
278will expand
279
280```rust
281impl TildeKindVa for float {
282 fn format(&self, tkind: &TildeKind) -> Result<String, Box<dyn std::error::Error>> {
283 Err("un-implenmented yet".into())
284 }
285}
286```"#]
287#[macro_export]
288macro_rules! multi_tilde_impl {
289 ($implName:ident, [$($y:ident),+], $s:ident, $buf:ident, $body:block) => {
290 $(
291 impl $implName for $y {
292 fn format(&$s, _: &TildeKind, $buf: &mut String) -> Result<(), TildeError>
293 $body
294
295 }
296 )+
297 };
298}
299
300#[doc = r"Macro for adding `as &dyn crate::TildeAble` to the expr"]
301#[macro_export]
302macro_rules! tilde {
303 ($arg:expr) => {
304 $arg as &dyn cl_format::TildeAble
305 };
306}
307
308#[doc = r#"`cl_format!` is the macro for quick using cl-format
309
310```rust
311cl_format!(control_str, &a, &b, &c) => {
312 let c = control_str::ControlStr::new("~a, ~a, ~a").expect("making control string has issue");
313 let a = Into::<
314 tildes::Args<'_>,
315 >::into([
316 &1 as &dyn tildes::TildeAble,
317 &2 as &dyn tildes::TildeAble,
318 &3 as &dyn tildes::TildeAble,
319 ]);
320 c.reveal(a)
321}
322```
323For example:
324
325```rust
326let l = vec![tilde!(&1), &2, &3, &4];
327let a = cl_format!("~{~a~#[~;, and ~:;, ~]~}", &l);
328assert_eq!(String::from("1, 2, 3, and 4"), a.unwrap());
329```"#]
330#[macro_export]
331macro_rules! cl_format {
332 ($control_str:expr) => {
333 {
334 let c = cl_format::ControlStr::new($control_str).expect("making control string has issue");
335 let a = cl_format::Args::new(vec![]);
336 c.reveal(a)
337 }
338 };
339 ($control_str:expr, $($ele:expr),*) => {
340 {
341 let c = cl_format::ControlStr::new($control_str).expect("making control string has issue");
342 let a = Into::<Args<'_,'_>>::into([$(tilde!($ele)),*]);
343 c.reveal(a)
344 }
345 }
346
347}
348
349#[cfg(test)]
350mod tests {}