Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose a way to make static or constant values #16

Open
schneems opened this issue Oct 31, 2024 · 1 comment
Open

Expose a way to make static or constant values #16

schneems opened this issue Oct 31, 2024 · 1 comment

Comments

@schneems
Copy link
Collaborator

Problem

In a refactor away from the "output" module of the ruby buildpack, I was left wanting to use a static value like DEBUG_INFO. Which is effectively style::important("Debug info:"). I can build this as a local variable i.e.

let debug_info = bullet_stream::style::important("Debug info:");

But I cannot expose it as a top level const or static i.e. this doesn't work:

// Does not work
const DEBUG_INFO: String = bullet_stream::style::important("Debug info:");

It's nice to define helpers like this in one place and re-use them.

OnceCell

If we don't mind exposing this as a function then it's not hard to do via OnceCell, but it looks a little off. i.e. this is the original

output
    .bullet(DEBUG_INFO)
    .done()

Versus with the function it's just not as clear that it's simple string and not doing some:

output
    .bullet(debug_info())
    .done()

Compile time macro

It's possible to expose a macro that allows the end user to define their own const values like:

// This CAN work
const DEBUG_INFO: &str = important!("Debug info");
Details

The implementation is pretty easy, it looks like this:

macro_rules! important {
    ($input:expr) => {
        concat!("\x1B[1;36m", $input, "\x1B[0m")
    };
}

It works because the string is known at compile time. However we cannot use indirection internally:

macro_rules! important {
    ($input:expr) => {
        concat!(BOLD_CYAN, $input, "\x1B[0m")
    };
}

Fails with:

 1  error: expected a literal
   --> buildpacks/ruby/src/user_errors.rs:22:17
    |
 22 |         concat!(BOLD_CYAN, $input, "\x1B[0m")
    |                 ^^^^^^^^^
 ...
 25 | const DEBUG_INFO: &str = important!("Debug info");
    |                          ------------------------ in this macro invocation

There's also https://crates.io/crates/const_format and https://www.youtube.com/watch?v=zXCr1BH5Y-4 is a great talk if you've not seen it. I benchmarked the impact of it on compile time and it's negligible https://gist.github.com/schneems/04512c87332ab447b34b545497310b4a.

Or an all-macro approach works as well:

macro_rules! bold_cyan {
    () => {
        "\x1B[1;36m"
    };
}

macro_rules! reset {
    () => {
        "\x1B[0m"
    };
}

macro_rules! important {
    ($input:expr) => {
        concat!(bold_cyan!(), $input, reset!())
    };
}

The only possibly confusing might be if people expect it to also do formatting as well i.e. important!("{}, path.display()) (etc.) Which could be useful. Ideally we would have both maybe? It's a little annoying calling important(format!("{}, path.display())) but it's the difference between "not really possible" and "slightly less typing".

I think I would also want this under a different module something like bullet_stream::const::important or bullet_stream::literal::important. It's a bit of a specialty case, but I think exposing it beats trying to hard code constants like I did in the ruby buildpack "output" module.

@schneems
Copy link
Collaborator Author

Related PR heroku/buildpacks-ruby#338

schneems added a commit that referenced this issue Nov 4, 2024
Outlined in #16 this adds a macro that can work with constant declarations. It has caveats outlined in the docs.
schneems added a commit that referenced this issue Nov 4, 2024
Outlined in #16 this adds a macro that can work with constant declarations. It has caveats outlined in the docs.
schneems added a commit that referenced this issue Nov 4, 2024
Issue #16 outlined how the current API is not usable to create const variables.

```rust
// Does not work
const DEBUG_INFO: String = bullet_stream::style::important("Debug info:");
```

This adds a macro that can work with constant declarations. It has caveats outlined in the docs.

```rust
const DEBUG_INFO: &str = important_lit!("Debug info:"); 
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant