Expand description
String formatter/builder with easy access of struct fields, which implement the std::fmt::Display trait. If they do not, they can be marked to be ignored.
§Usage
Here’s most you have to know!
use strung::prelude::*; // import everything from prelude
fn main(){
// create structs - defined below cause instant action!
let NAMED = Test {num: 1, name: "st"};
let TUPLE = TestTup (1, "st");
let CUSTOM = TestCustom {num: 1, nop: NoDsply};
// most general - you'll probably mostly use this! - using {field_name}
let text = NAMED.strung("{num}{name}");
assert_eq!(&text,"1st");
// also works with String, just reference it
let s: String = "{num}{name}".into();
let text = NAMED.strung(&s);
assert_eq!(&text,"1st");
// it will always replace every occurrence
let text = NAMED.strung("{num}{num}th < {num}{name}");
assert_eq!(&text,"11th < 1st");
// for tuple structs, use the fields index number, instead of the name
let text = TUPLE.strung("{0}{1}");
assert_eq!(&text,"1st");
// the [strung] function will change if you set custom pre/postfix - see TestCustom below
let text = CUSTOM.strung("%num%st");
assert_eq!(&text,"1st");
// there are different presets, so you can still use {field_name} using [strung_curly]
let text = CUSTOM.strung_curly("{num}st");
assert_eq!(&text,"1st");
// note: {nop} is not available, cause it's ignored - see TestCustom below
let text = CUSTOM.strung_curly("{num}st {nop}");
assert_eq!(&text,"1st {nop}");
// [strung_dollar] for $field_name
let text = NAMED.strung_dollar("$num$name");
assert_eq!(&text,"1st");
// [strung_dollry] for ${field_name}
let text = NAMED.strung_dollry("${num}${name}");
assert_eq!(&text,"1st");
// [strung_hashtag] for #field_name
let text = NAMED.strung_hashtag("#num#name");
assert_eq!(&text,"1st");
// [strung_angle] for <field_name>
let text = NAMED.strung_angle("<num><name>");
assert_eq!(&text,"1st");
// most flexible - inline setting via [strung_dynamic] - a bit less efficient
let text = NAMED.strung_dynamic("<",">","<num><name>");
assert_eq!(&text,"1st");
// also flexible - global static variables, you can easily change ...
strung::config::static_global("+","+");
let text = NAMED.strung_static("+num++name+");
assert_eq!(&text,"1st");
// ... whenever you want, but usually you'll just need it once at the start of main()
strung::config::static_global("[","]");
let text = NAMED.strung_static("[num][name]");
assert_eq!(&text,"1st");
// [strung_hashtag] and [strung_dollar] also enable cascading
let CASCADE = TestCascade {tup: TestTup(2,"nd")};
let text = CASCADE.strung_dollar("$tup.0$tup.1");
assert_eq!(&text,"2nd");
}
// named struct
#[derive(Strung)] // easy derive
struct Test {
num: u32,
name: &'static str,
}
// tuple struct
#[derive(Strung)] // easy derive
struct TestTup(u32,&'static str);
// custom pre/postfix per struct + ignore
#[derive(Strung)] // easy derive
#[strung("%","%")] // custom pre/postfix!
struct TestCustom {
num: u32,
// ignore: makes this field unavailable
// this would fail w/o the ignore, cause no [Display]!
// other usage: gain a lil more performance
#[igno] // or #[strung(igno)] 2b more specific
nop: NoDsply
}
// custom pre/postfix per struct + ignore
#[derive(Strung)] // easy derive
struct TestCascade {
// cascade: makes the fields of another Strung available
// automatically ignores the struct itself, not its fields
#[cscd] //, #[cascade], #[strung(cascade)] or #[strung(cscd)]
tup: TestTup
}
// struct with no Display trait
struct NoDsply;
§About Statics
Prelude imports two static variables config::STRUNG_PRE and config::STRUNG_POST, which can be used to set the prefix and postfix as a configuration. Strung::strung_static uses anything called STRUNG_PRE or STRUNG_POST on the file.
config::static_global changes these variables, as you saw in the walkthrough, there’s another method of changing it per file. It’s not included in the walkthrough cause it shadows these variables, it’s a macro called config::static_on_file.
📝 Note: This will maybe change in future, so these variables dont have to always be imported.
But: here’s how it can be used for now:
// Import everything from prelude
use strung::prelude::*;
// Shadow static pre/postfix for this file.
strung::config::static_on_file!("[","]");
#[derive(Strung)]
struct Test {
text: &'static str,
num: u32
}
fn main(){
// Create struct as usual
let test = Test {
text: "5k",
num: 5000
};
// Use whatever you've set above
let text = test.strung_static("[text]=[num]");
assert_eq!(&text,"5k=5000");
}
§Ignore fields
Sometimes you wanna ignore certain fields - e.g. in these scenarios:
- Get even moar efficiency 📈
- A field-type doesn’t implement std::fmt::Display
This can be done with the
#[igno]
attribute, or if it interferes with other macros, you can be more specific:#[strung(ignore)]
or#[strung(igno)]
:
// Import everything from prelude
use strung::prelude::*;
// A struct, not impl Display
struct CustomField (u32);
#[derive(Strung)]
struct Test {
num: u32,
// Would fail without the attribute:
#[igno] nope: CustomField
}
#[derive(Strung)]
struct TestTup (
u32,
// Would fail without the attribute:
#[strung(ignore)] CustomField,
&'static str
);
fn main(){
/* ------------------------------ Named Fields ------------------------------ */
// Create struct as usual
let test = Test {
num: 1,
nope: CustomField(0),
};
// {nope} not available!
let text = test.strung("Number {num} {nope}");
assert_eq!(&text,"Number 1 {nope}");
/* ------------------------- Unnamed Fields (Tuple) ------------------------- */
// Create struct as usual
let test = TestTup(1,CustomField(0),":)");
// {1} not available!
let text = test.strung("Number {0} {1} {2}");
assert_eq!(&text,"Number 1 {1} :)");
}
§❗ Experimental: Cascading ❗
There’s also the possibility of cascading. e.g.: $field.0.num
, it’s experimentally implemented for Strung::strung_dollar and Strung::strung_hashtag at the moment,
cause it was the easiest to do. 🦀
For this to work, the field-type has to derive Strung via derive macro and mark it with the #[cascade]
, #[cscd]
attribute or #[strung(cascade)]
, #[strung(cscd)]
in more specific use cases:
use strung::prelude::*;
// #[strung(ignore)] just because none of them are implementing Display!
#[derive(Strung)] struct A {#[cscd]field:B}
#[derive(Strung)] struct B (u32,#[cascade]C);
#[derive(Strung)] struct C {#[strung(cascade)]field:D,num:u32}
#[derive(Strung)] struct D (#[strung(cscd)]E);
#[derive(Strung)] struct E {num:u32}
fn main(){
let test = A{
field: B(500,C{
num: 123,
field: D(E{
num: 623
})
})
};
let text = test.strung_dollar(
"Hello, $field.0 + $field.1.num = $field.1.field.0.num"
);
assert_eq!(&text,"Hello, 500 + 123 = 623");
let text = test.strung_hashtag(
"Hello, #field.0 + #field.1.num = #field.1.field.0.num"
);
assert_eq!(&text,"Hello, 500 + 123 = 623");
}
Re-exports§
pub use config::set_static;
Modules§
Macros§
- static_
on_ file - Quickly shadow STRUNG_PRE and STRUNG_POST for the current file.
Structs§
- Strung
Unit - Just an empty unit struct with Strung trait!
Traits§
- Strung
- Designed to be used with the strung-derive crate!