Rust macros are Token Trees and provide namespace hygiene, so not quite "text in, text out".
You know, I was going to say tokens rather than text, but the AI discourse has me so burnt out on the term that I edited it. Regardless, one can emit unsafe blocks from a macro, provided they are valid tokens.
Token list, not token trees. There are official libraries for parsing token stream as rust code but you can parse it as anything (eg json, html) if you want to.