feat: Initial limited implementation
This initial implementation only supports structs with named field and basic enum. It also does not support all the available primitives, although when needed these are very easy to implement.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/target
|
||||
Generated
+394
@@ -0,0 +1,394 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dissimilar"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.43.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fdb647ebde000f43b5b53f773c30cf9b0cb4300453208713fa38b2c70935a0"
|
||||
dependencies = [
|
||||
"console",
|
||||
"once_cell",
|
||||
"similar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.175"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
|
||||
[[package]]
|
||||
name = "lua_typed"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"insta",
|
||||
"lua_typed_macro",
|
||||
"trybuild",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lua_typed_macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.220"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ceecad4c782e936ac90ecfd6b56532322e3262b14320abf30ce89a92ffdbfe22"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.220"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddba47394f3b862d6ff6efdbd26ca4673e3566a307880a0ffb98f274bbe0ec32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.220"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60e1f3b1761e96def5ec6d04a6e7421c0404fa3cf5c0155f1e2848fae3d8cc08"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56177480b00303e689183f110b4e727bb4211d692c62d4fcd16d02be93077d40"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-triple"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790"
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64"
|
||||
|
||||
[[package]]
|
||||
name = "trybuild"
|
||||
version = "1.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ded9fdb81f30a5708920310bfcd9ea7482ff9cba5f54601f7a19a877d5c2392"
|
||||
dependencies = [
|
||||
"dissimilar",
|
||||
"glob",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"target-triple",
|
||||
"termcolor",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "lua_typed"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[workspace]
|
||||
members = ["lua_typed_macro"]
|
||||
|
||||
[dependencies]
|
||||
lua_typed_macro = { path = "./lua_typed_macro/" }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "1.43.2"
|
||||
trybuild = { version = "1.0.111", features = ["diff"] }
|
||||
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev.package.similar]
|
||||
opt-level = 3
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "lua_typed_macro"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.101"
|
||||
quote = "1.0.40"
|
||||
syn = "2.0.106"
|
||||
convert_case = "0.8.0"
|
||||
@@ -0,0 +1,255 @@
|
||||
#![feature(iterator_try_collect)]
|
||||
use std::ops::Deref;
|
||||
|
||||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{ToTokens, quote};
|
||||
use syn::{DeriveInput, LitStr, Token, parse_macro_input, spanned::Spanned};
|
||||
|
||||
struct StructField {
|
||||
name: syn::Ident,
|
||||
ty: syn::Type,
|
||||
case: Option<Case<'static>>,
|
||||
default: bool,
|
||||
}
|
||||
|
||||
impl ToTokens for StructField {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let mut name = self.name.to_string();
|
||||
|
||||
if let Some(case) = self.case {
|
||||
name = name.to_case(case);
|
||||
}
|
||||
|
||||
let ty = &self.ty;
|
||||
|
||||
let default = if self.default { "?" } else { "" };
|
||||
|
||||
let format = format!("---@field {} {{}}{}\n", name, default);
|
||||
|
||||
tokens.extend(quote! {
|
||||
format!(#format, <#ty as Typed>::type_name())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct Struct {
|
||||
name: syn::Ident,
|
||||
fields: Vec<StructField>,
|
||||
}
|
||||
|
||||
impl ToTokens for Struct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let name = &self.name;
|
||||
let fields = &self.fields;
|
||||
|
||||
tokens.extend(quote! {
|
||||
fn generate_header() -> Option<String> {
|
||||
Some(format!("---@class {}\n", <#name as Typed>::type_name()))
|
||||
}
|
||||
|
||||
fn generate_members() -> Option<String> {
|
||||
let mut output = String::new();
|
||||
|
||||
#(
|
||||
output += &#fields;
|
||||
)*
|
||||
|
||||
Some(output)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct BasicEnumVariant {
|
||||
name: syn::Ident,
|
||||
case: Option<Case<'static>>,
|
||||
}
|
||||
|
||||
impl ToTokens for BasicEnumVariant {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let mut name = self.name.to_string();
|
||||
|
||||
if let Some(case) = self.case {
|
||||
name = name.to_case(case);
|
||||
}
|
||||
|
||||
let format = format!("---| \"{}\"\n", name);
|
||||
|
||||
tokens.extend(quote! {
|
||||
#format
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct BasicEnum {
|
||||
name: syn::Ident,
|
||||
variants: Vec<BasicEnumVariant>,
|
||||
}
|
||||
|
||||
impl ToTokens for BasicEnum {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let name = &self.name;
|
||||
let variants = &self.variants;
|
||||
|
||||
tokens.extend(quote! {
|
||||
fn generate_header() -> Option<String> {
|
||||
Some(format!("---@alias {}\n", <#name as Typed>::type_name()))
|
||||
}
|
||||
|
||||
fn generate_members() -> Option<String> {
|
||||
let mut output = String::new();
|
||||
|
||||
#(
|
||||
output += &#variants;
|
||||
)*
|
||||
|
||||
Some(output)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Typed, attributes(serde))]
|
||||
pub fn typed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = &ast.ident;
|
||||
|
||||
let mut case = None;
|
||||
|
||||
for attr in &ast.attrs {
|
||||
if attr.path().is_ident("serde")
|
||||
&& let Err(err) = attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("rename_all") {
|
||||
let value = meta.value()?;
|
||||
let case_name: LitStr = value.parse()?;
|
||||
|
||||
case = Some(match case_name.value().deref() {
|
||||
"lowercase" => Ok(convert_case::Case::Lower),
|
||||
"UPPERCASE" => Ok(convert_case::Case::Upper),
|
||||
"PascalCase" => Ok(convert_case::Case::Pascal),
|
||||
"camelCase" => Ok(convert_case::Case::Camel),
|
||||
"snake_case" => Ok(convert_case::Case::Snake),
|
||||
"SCREAMING_SNAKE_CASE" => Ok(convert_case::Case::Constant),
|
||||
"kebab-case" => Ok(convert_case::Case::Kebab),
|
||||
"SCREAMING-KEBAB-CASE" => Ok(convert_case::Case::Cobol),
|
||||
_ => Err(syn::Error::new(
|
||||
case_name.span(),
|
||||
"Typed does not support this type of rename",
|
||||
)),
|
||||
}?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
{
|
||||
return err.into_compile_error().into();
|
||||
}
|
||||
}
|
||||
|
||||
let type_name_fn = quote! {
|
||||
fn type_name() -> ::std::string::String {
|
||||
stringify!(#name).to_string()
|
||||
}
|
||||
};
|
||||
|
||||
let test: TokenStream2 = match ast.data {
|
||||
syn::Data::Struct(data_struct) => {
|
||||
if data_struct.fields.iter().any(|field| field.ident.is_none()) {
|
||||
return syn::Error::new(
|
||||
data_struct.fields.span(),
|
||||
"Tuple structs are not supported by Typed",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let mut fields = Vec::new();
|
||||
for field in data_struct.fields {
|
||||
let mut default = false;
|
||||
for attr in &field.attrs {
|
||||
if attr.path().is_ident("serde")
|
||||
&& let Err(err) = attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("default") {
|
||||
default = true;
|
||||
if meta.input.peek(Token![=]) {
|
||||
meta.input.parse::<Token![=]>()?;
|
||||
meta.input.parse::<LitStr>()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
{
|
||||
return err.into_compile_error().into();
|
||||
}
|
||||
}
|
||||
|
||||
fields.push(StructField {
|
||||
name: field.ident.expect("We already checked that ident is some"),
|
||||
ty: field.ty,
|
||||
case,
|
||||
default,
|
||||
});
|
||||
}
|
||||
|
||||
Struct {
|
||||
name: name.to_owned(),
|
||||
fields,
|
||||
}
|
||||
.into_token_stream()
|
||||
}
|
||||
syn::Data::Enum(data_enum) => {
|
||||
let errors: TokenStream2 = data_enum
|
||||
.variants
|
||||
.iter()
|
||||
.filter(|variant| !variant.fields.is_empty())
|
||||
.map(|variant| {
|
||||
syn::Error::new(
|
||||
variant.fields.span(),
|
||||
"Only basic enums are supported by Typed",
|
||||
)
|
||||
.into_compile_error()
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !errors.is_empty() {
|
||||
return errors.into();
|
||||
}
|
||||
|
||||
BasicEnum {
|
||||
name: name.to_owned(),
|
||||
variants: data_enum
|
||||
.variants
|
||||
.into_iter()
|
||||
.map(|variant| BasicEnumVariant {
|
||||
name: variant.ident,
|
||||
case,
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
.into_token_stream()
|
||||
}
|
||||
syn::Data::Union(data_union) => {
|
||||
return syn::Error::new(
|
||||
data_union.union_token.span,
|
||||
"Unions are not supported by Typed",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
impl ::lua_typed::Typed for #name {
|
||||
#type_name_fn
|
||||
|
||||
#test
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
pub use lua_typed_macro::Typed;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub trait Typed {
|
||||
fn type_name() -> String;
|
||||
|
||||
fn generate_header() -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn generate_members() -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn generate_full() -> Option<String> {
|
||||
let mut output = String::new();
|
||||
|
||||
let Some(header) = &Self::generate_header() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
output += header;
|
||||
if let Some(members) = &Self::generate_members() {
|
||||
output += members;
|
||||
}
|
||||
|
||||
Some(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed for bool {
|
||||
fn type_name() -> String {
|
||||
"boolean".into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed for u8 {
|
||||
fn type_name() -> String {
|
||||
"integer".into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed for String {
|
||||
fn type_name() -> String {
|
||||
"string".into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Typed> Typed for Option<T> {
|
||||
fn type_name() -> String {
|
||||
format!("{}?", <T as Typed>::type_name())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Typed> Typed for Vec<T> {
|
||||
fn type_name() -> String {
|
||||
format!("{}[]", <T as Typed>::type_name())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Typed, V: Typed> Typed for HashMap<K, V> {
|
||||
fn type_name() -> String {
|
||||
format!(
|
||||
"table<{}, {}>",
|
||||
<K as Typed>::type_name(),
|
||||
<V as Typed>::type_name()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
use lua_typed::Typed;
|
||||
|
||||
#[derive(Typed)]
|
||||
#[repr(u8)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Priority {
|
||||
Min = 1,
|
||||
Low,
|
||||
Default,
|
||||
High,
|
||||
Max,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn priority() {
|
||||
insta::assert_snapshot!(<Priority as Typed>::generate_full().unwrap(), @r#"
|
||||
---@alias Priority
|
||||
---| "min"
|
||||
---| "low"
|
||||
---| "default"
|
||||
---| "high"
|
||||
---| "max"
|
||||
"#);
|
||||
}
|
||||
|
||||
#[derive(Typed)]
|
||||
pub struct Action {
|
||||
// #[serde(flatten)]
|
||||
// pub action: ActionType,
|
||||
pub label: String,
|
||||
pub clear: Option<bool>,
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn action() {
|
||||
// insta::assert_snapshot!(<Action as Typed>::generate_full().unwrap(), @r#"
|
||||
// ---@class Action
|
||||
// ---@field action "broadcast"
|
||||
// ---@field extras table<string, string>?
|
||||
// ---@field label string?
|
||||
// ---@clear clear bool?
|
||||
// "#);
|
||||
// }
|
||||
|
||||
#[derive(Typed)]
|
||||
pub struct Notification {
|
||||
pub title: String,
|
||||
pub message: Option<String>,
|
||||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
pub priority: Option<Priority>,
|
||||
#[serde(default)]
|
||||
pub actions: Vec<Action>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notification() {
|
||||
insta::assert_snapshot!(<Notification as Typed>::generate_full().unwrap(), @r"
|
||||
---@class Notification
|
||||
---@field title string
|
||||
---@field message string?
|
||||
---@field tags string[]?
|
||||
---@field priority Priority?
|
||||
---@field actions Action[]?
|
||||
");
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
use lua_typed::Typed;
|
||||
|
||||
#[derive(Typed)]
|
||||
enum Test {
|
||||
A,
|
||||
B(String),
|
||||
C(u8),
|
||||
D { test: f32 },
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,17 @@
|
||||
error: Only basic enums are supported by Typed
|
||||
--> tests/ui/complex_struct.rs:6:6
|
||||
|
|
||||
6 | B(String),
|
||||
| ^^^^^^^^
|
||||
|
||||
error: Only basic enums are supported by Typed
|
||||
--> tests/ui/complex_struct.rs:7:6
|
||||
|
|
||||
7 | C(u8),
|
||||
| ^^^^
|
||||
|
||||
error: Only basic enums are supported by Typed
|
||||
--> tests/ui/complex_struct.rs:8:7
|
||||
|
|
||||
8 | D { test: f32 },
|
||||
| ^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
use lua_typed::Typed;
|
||||
|
||||
#[derive(Typed)]
|
||||
#[serde(rename_all = "invalid/case")]
|
||||
enum Test {
|
||||
HelloWorld,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Typed does not support this type of rename
|
||||
--> tests/ui/invalid_rename.rs:4:22
|
||||
|
|
||||
4 | #[serde(rename_all = "invalid/case")]
|
||||
| ^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,6 @@
|
||||
use lua_typed::Typed;
|
||||
|
||||
#[derive(Typed)]
|
||||
pub struct Test(u8);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Tuple structs are not supported by Typed
|
||||
--> tests/ui/tuple_struct.rs:4:16
|
||||
|
|
||||
4 | pub struct Test(u8);
|
||||
| ^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
use lua_typed::Typed;
|
||||
|
||||
#[derive(Typed)]
|
||||
union MyUnion {
|
||||
f1: u32,
|
||||
f2: f32,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Unions are not supported by Typed
|
||||
--> tests/ui/union.rs:4:1
|
||||
|
|
||||
4 | union MyUnion {
|
||||
| ^^^^^
|
||||
Reference in New Issue
Block a user