tweld 0.3.0-alpha.rc.2

Dynamic identifier generation for Rust macros. Tweld provides a flexible @[] syntax to "fuse" strings, case-conversions, and logic directly into your generated source code.
Documentation

Tweld

(you can read it as tiny-weld, token-weld, or just tweld, I am just happy to be here)

Tweld is a procedural macro toolkit and naming DSL for Rust. It allows you to dynamically generate, modify, and compose identifiers directly within your Rust code using a clean, safe, and intuitive @[] syntax (hopefully). Because the parsing happens safely inside the macro, any syntax errors in your modifiers will point exactly to the broken line in your IDE (also hopefully).

weld!("## @[(the idea | title)]");

The name comes from idea of fusing tokens, to help when writing macros, or macros for your macros (which was my initial case).

The @[] "interpolator"

Anything inside the @[] "interpolator" will be fused together. You can use the @[] syntax inside structs, functions, trait implementations, or anywhere an identifier is expected, as well as in the content of string literal.

"@[one - two]" will render "one-two"

It can be used with tokens to create identifiers, or inside a string literal.

weld!(
	fn @[weld_(_these toKens ById|snek|substr{1,})]() -> String {
	  "@This will render a function name (@[weld_(_these toKens ById|snek|substr{1,})]) with all these @[(tokens fused | title | lower )] together!]".to_string()
	}
);

List [] and single value () groups

Inside the the brackets, you can organize it in groups, and apply specific modifiers to that group.

List Group

When creating a group using [] and applying mods to it, each modification will be handled as in a collection, instead of a single concatenated value.


weld!(
    // will render: const super_duper = "";
	const @[([er sup] | reverse ) _duper] = ""; 
);

use weld::render_names;

weld! {
    // 1. Basic interpolation
    pub struct @[Users | singular | pascal] {
        pub id: i64,
    }

    // 2. Inline string replacement and casing
    impl @[Users | singular | PascalCase] {
        
        // Generates: pub fn get_user_profile_by_id(id: i64)
        pub fn @[((get_ UserProfiles) | replace{'s', ''}  snakecase) _by_id](id: i64) {
            println!("Fetching @[Users | singular | lower] {}...", id);
        }
    }
}

And inside a list group, you can have other groups, either single value or lists.

weld!(
    /* this will render:
        struct SuperDuper {
            pub id: i64,
        };
    */
	struct @[([([er sup] |reverse )-duper]|camel)| pascal ] {
        pub id: i64,
    }; 
);

When modifying a list group, these modifiers will be applied to each item:

  • singular,
  • plural,
  • casing modifiers
    • lowercase,
    • uppercase,
    • pascalcase,
    • camelcase,
    • snakecase,
    • titlecase,
    • kebabcase,
    • traincase,
    • shoutykebabcase,
    • shoutysnakecase,
  • string specific
    • replace
    • substring
    • padstart
    • padend

While other modifiers will behave as handling a vector, - reverse: reverses the order of items, not the items themselves, - repeat: repeats the items N times, - splice: replaces the specified range in the vector with the given value (if the value is informed) and either yield the removed items, if used with out, or the modified vector if used with into, - slice: slices the vector, returning the the values whithn the range,

Using split in this mode will split all the items that can be separated, adding them to the collection in the same point

join flattens the collection into a single value (a String), placing a given separator between each (if informed).

Single Value group

When creating a group using () and applying mods to it, each modification will be applied as in a single value. This group concatenates all the values with no separator.

weld!(
    // will render: const super_duper = "";
	const @[((er pus) | reverse ) _duper] = ""; 
);

Modifiers

While inside a group, you can apply a chain of modifiers, where each one will perform an operation on the previous result.

Simple modifiers:

These are self explanatory, being singular and plural just the removal of the letter 's' from the last word.

  • singular,
  • plural,
  • lower , lowercase
  • upper, uppercase

Casing style modifiers:

