tailwindcss-to-rust 0.1.4

Generate Rust code from your compiled tailwind CSS
The `tailwindcss-to-rust` CLI tool generates Rust code that allows you to
refer to Tailwind classes from your Rust code. This means that any attempt to
use a nonexistent class will lead to a compile-time error, and you can use
code completion to list available classes.

**This tool works with version 3 of Tailwind.**

The generated code allows you to use Tailwind CSS classes in your Rust
frontend code with compile-time checking of names and code completion for
class names. These classes are grouped together based on the heading in the
Tailwind docs. It also generates code for the full list of Tailwind modifiers
like `lg`, `hover`, etc.

[**Check out the tailwindcss-to-rust-macros
crate**](https://crates.io/crates/tailwindcss-to-rust-macros) for the most
ergonomic way to use the code generated by this tool.

**Note that there is a known issue when using the generated code and macros
with Dioxus in some debug builds. For some reason the generated code ends up
exceeding the size of the stack at runtime, leading to weird memory errors.
Compiling with `--release` or calling `.to_string()` on the style strings
before passing them to Dioxus appears to prevent this.**

So instead of this:

```rust,ignore
let class = "pt-4 pb-2 text-whit";
```

You can write this:

```rust,ignore
let class = C![C.spc.pt_4 C.pb_2 C.type.text_white];
```

Note that the typo in the first example, **"text-whit"** (missing the "e")
would become a compile-time error if you wrote `C.type.text_whit`.

Here's a quick start recipe:

1. Install this tool by running:

   ```
   cargo install tailwindcss-to-rust
   ```

2. [Install the `tailwindcss` CLI
   tool](https://tailwindcss.com/docs/installation). You can install it with
   `npm` or `npx`, or you can [download a standalone binary from the
   tailwindcss repo](https://github.com/tailwindlabs/tailwindcss/releases).

3. Create a `tailwind.config.js` file with the tool by running:

   ```sh
   tailwindcss init
   ```

4. Edit this file however you like to add plugins or customize the generated
   CSS.

5. Create a CSS input file for Tailwind. For the purposes of this example we
   will assume that it's located at `css/tailwind.css`. The standard file
   looks like this:

   ```css
   @tailwind base;
   @tailwind components;
   @tailwind utilities;
   ```

6. Generate your Rust code by running:

   ```sh
   tailwindcss-to-rust \
        --tailwind-config tailwind.config.js \
        --input tailwind.css \
        --output src/css/generated.rs \
        --rustfmt
   ```

   **The `tailwindcss` executable must be in your `PATH` when you run
   `tailwindcss-to-rust` or you must provide the path to the executable in the
   `--tailwindcss` argument.**

7. Edit your `tailwind.config.js` file to look in your Rust files for Tailwind
   class names:

   ```js
   module.exports = {
     content: {
       files: ["index.html", "**/*.rs"],
       // You do need to copy this big block of code in, unfortunately.
       extract: {
         rs: (content) => {
           const rs_to_tw = (rs) => {
             if (rs.startsWith("two_")) {
               rs = rs.replace("two_", "2");
             }
             return rs
               .replaceAll("_of_", "/")
               .replaceAll("_p_", ".")
               .replaceAll("_", "-");
           };

           let classes = [];
           let class_re = /C\.[^ ]+\.([^\. ]+)\b/g;
           let mod_re = /(?:M\.([^\. ]+)\s*,\s*)+C\.[^ ]+\.([^\. ]+)\b/g;
           let matches = [...content.matchAll(mod_re)];
           if (matches.length > 0) {
             classes.push(
               ...matches.map((m) => {
                 let pieces = m.slice(1, m.length);
                 return pieces.map((p) => rs_to_tw(p)).join(":");
               })
             );
           }
           classes.push(
             ...[...content.matchAll(class_re)].map((m) => {
               return rs_to_tw(m[1]);
             })
           );
           return classes;
         },
       },
     },
     ...
   };
   ```

8. Hack, hack, hack ...

9. Regenerate your compiled Tailwind CSS file by running:

   ```sh
   tailwindcss --input css/tailwind.css --output css/tailwind_compiled.css`
   ```

10. Make sure to import the compiled CSS in your HTML:

    ```html
    <link data-trunk rel="css" href="/css/tailwind_compiled.css" />
    ```

In this example, I'm using [Trunk](https://trunkrs.dev/), which is a great
alternative to webpack for projects that want to use Rust -> WASM without any
node.js tooling. My `Trunk.toml` looks like this:

```toml
[build]
target = "index.html"
dist = "dist"

[[hooks]]
stage = "build"
# I'm not sure why we can't just invoke tailwindcss directly, but that doesn't
# seem to work for some reason.
command = "sh"
command_arguments = ["-c", "tailwindcss -i css/tailwind.css -o css/tailwind_compiled.css"]
```

When I run `trunk` I have to make sure to ignore that generated file:

```sh
trunk --ignore ./css/tailwind_compiled.css ...
```

The generated names consist of all the class names present in the CSS file,
except names that start with a dash (`-`), names that contain pseudo-elements,
like `.placeholder-opacity-100::-moz-placeholder`, and names that contain
modifiers like `lg` or `hover`. Names are transformed into Rust identifiers
using the following algorithm:

- All backslash escapes are removed entirely, for example in `.inset-0\.5`.
- All dashes (`-`) become underscores (`_`).
- All periods (`.`) become `_p_`, so `.inset-2\.5` becomes `inset_2_p_5`.
- All forward slashes (`/`) become `_of_`, so `.inset-2\/4` becomes
  `inset_2_of_4`.
- If a name _starts_ with a `2`, as in `2xl`, it becomes `two_`, so the `2xl`
  modifier becomes `two_xl`.

The generated code provides two structs containing all of the relevant
strings. The `C` struct contains all the classes, with each group of classes
as one field in the struct:

```rust,ignore
pub(crate) struct C {
    pub(crate) acc: Accessibility,
    pub(crate) anim: Animation,
    pub(crate) asp: Aspect,
    pub(crate) bg: Backgrounds,
    pub(crate) bor: Borders,
    pub(crate) eff: Effects,
    pub(crate) fil: Filters,
    pub(crate) fg: FlexAndGrid,
    pub(crate) intr: Interactivity,
    pub(crate) lay: Layout,
    pub(crate) lc: LineClamp,
    pub(crate) pro: Prose,
    pub(crate) siz: Sizing,
    pub(crate) spc: Spacing,
    pub(crate) svg: Svg,
    pub(crate) tbl: Tables,
    pub(crate) trn: Transforms,
    pub(crate) typ: Typography,
}
```

In your code, you can refer to classes with `C.typ.text_lg` or
`C.lay.flex`. If you have any custom classes, these will end in an "unknown"
group available from `C.unk`. Adding a way to put these custom classes in
other groups is a todo item.

The modifiers have their own struct, `M`, which contains one field per
modifiers, so it's used as `M.lg` or `M.hover`.

The best way to understand the generated structs is to simply open the
generated code file in your editor and look at it.

Then you can import these consts in your code and use them to refer to
Tailwind CSS class names with compile time checking:

```rust,ignore
element.set_class(C.asp.aspect_h_auto);
```