use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use crate::ir::types::{Cardinality, RustType, TypeRef};
const RUST_KEYWORDS: &[&str] = &[
"abstract", "as", "async", "await", "become", "box", "break", "const", "continue", "crate",
"do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in",
"let", "loop", "macro", "match", "mod", "move", "mut", "override", "priv", "pub", "ref",
"return", "self", "Self", "static", "struct", "super", "trait", "true", "try", "type",
"typeof", "union", "unsafe", "unsized", "use", "virtual", "where", "while", "yield",
];
pub fn make_ident(name: &str) -> Ident {
if RUST_KEYWORDS.contains(&name) {
Ident::new_raw(name, Span::call_site())
} else {
Ident::new(name, Span::call_site())
}
}
pub fn type_ref_tokens(type_ref: &TypeRef) -> TokenStream {
match type_ref {
TypeRef::Named(name) => {
let ident = make_ident(name);
quote! { #ident }
}
TypeRef::Builtin(RustType::Bool) => quote! { bool },
TypeRef::Builtin(
RustType::String | RustType::Decimal | RustType::Date | RustType::DateTime,
) => {
quote! { String }
}
}
}
pub fn apply_cardinality(base: TokenStream, cardinality: &Cardinality) -> TokenStream {
match cardinality {
Cardinality::Required => base,
Cardinality::Optional => quote! { Option<#base> },
Cardinality::Vec | Cardinality::BoundedVec(_) => quote! { Vec<#base> },
}
}
pub fn standard_derives() -> TokenStream {
quote! {
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn make_ident_normal() {
let id = make_ident("my_field");
assert_eq!(id.to_string(), "my_field");
}
#[test]
fn make_ident_keyword_type() {
let id = make_ident("type");
assert_eq!(id.to_string(), "r#type");
}
#[test]
fn make_ident_keyword_ref() {
let id = make_ident("ref");
assert_eq!(id.to_string(), "r#ref");
}
#[test]
fn type_ref_named() {
let ts = type_ref_tokens(&TypeRef::Named("Max35Text".to_owned()));
assert_eq!(ts.to_string(), "Max35Text");
}
#[test]
fn type_ref_builtin_string() {
let ts = type_ref_tokens(&TypeRef::Builtin(RustType::String));
assert_eq!(ts.to_string(), "String");
}
#[test]
fn type_ref_builtin_bool() {
let ts = type_ref_tokens(&TypeRef::Builtin(RustType::Bool));
assert_eq!(ts.to_string(), "bool");
}
#[test]
fn type_ref_builtin_decimal_is_string() {
let ts = type_ref_tokens(&TypeRef::Builtin(RustType::Decimal));
assert_eq!(ts.to_string(), "String");
}
#[test]
fn apply_cardinality_required() {
let base = quote! { String };
let ts = apply_cardinality(base, &Cardinality::Required);
assert_eq!(ts.to_string(), "String");
}
#[test]
fn apply_cardinality_optional() {
let base = quote! { String };
let ts = apply_cardinality(base, &Cardinality::Optional);
assert_eq!(ts.to_string(), "Option < String >");
}
#[test]
fn apply_cardinality_vec() {
let base = quote! { String };
let ts = apply_cardinality(base, &Cardinality::Vec);
assert_eq!(ts.to_string(), "Vec < String >");
}
#[test]
fn apply_cardinality_bounded_vec() {
let base = quote! { String };
let ts = apply_cardinality(base, &Cardinality::BoundedVec(10));
assert_eq!(ts.to_string(), "Vec < String >");
}
}