String formatter/builder with easy access of struct fields, which implement the Display trait. If they do not, they can be marked to be ignored.
Here's most you have to know! ```rust 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
struct Test { num: u32, name: &'static str, }
// tuple struct
struct TestTup(u32,&'static str);
// custom pre/postfix per struct + ignore
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
#[strung(ignore)] nop: NoDsply
}
// custom pre/postfix per struct + ignore
struct TestCascade {
// cascade: makes the fields of another Strung available
// the ignore only affects the the struct itself, not its fields
// and this would fail w/o it cause it doesn't implement it!
#[strung(cascade,ignore)]
tup: TestTup
}
// struct with no Display trait
struct NoDsply;
```
Prelude imports two static variables [config::STRUNGPRE] and [config::STRUNGPOST], which can be used to set the prefix and postfix as a configuration. [Strung::strungstatic] uses anything called STRUNGPRE or STRUNG_POST on the file.
[config::staticglobal] 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::staticon_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: ```rust use strung::prelude::*; // Import everything from prelude strung::config::staticonfile!("[","]"); // 🌱 Shadow static pre/postfix for this file.
struct Test { text: &'static str, num: u32 } fn main(){ let test = Test { // Create struct as usual text: "5k", num: 5000 }; let text = test.strungstatic("[text]=[num]"); // 🌱 Use whatever you've set above asserteq!(&text,"5k=5000"); } ```
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 #[strung(ignore)] attribute: ```rust use strung::prelude::*; // Import everything from prelude
struct CustomField (u32); // 🌱 A struct, not impl Display
struct Test { num: u32, #[strung(ignore)] nope: CustomField // 🌱 Would fail without the attribute! }
struct TestTup ( u32, #[strung(ignore)] CustomField, // 🌱 Would fail without the attribute! &'static str );
fn main(){ /* ------------------------------ Named Fields ------------------------------ */ let test = Test { // Create struct as usual num: 1, nope: CustomField(0), // 🌱 }; let text = test.strung("Number {num} {nope}"); // 🌱 {nope} not available! assert_eq!(&text,"Number 1 {nope}");
/* ------------------------- Unnamed Fields (Tuple) ------------------------- */
let test = TestTup(1,CustomField(0),":)"); // Create struct as usual
let text = test.strung("Number {0} {1} {2}"); // 🌱 {1} not available!
assert_eq!(&text,"Number 1 {1} :)");
} ```
There's also the possibility of cascading. e.g.: $field.0.num
, it's experimentally implemented for [Strung::strungdollar] and [Strung::strunghashtag] 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 #[strung(cascade)]
attribute:
```rust
use strung::prelude::*;
// #[strung(ignore)] just because none of them are implementing Display!
fn main(){ let test = A{ field: B(500,C{ num: 123, field: D(E{ num: 623 }) }) }; let text = test.strungdollar( "Hello, $field.0 + $field.1.num = $field.1.field.0.num" ); asserteq!(&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");
} ```
Licensed under either of Apache License, Version
2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.