combine!() { /* proc-macro */ }Expand description
Combines idents and strings into a single ident or string.
Both idents and strings are decoded as literal strings. Punctuation is
ignored when building ident output.
Arguments are specified via named arguments:
output: The type of the combined value. Must be eitherident,stringorisize.input: The input tokens to combine.prefix: The prefix tokens to emit before the combined value.suffix: The suffix tokens to emit after the combined value.paren: The group to emit around the combined value.paren_prefix: The prefix tokens to emit before the value inside the group.paren_suffix: The suffix tokens to emit after the value inside the group.span: The span of the combined value. If not specified, the call-site’s span is used.
// Idents, strings and numeric literals are combined as-is.
let str = combine!(output=string input=(prefix _ "NAME" _ suffix));
assert_eq!(str, "prefix_NAME_suffix");
let str = combine!(output=string input=(prefix _ NAME _ 1 _ suffix));
assert_eq!(str, "prefix_NAME_1_suffix");
// Resolves to `let prefix_NAME_suffix = 1;`
combine!(output=ident input=(prefix _ "NAME" _ suffix) prefix=(let ) suffix=(= 1;));
assert_eq!(prefix_NAME_suffix, 1);
// Set the span of the combined value to one of a specific token (useful for macros)
combine!(output=ident input=(prefix _ "NAME" _ suffix) span=token);§Token handling
The macro will ignore grouping tokens. For ident output, the macro will also ignore punctuation tokens and will strip any non-ident-compatible characters.
§Functions
The macro also supports a number of special function tokens which are resolved recursively and can be nested arbitrarily deep.
macro_rules! make_name_max_length {
($prefix:ident $name:ident $suffix:ident) => {
// Ensure user calls are not expanded recursively
/* $crate:: */ make_name_max_length!(@internal (__RAW__(input=($prefix))) (__RAW__(input=($name))) (__RAW__(input=($suffix))))
};
(@internal $prefix:tt $name:tt $suffix:tt) => {
combine!(output=string input=(
__IF__(
test=(__GT__(a=(__LENGTH__(string=($prefix _ $name _ $suffix))) b=20))
then=(
__SUBSTRING__(input=($prefix _ $name _ $suffix) start=0 end=10)
_ $
// uppercase hash
__TRANSLATE__(
input=(__SUBSTRING__(input=(__HASH__(string=($prefix _ $name _ $suffix))) length=10))
pattern=[a-f] replacement=[A-F]
)
)
else=(
$prefix _ $name _ $suffix _ __LENGTH__(string=($name))
)
)
))
};
}
assert_eq!(make_name_max_length!(prefix NAME suffix), "prefix_NAME_suffix_4");
assert_eq!(make_name_max_length!(prefix LONG_NAME suffix), "prefix_LON_$64470473B5");§Source span functions
__FILE__(of=token): The file name of the file containing thetoken. Supported on Rust 1.88+, returns “” otherwise.__LINE__(of=token): The line number of thetoken. Supported on Rust 1.88+, returns 0 otherwise.__COLUMN__(of=token): The column number of thetoken. Supported on Rust 1.88+, returns 0 otherwise.
let file = combine!(output=string input=("file:" __FILE__(of=token) ":" __LINE__(of=token) ":" __COLUMN__(of=token)));
assert_eq!(file, "file:linktime-proc-macro/src/lib.rs:6:110");__LOCATIONHASH__(of=token, alphabet=[chars...]): Returns a hash of location information for all tokens within the tree oftoken. Ifalphabetis specified, the hash is converted to a string using the characters in thealphabet. No zero padding is applied in this case.
let location_hash = combine!(output=string input=("location_hash:" __LOCATIONHASH__(of=(a bunch of tokens) alphabet=[a-z])));
assert_eq!(location_hash, "location_hash:dwsrxapjetrwwu");__SOURCE__(of=token): The source text content of thetoken.
macro_rules! source_of {
($token:item) => {
combine!(output=string input=("source" "(@" __LINE__(of=$token) "):" __SOURCE__(of=$token)))
};
}
// Use a macro to get the line and source text of a token
assert_eq!(source_of!(fn foo() {}), "source(@12):fn foo () {}");
assert_eq!(source_of!(static X: u32 = 1;), "source(@13):static X : u32 = 1 ;");
let source = combine!(output=string input=("source:" __SOURCE__(of=(my token))));
assert_eq!(source, "source:my token");§String functions
__HASH__(string=(tokens...) [alphabet=[chars...]]): The hash of thetokenswhen converted to a string, by default as a zero-padding lowercase hexadecimal string.tokensmay contain nested function calls. Ifalphabetis specified, the hash is converted to a string using the characters in thealphabet. No zero padding is applied in this case.
let hash = combine!(output=string input=("hash:" __HASH__(string=(__LENGTH__(string="a")))));
assert_eq!(hash, "hash:65cd25028f98f158");
let hash = combine!(output=string input=("hash:" __HASH__(string=(1))));
assert_eq!(hash, "hash:65cd25028f98f158");
let hash = combine!(output=string input=("hash:" __HASH__(string=(1) alphabet=[0-9a-f])));
assert_eq!(hash, "hash:65cd25028f98f158");
let hash = combine!(output=string input=("hash:" __HASH__(string=(1) alphabet=[a-z])));
assert_eq!(hash, "hash:cywprjlaqhbdie");__LENGTH__(string=(tokens...)): The length of thetokenswhen converted to a string.
let length = combine!(output=string input=("length:" __LENGTH__(string="a")));
assert_eq!(length, "length:1");__SUBSTRING__(input=(input) start=start end=end length=length): The substring of theinputfrom thestartto theendindex (exclusive) orlengthcharacters from thestartindex. Ifendorlengthwould exceed the length of the input, the substring is truncated to the end of the input. Ifendorlengthare missing, the substring is the entire input fromstartto the end of the input.
let substring = combine!(output=string input=("substring:" __SUBSTRING__(input="abc" start=1 end=2)));
assert_eq!(substring, "substring:b");
let substring = combine!(output=string input=("substring:" __SUBSTRING__(input="abc" start=1 length=2)));
assert_eq!(substring, "substring:bc");__PAD__(input=(input) length=length left=(padding...) right=(padding...)): Pad theinputto thelengthwith thepadding...(converted to a string).
let pad = combine!(output=string input=("pad:" __PAD__(input=123 length=5 left=0)));
assert_eq!(pad, "pad:00123");§Pattern functions
__REPLACE__(input=(input) pattern=(pattern...)|[chars...] replacement=(replacement)): Replace all occurrences of thepattern...(converted to a string) in theinputwith thereplacement. Ifreplacementis missing or empty, the pattern is removed.
Note: pattern may be specified as a regex-like character group. Use square
brackets to specify a character group.
let replace = combine!(output=string input=("replace:" __REPLACE__(input=(a b c) pattern=(b) replacement=(x))));
assert_eq!(replace, "replace:axc");
// Remove non-alnum characters
let translate = combine!(output=string input=("replace:" __REPLACE__(input="⚠️ thx 1138" pattern=[^a-zA-Z0-9] replacement="")));
assert_eq!(translate, "replace:thx1138");__TRANSLATE__(input=(input) pattern=(pattern...)|[chars...] replacement=(replacement)|[chars...]): Replace all occurrences of thepattern...(converted to a string) characters in theinputwith thereplacementcharacters. If replacement is missing or empty, the character is removed. Otherwise, the replacement character is selected from the replacement string in order (repeating the last character if necessary).
Note: pattern and replacement may be specified as regex-like character
groups. Use square brackets to specify a character group.
// Deletes all digits
let translate = combine!(output=string input=("translate:" __TRANSLATE__(input=(thx 1138) pattern=(0 1 2 3 4 5 6 7 8 9))));
assert_eq!(translate, "translate:thx");
// Uppercase all ASCII letters
let translate = combine!(output=string input=("translate:" __TRANSLATE__(input=(thx 1138) pattern=[a-z] replacement=[A-Z])));
assert_eq!(translate, "translate:THX1138");__TRIM__(input=(input) left=(padding...)|[chars...] right=(padding...)|[chars...]): Trim theinputof theleftandrightpatterns.
// Note: because we are using escaped characters, we need to put them in a string
let trim = combine!(output=string input=("trim:" __TRIM__(input=" thx 1138 " left=[" \n\t"] right=[" \n\t"])));
assert_eq!(trim, "trim:thx 1138");
let trim = combine!(output=string input=("trim:" __TRIM__(input=" thx 1138 " left=[' '] right=[' '])));
assert_eq!(trim, "trim:thx 1138");__CONTAINS__(input=(input) pattern=(pattern...)|[chars...]): Check if theinputcontains thepattern.... If so, returns1, otherwise0.
let contains = combine!(output=isize input=("contains:" __CONTAINS__(input="thx 1138" pattern=[0-9])));
assert_eq!(contains, 1);
let contains = combine!(output=isize input=("contains:" __CONTAINS__(input="thx 1138" pattern=[aeiou])));
assert_eq!(contains, 0);
let contains = combine!(output=isize input=("contains:" __CONTAINS__(input="thx 1138" pattern="1138")));
assert_eq!(contains, 1);__STREQ__(a=(tokens...) b=(tokens...)): Compareaandbas strings. Returns1if equal,0otherwise.
let eq = combine!(output=isize input=("eq:" __STREQ__(a="thx 1138" b="thx 1138")));
assert_eq!(eq, 1);
let eq = combine!(output=isize input=("eq:" __STREQ__(a="thx 1138" b="thx 1139")));
assert_eq!(eq, 0);§Comparison functions
__IF__(test=(tokens...) then=(tokens...) else=(tokens...)): If test is non-zero, emit thethentokens, otherwise emit theelsetokens.
// Outputs true or false idents
assert!(combine!(output=ident input=(__IF__(test=1 then="true" else="false"))));
assert!(combine!(output=ident input=(__IF__(test=(__CONTAINS__(input="thx 1138" pattern="1138")) then="true" else="false"))));__LT__(a=(tokens...) b=(tokens...)): Comparea<bas numbers.__GT__(a=(tokens...) b=(tokens...)): Comparea>bas numbers.__LE__(a=(tokens...) b=(tokens...)): Comparea<=bas numbers.__GE__(a=(tokens...) b=(tokens...)): Comparea>=bas numbers.__EQ__(a=(tokens...) b=(tokens...)): Comparea==bas numbers.__NE__(a=(tokens...) b=(tokens...)): Comparea!=bas numbers.
let lt = combine!(output=isize input=(__LT__(a=1 b=2)));
assert_eq!(lt, 1);
let gt = combine!(output=isize input=(__GT__(a=1 b=2)));
assert_eq!(gt, 0);§Math functions
__ADD__(a=(tokens...) b=(tokens...)): Add theaandbtokens.__SUB__(a=(tokens...) b=(tokens...)): Subtract thebfroma.__MUL__(a=(tokens...) b=(tokens...)): Multiply theaandbtokens.__DIV__(a=(tokens...) b=(tokens...)): Divide theabyb.__AND__(a=(tokens...) b=(tokens...)): Bitwise AND theaandbtokens.__OR__(a=(tokens...) b=(tokens...)): Bitwise OR theaandbtokens.
assert_eq!(combine!(output=isize input=("add:" __ADD__(a=1 b=2))), 3);
assert_eq!(combine!(output=isize input=("sub:" __SUB__(a=1 b=2))), -1);
assert_eq!(combine!(output=isize input=("mul:" __MUL__(a=1 b=2))), 2);
assert_eq!(combine!(output=isize input=("div:" __DIV__(a=1 b=2))), 0);
assert_eq!(combine!(output=isize input=("and:" __AND__(a=1 b=2))), 0);
assert_eq!(combine!(output=isize input=("or:" __OR__(a=1 b=2))), 3);§Conversion functions
__TOSTRING__(input=(tokens...)): Convert thetokensto a string literal.__RAW__(input=(tokens...)): Convert thetokensto a string, ignoring nested function calls (recommended for user input).__TOIDENT__(input=(tokens...)): Convert thetokensto an ident, stripping invalid characters.__TONUMBER__(input=(tokens...)): Convert thetokensto a numeric literal.
let string = combine!(output=string input=("string:" __TOSTRING__(input=(a b c))));
assert_eq!(string, "string:abc");
let number = combine!(output=isize input=("number:" __TONUMBER__(input=(1 2 3))));
assert_eq!(number, 123);
let number = combine!(output=isize input=("number:" __TONUMBER__(input=("0x" 123 _ 456))));
assert_eq!(number, 0x123456);
let ident = combine!(output=string input=("ident:" __TOIDENT__(input=(a $ b _ c))));
assert_eq!(ident, "ident:ab_c");
let raw = combine!(output=string input=("raw:" __RAW__(input=(__TOSTRING__(input=(a b c))))));
assert_eq!(raw, "raw:__TOSTRING__input=abc");