Casing style modifiers make use of the crate heck.

  • PascalCase: pascal , pascalcase, uppercamelcase
  • camelCase:lowercamelcase, camelcase, camel
  • snake_casesnakecase, snake, snekcase, snek
  • Title Case: titlecase, title
  • kebab-case: kebabcase, kebab
  • Train-Case: traincase, train
  • SHOUTY-KEBAB-CASE:shoutykebabcase, shoutykebab
  • SHOUTY_SNAKE_CASE: shoutysnakecase, shoutysnake, shoutysnekcase, shoutysnek

Limitations

Given the nature of the syntax, when applied to tokens (as in the body or signature of a function, etc), some of these modifiers will behave a bit different.

  • kebabcase, shoutykebabcase, and traincase won't work,
  • titlecase will behave like PascalCase

When applied to string literals, they will all work as intended

Advanced modifiers:

This crate comes with some modifiers that offer more complex operations that can be pretty helpful, given the right context

replace

It replaces all non-overlapping occurrences of a pattern, with a replacement string.

// will render: const a_small_ident = "";
weld!(const @[(a long ident) | replace{"long","small"}|snek] = "";);

substr, substring

This modifier returns the part of this string from the start index up to and excluding the end index, or to the end of the string if no end index is supplied. Both indexes are optional.

// will render: const a_long_ident = "";
weld!(const @[(a long identifier)| substr{,9}|snek] = "";);

reverse, rev

Reverses the identifier, or literal.

// will render: const tnedi_gnol_a = "";
weld!(const @[(a long identifier)| reverse] = "";);

// will render: const nolem_on_nomel_on = "";
weld!(const @[(no lemon no melon)| reverse|snek] = "";);

repeat, rep, times

Creates a new value by repeating it n times.

// will render: const rawhide = "rolling' ,rolling' ,rolling' ";
weld!(const rawhide = @[",rolling' "| times | substr{1}];);

split

This modifier will either break the whole value if applied to a single value group, or break each item in a list, if applied to a list group. This modifier accepts a char, a string or a number higher than 0.

(The second case will render a different result because it will have all items splitted)

Splitting by a char, or a string:

// will render: const val = "get, onetwo, 3, 4struct";
weld!( const val = @[(("get-one" two - "3-4" Struct) | split{'-'} | lower | join{", "})]) = ""; 

// will render: const val = "get, one, two, 3, 4, struct";
weld!( const val = @[["get-one" two - "3-4" Struct] | split{"-"} | lower | join{", "}]);                

Splitting by an index:

// will render: const val = "get-te_st-struct";
weld!( const val = @[(("get-" Test - Struct) | split{6} | lower | join{"_"})] );

// will render: const val = "ge,t-,te,st,-,st,ruct";
weld!( const val = @[["get-" Test - Struct] | split{2} | lower | join{","}] );

When splitting by index, if the value is bigger than the length, the argument will be ignored

// will render: const val = "get-,test,-,stru,ct";
weld!( const val = @[["get-" Test - Struct] | split{4} | lower | join{","}] );

join

Flattens the list of values into a single value, placing a given separator between each. The separator can be a string or a char. (If used in a scenario where the previous result is a single value, it wont have any impact).

// will render: const val = "get-,Test,-,Struct";
weld!( const val = @[["get-" Test - Struct] | join{","}] );

padstart, padleft, padl

Padstart pads the value with a given string (repeated and/or truncated, if needed) so that the resulting string has a given length. The padding is applied from the start of this string.

/*
                             0         1         2         3
                             0123456789012345678901234567890 */
// will render: const val = "-----get-Test-Struct",
weld!( const val = @[("get-" Test-Struct) | padleft{20, "-"} ]);

/*
                             0         1         2         3
                             0123456789012345678901234567890 */
// will render: const val = "get-Test-Struct",
weld!( const val = quote! { @[("get-" Test-Struct) | padstart{5, "-"} ]);

padend, padright, padr

Padstart pads the value with a given string (repeated and/or truncated, if needed) so that the resulting string has a given length. The padding is applied from the end of this string.

/*
                             0         1         2         3
                             0123456789012345678901234567890 */
// will render: const val = "get-Test-Struct-----",
weld!( const val = @[("get-" Test-Struct) | padright{20, "-"} ]);

/*
                             0         1         2         3
                             0123456789012345678901234567890 */
// will render: const val = "get-Test-Struct",
weld!( const val = @[("get-" Test-Struct) | padend{5, "-"} ]);

slice

Extracts a substring from a string using start and end positions. Both parameters are optional and support negative indexing, where negative values count backwards from the end of the string. When no arguments are provided, returns the full string. If the start is less than end, it will return an empty value

// will render: const val = "get_Struct",
weld!( const val = @["get_" Test_Struct | slice{5}] );

// will render: const val = "get_Test_St",
weld!( const val = @[("_get_" Test_Struct) | slice{1, -4}] );

// will render: const val = "St",
weld!( const val = @[("_get_" Test_Struct) | slice{-6, -4}] );

// will render: const val = "get_",
weld!( const val = @["get_" Test_Struct| slice{-4,-6}] );

splice

Modifies a string in place by removing a portion defined by start and end positions, optionally replacing it with new content. Returns either the removed portion or the modified string depending on the variant used. Both positions are optional and support negative indexing, where negative values count backwards from the end of the string.

Returning the altered value (value, val, into)

// will render: const val = "g";
weld!( const val = @[("get_" Test_Struct)| splice{value, 1}] );
weld!( const val = @[("get_" Test_Struct)| splice{val, 1}] );
weld!( const val = @[("get_" Test_Struct)| splice{into, 1}] );

Returning the removed value out, removed, rm

// will render: const val = "et_Test_Struct";
weld!( const val = @[("get_" Test_Struct)| splice{out, 1}] );
weld!( const val = @[("get_" Test_Struct)| splice{removed, 1}] );
weld!( const val = @[("get_" Test_Struct)| splice{rm, 1}] );

Removing

// will render: const val = "gTest_Struct";
weld!( const val = @[("get_" Test_Struct)| splice{into, 1, 4}] );

// will render: const val = "et";
weld!( const val = @[("get_" Test_Struct)| splice{out, 1, 4}] );

Replacing

// will render: const val = "got_Test_Struct";
weld!( const val = @[("get_" Test_Struct)| splice{value, 1, 4, "ot_"}] );

// will render: const val = "got_Test_Struct";
weld!( const val = @[("get_" Test_Struct)| splice{val,, 4, "got_"}] );

// will render: const val = "got_Test_Struct";
weld!( const val = @[("get_" Test_Struct)| splice{into,, 4, "got_"}] );

// will render: const val = "got_";
weld!( const val = @[("get_" Test_Struct)| splice{value, 1,, "ot_"}] );

// will render: const val = "new";
weld!( const val = @[("get_" Test_Struct)| splice{val,,, "new"}] );

Using negative start

Will start counting from the end of the string

// will render: const val = "get_Test_St";
weld!( const val = @[("get_" Test_Struct)| splice{into, -4 }] );

// will render: const val = "get_Test_Stru";
weld!( const val = @[("get_" Test_Struct)| splice{out, -2 }] );

Using negative end

Will count from the end of the string

// will render: const val = "get_Test_Stt";
weld!( const val = @[("get_" Test_Struct)| splice{into, -4, -1 }] );

// will render: const val = "get_Test_Strut";
weld!( const val = @[("get_" Test_Struct)| splice{out, -2, -1 }] );

// will render: const val = "get_Test_St<->t";
weld!( const val = @[("get_" Test_Struct)| splice{into, -4, -1, "<->3" }] );

spliceout, splice_out

Alias for splice with out argument.

// will render: const val = "ruc";
weld!( const val = @[("get_" Test_Struct)| splice_out {-4, -1, "<->3" }] );

spliceinto, splice_into

Alias for splice with the into argument.

// will render: const val = "get_Test_St<->t";
weld!( const val = @[("get_" Test_Struct)| splice_into {-4, -1, "<->3" }] );