Replies: 5 comments 2 replies
-
Utilizing module namespacing is idiomatic and common in Rust, just as it is in Python and many other languages. Think As a user, I don't think that But you found an interesting edge case with derive macros, as they don't have access to the name of the current module (AFAIK). Can you try researching this general problem or reporting it to |
Beta Was this translation helpful? Give feedback.
-
|
I have recently run into this problem as well, however, I don't believe this is an issue with the Rust provides an extremely powerful macro system that allows us to fill these gaps easily! Anyone not familiar with it should definitely learn the basics. I have shared the /// # Description
/// Adds a ts_export rename attribute in the format of: "{literal_name}Entity"
/// to the given entity. This macro assumes the `[sea_orm(table_name = "...")]` or
/// `[sea_orm(entity = "...")]` attribute is present and will error if not found.
/// # Attribute Arguments
/// Optional string literal, if given the literal provided will be inserted after the
/// identified literal name instead of the default "Entity".
#[proc_macro_attribute]
pub fn ts_rs_export_sea_orm_entity_name(args: TokenStream, input: TokenStream) -> TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
match ts_rs_export_sea_orm_entity_name::expand(derive_input, args) {
Ok(token_stream) => token_stream.into(),
Err(e) => e.to_compile_error().into(),
}
}
pub(crate) fn expand(
mut input: DeriveInput,
args: TokenStream,
) -> syn::Result<proc_macro2::TokenStream> {
let mut literal_name = None;
// Append attribute argument or default value of "Entity"
let entity_name_append = syn::parse::<Option<LitStr>>(args)?
.map(|x| x.value())
.unwrap_or(String::from("Entity"));
input
.attrs
.iter()
.filter(|attr| attr.path().is_ident("sea_orm"))
.try_for_each(|attr| {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("table_name") || meta.path.is_ident("entity") {
literal_name = Some(meta.value()?.parse::<LitStr>()?);
}
Ok(())
})
})?;
let literal_name = literal_name.ok_or(syn::Error::new(
input.ident.span(),
"Must provide a #[sea_orm(table_name = \"...\")] or #[sea_orm(entity = \"...\")] attribute",
))?;
// Get the string value stored in the literal
let ts_name = LitStr::new(
&(convert_snake_to_pascal(&literal_name.value()) + &entity_name_append),
literal_name.span(),
);
let mut attrs: Vec<Attribute> = vec![
parse_quote_spanned!(input.ident.span() => #[derive(::ts_rs::TS)]),
parse_quote_spanned!(literal_name.span() => #[ts(export, rename = #ts_name)]),
];
let input_attrs: &mut Vec<Attribute> = input.attrs.as_mut();
input_attrs.append(&mut attrs);
Ok(quote!(#input))
}
/// #Description
/// Convert a snake case string to pascal case
pub fn convert_snake_to_pascal(input: &str) -> String {
// We will over allocate here but not by much in most cases
let mut ret = String::with_capacity(input.len());
let mut chars = input.chars();
// Ensure the first character is upper case
if let Some(c) = chars.next() {
ret.push_str(&c.to_uppercase().to_string());
} else {
return ret;
};
while let Some(c) = chars.next() {
if c == '_' {
if let Some(next_char) = chars.next() {
ret.push_str(&next_char.to_uppercase().to_string())
} else {
break;
}
} else {
ret.push(c)
}
}
ret
}
|
Beta Was this translation helpful? Give feedback.
-
|
I am using seaorm with Specta and I ran into the same issue as well. I ended up solving this by having a small post processing script that modifies the codegen output: generate-entities:
#!/usr/bin/env sh
sea-orm-cli generate entity \
-u $DATABASE_URL \
-o crates/seaorm-entities/src/entities \
--with-serde both --model-extra-derives "specta::Type" \
--enum-extra-derives "specta::Type" \
--model-extra-attributes "specta(rename = \"___model_name___\")"
for file in crates/seaorm-entities/src/entities/*.rs; do
if [ -f "$file" ] && [ "$(basename "$file")" != "mod.rs" ] && [ "$(basename "$file")" != "prelude.rs" ] && [ "$(basename "$file")" != "sea_orm_active_enums.rs" ]; then
table_name=$(basename "$file" .rs)
# Convert snake_case to PascalCase for the specta rename
pascal_name=$(echo "$table_name" | perl -pe 's/(^|_)([a-z])/\U$2/g')
model_name=$(echo "$pascal_name"Model)
echo "Adding specta rename for $table_name -> $model_name"
perl -i -pe "s|(#\[specta\(rename = \"___model_name___\"\)\])|$1#[specta(rename = \"$model_name\")]|" "$file"
fi
done |
Beta Was this translation helpful? Give feedback.
-
|
I'm not sure why people are reaching for post processing scripts when Rust comes with the tools to do this (in a much more stable manner). Additionally it adds extra dependencies and confusion to your code base, why create more work for yourself?? I use docker to compile and host my Rust API's so it would be a lot more work to use an external tool like this, I believe my points are still relevant for anyone though. I responded to this since I got an email notification, I do believe my previous response from June 20th should be marked as the solution. |
Beta Was this translation helpful? Give feedback.
-
|
Can't you just use |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I fell in love with Ruby on Rails in 2010. It was so intuitive.
I mainly develop in Rust and Dart now, but have to inter-operate with TypeScript, because the world imposes it on us.
Why does sea-orm name all structs,
Model? This is crazy. Everywhere they are constructed in code, we lose the wonderful typing of Rust.Why not NewType or type alias, you ask?
Well, what if I derive
ts::TSon the structs with--model-extra-derives?There's no dynamic variables to interpolate with the struct name, so they all generate typescript interfaces named
Modeland overwrite each other!That's horrendous in my opinion. How did this idea to name all structs get even one step past the drawing board?
So I wind up with this monstrosity (have to remove generated enums that were generated ... why? ... because I used predefined ones in the migrations anyway):
justfilerecipe:rename-ts-exports.awk:Is this maintainable? No.
Is it real world? Yes. How many thousands of devs have even more complex projects than I do, and will have to write this kind of spaghetti just to fight the sea-orm entity generator?
Beta Was this translation helpful? Give feedback.
All reactions