Skip to content

matous-volf/caseidae

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Caseidae

Convenient crate converting conventional casings of Rust identifiers, conveying correct context.

While there are several crates providing casing manipulation functionality, Caseidae is specifically designed for converting identifiers. It has built-in support for syn::Ident, handles lifetime annotations, and preserves prefixes like raw identifiers (r#) and underscores.

Additionally, general casing conversions don't convey the same meaning. For instance, instead of exposing a function named to_snake_case, this crate provides the same conversion under functions called to_variable_name, to_field_name, to_function_name and so on. A reader of the code doesn't have to think about what the casing looks like, or why it was chosen in the particular case (pun accidental) – the intent is clear.

Example

Imagine we are writing a wild macro that generates functions based on traits, such that

  • the function name is the trait name,
  • the parameters are the trait's associated constants,
  • the generic type parameters are the trait's functions,
  • the lifetime parameters are the trait's generic parameters.

For instance, turning

trait HeroHealth<Armor> {
    const MAGIC_RESISTANCE: u8 = 20;

    fn deal_damage();
    // `type` is a keyword, so we need a raw identifier.
    // Caseidae will handle it properly.
    fn r#type();
    // Caseidae will preserve leading underscores.
    fn __hidden_secret();
}

into

fn hero_health<'armor, DealDamage, r#Type, __HiddenSecret>(
    magic_resistance: u8
) {
    // ...
}

Then our code might look like this:

use caseidae::{ToFunctionName, ToLifetimeName, ToTypeName, ToVariableName};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::Ident;

fn trait_to_function(
    name: Ident,
    // The tuple is for name and type.
    associated_constants: &[(Ident, Ident)],
    generic_type_parameters: &[Ident],
    functions: &[Ident],
) -> TokenStream {
    let function_name = name.to_function_name();

    let function_lifetime_parameters = generic_type_parameters
        .iter()
        .map(|parameter| parameter.to_lifetime_name());

    let function_generic_type_parameters = functions
        .iter()
        .map(|function| function.to_type_name());

    let function_parameters = associated_constants
        .iter()
        .map(|(constant_name, constant_type)| {
            let parameter_name = constant_name.to_variable_name();
            quote! (#parameter_name: #constant_type)
        });

    quote! {
        fn #function_name<
            #(#function_lifetime_parameters,)*
            #(#function_generic_type_parameters,)*
        >(#(#function_parameters,)*) {
            // ...
        }
    }
}

You can test this in examples/macro.rs.

About

Convenient converters between letter casings of Rust identifiers. A mirror of https://codeberg.org/matous-volf/caseidae.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages