Crate strung

source ·
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

Modules

  • Configurations, currently just static pre- and postfix! 👻
  • All needed goods!

Macros

Structs

  • Just an empty unit struct with Strung trait!

Traits

  • Designed to be used with the strung-derive crate!