Expand description
cl-format
s the Rust implementation of the Common Lisp format function.
§Usage
There 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.
First, add cl-format = "0.2"
in your Cargo.toml
.
§Use macro
~a
is the most common directive I like to use, so let’s start from normal ~a
:
use cl_format::*;
let a = cl_format!("~a, ~a, ~a", &1_i32, &2, &3);
assert_eq!(String::from("1, 2, 3"), a.unwrap());
All arguments used for formatting have to be borrowed, and they must implement the TildeAble
trait. Check the Implement for custom type section for more details.
Here is more usage of the macro. Escaping the double quote symbol for strings:
use cl_format::*;
let s = String::from("abc");
let a = cl_format!("~a, ~a, ~a, ~S", &1_i32, &2, &3, &s);
assert_eq!(String::from("1, 2, 3, \"abc\""), a.unwrap());
Or not:
use cl_format::*;
let a = cl_format!("start ~a, ~a, ~a, ~a, here", &1_i32, &2, &3, &s);
assert_eq!(String::from("start 1, 2, 3, abc, here"), a.unwrap());
Let’s make some loops inside the control string like Lispers do:
use cl_format::*;
let ll: Vec<&dyn TildeAble> = vec![&1, &2, &3];
let a = cl_format!("~a, ~a, ~a, ~{~a,~}", &1_i32, &2, &3, &ll);
assert_eq!(String::from("1, 2, 3, 1,2,3,"), a.unwrap());
Wait, we have an unnecessary comma at the end of the result, let’s clean it up:
use cl_format::*;
let a = cl_format!("~a, ~a, ~a, ~{~a~^,~}", &1_i32, &2, &3, &ll);
assert_eq!(String::from("1, 2, 3, 1,2,3"), a.unwrap());
I suddenly don’t want to loop the Vec anymore:
use cl_format::*;
let l = vec![&1 as &dyn TildeAble, &2, &3];
let a = cl_format!("The value is:\n ~a", &l);
assert_eq!(String::from("The value is:\n [1, 2, 3]"), a.unwrap());
Now, 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.
I 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:
use cl_format::*;
let l = vec![tilde!(&1), &2, &3];
let a = cl_format!("The value is:\n ~a", &l);
assert_eq!(String::from("The value is:\n [1, 2, 3]"), a.unwrap());
As in Common Lisp, we can loop through all arguments instead of putting them inside a Vec:
use cl_format::*;
let a = cl_format!("~@{~a~^, ~}", &1, &2, &3);
assert_eq!(String::from("1, 2, 3"), a.unwrap());
Now, 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):
use cl_format::*;
let l = vec![tilde!(&1), &2, &3];
let a = cl_format!("~{~a~#[~;, and ~:;, ~]~}", &l);
assert_eq!(String::from("1, 2, and 3"), a.unwrap());
let l = vec![tilde!(&1), &2, &3, &4];
let a = cl_format!("~{~a~#[~;, and ~:;, ~]~}", &l);
assert_eq!(String::from("1, 2, 3, and 4"), a.unwrap());
§Manually
Using 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.
We can generate it by ourselves:
let cs = cl_format::ControlStr::from("~{~#[~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~}").unwrap();
Then we can generate the Args for the control string to reveal:
use cl_format::Args;
let mut list = vec![];
let args = Args::new(vec![&list]);
Let’s use it several times by giving different lengths of arguments:
use cl_format::*;
use cl_format::cl_format;
// this equal cl_format!(cs, &list)
assert_eq!(cs.reveal(args).unwrap(), "".to_string());
list.push(&1);
let args = Args::new(vec![&list]);
assert_eq!(cs.reveal(args).unwrap(), "1".to_string());
list.push(&2);
let args = Args::new(vec![&list]);
assert_eq!(cs.reveal(args).unwrap(), "1 and 2".to_string());
list.push(&3);
let args = Args::new(vec![&list]);
assert_eq!(cs.reveal(args).unwrap(), "1, 2, and 3".to_string());
list.push(&4);
let args = Args::new(vec![&list]);
assert_eq!(cs.reveal(args).unwrap(), "1, 2, 3, and 4".to_string());
Let’s try a mixed example:
use cl_format::*;
let my_team = String::from("STeam");
let my_stars = vec![
String::from("Adam Lambert"),
String::from("Queen"),
String::from("snoop dogg"),
];
let stars = my_stars
.iter()
.map(|s| tilde!(s))
.collect::<Vec<&dyn TildeAble>>();
assert_eq!(
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"),
cl_format!(
"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",
&my_team,
&58,
&stars,
&38,
&35
)
.unwrap()
);
§Implement for custom type
So far, we have only shown the basic types. It would be better if we could make our type be revealed as well.
Here is a demo on how to implement:
use cl_format::*;
// has to derive to Debug
#[derive(Debug)]
struct MyStruct {
a: usize,
b: String,
}
impl TildeAble for MyStruct {
// there are a lot methods inside, but not every of them
// we are need.
// ~a is good enough
fn into_tildekind_va(&self) -> Option<&dyn TildeKindVa> {
Some(self)
}
// ~d just for show case
fn into_tildekind_digit(&self) -> Option<&dyn TildeKindDigit> {
Some(self)
}
// how many elements you want cl_format treat this type
// 1 is enough. And this one has to implement
fn len(&self) -> usize {
1
}
}
/// By now, your IDE should give you some errors, letting you implement `TildeKindVa` and `TildeKindDigit`.
impl TildeKindVa for MyStruct {
fn format(&self, tkind: &TildeKind, buf: &mut String) -> Result<(), TildeError> {
buf.push_str(&format!("a: {}, b: {}", self.a, self.b));
Ok(())
}
}
impl TildeKindDigit for MyStruct {
fn format(&self, tkind: &TildeKind, buf: &mut String) -> Result<(), TildeError> {
buf.push_str(&format!("{}", self.a));
Ok(())
}
}
Now MyStruct
can be used by cl_format
, but as you guessed, only for ~a
and ~d
use cl_format::*;
let s = MyStruct {
a: 1,
b: "b".to_string(),
};
assert_eq!("a: 1, b: b".to_string(), cl_format!("~a", &s).unwrap());
assert_eq!(
"a: 1, b: b lalalal a: 1, b: b".to_string(),
cl_format!("~a lalalal ~a", &s, &s).unwrap()
);
assert_eq!("1".to_string(), cl_format!("~d", &s).unwrap());
assert_eq!(
"First: a: 1, b: b; Second: 1".to_string(),
cl_format!("First: ~a; Second: ~d", &s, &s).unwrap()
);
§Format directives
This is the table of which directives have been implemented:
tilde | rust type |
---|---|
~a | f32, f64, char, i8, i16, i32, i64, i128, isize, bool, u8, u16, u32, u64, u128, usize, String |
~s | f32, f64, char, i32, i64, usize, bool, u32, u64, String |
~d | i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize |
~C | char |
~[~] (normal condition) | bool, usize |
~R | i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize, isize |
Macros§
- cl_
format cl_format!
is the macro for quick using cl-format- multi_
tilde_ impl - Helper macro for implementing type with specific Tilde traits
- tilde
- Macro for adding
as &dyn crate::TildeAble
to the expr
Structs§
- Args
- The args for control string to use.
- Control
Str - The control string is the type contains control string for format.
- Tilde
- The tilde struct
- Tilde
Error - Error type for tildes parsing
Enums§
- Char
Kind - CharKind
- Error
Kind - ErrorKind
- Radix
Flag - Radix flag ~@R, ~:R, and ~:@R
- Star
Kind - StarKind
- Tilde
Cond Kind - TildeCondKind
- Tilde
Kind - TildeKind is the enum that including all potential kind.
- Tilde
Loop Kind - TildeLoopKind
Traits§
- Tilde
Able - TildeAble is used for dispatch types to the specific tilde trait it implemented.
- Tilde
Kind Char - TildeKindChar is the trait that contains implementation of type for TildeKind::Char.
- Tilde
Kind Cond - TildeKindCond is the trait that contains implementation of type for TildeKind::Cond.
- Tilde
Kind Digit - TildeKindDigit is the trait that contains implementation of type for TildeKind::Digit.
- Tilde
Kind Float - TildeKindFloat is the trait that contains implementation of type for TildeKind::Float.
- Tilde
Kind Loop - TildeKindLoop is the trait that contains implementation of type for TildeKind::Loop.
- Tilde
Kind Loop End - TildeKindLoopEnd is the trait that contains implementation of type for TildeKind::LoopEnd.
- Tilde
Kind Radix - TildeKindRadix is the trait that contains implementation of type for TildeKind::Radix.
- Tilde
Kind Standard - TildeKindStandard is the trait that contains implementation of type for TildeKind::Standard.
- Tilde
Kind Star - TildeKindStar is the trait that contains implementation of type for TildeKind::Star.
- Tilde
Kind Text - TildeKindText is the trait that contains implementation of type for TildeKind::Text.
- Tilde
Kind Tildes - TildeKindTildes is the trait that contains implementation of type for TildeKind::Tildes.
- Tilde
Kind Va - TildeKindVa is the trait that contains implementation of type for TildeKind::Va.
- Tilde
Kind VecTilde - TildeKindVecTilde is the trait that contains implementation of type for TildeKind::VecTilde.