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:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
394
Cargo.lock
generated
Normal file
394
Cargo.lock
generated
Normal file
@@ -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
Cargo.toml
Normal file
20
Cargo.toml
Normal file
@@ -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
|
||||
13
lua_typed_macro/Cargo.toml
Normal file
13
lua_typed_macro/Cargo.toml
Normal file
@@ -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"
|
||||
255
lua_typed_macro/src/lib.rs
Normal file
255
lua_typed_macro/src/lib.rs
Normal file
@@ -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
src/lib.rs
Normal file
69
src/lib.rs
Normal file
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
66
tests/notification.rs
Normal file
66
tests/notification.rs
Normal file
@@ -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[]?
|
||||
");
|
||||
}
|
||||
5
tests/ui.rs
Normal file
5
tests/ui.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
}
|
||||
11
tests/ui/complex_struct.rs
Normal file
11
tests/ui/complex_struct.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use lua_typed::Typed;
|
||||
|
||||
#[derive(Typed)]
|
||||
enum Test {
|
||||
A,
|
||||
B(String),
|
||||
C(u8),
|
||||
D { test: f32 },
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
17
tests/ui/complex_struct.stderr
Normal file
17
tests/ui/complex_struct.stderr
Normal file
@@ -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 },
|
||||
| ^^^^^^^^^^^^^
|
||||
9
tests/ui/invalid_rename.rs
Normal file
9
tests/ui/invalid_rename.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use lua_typed::Typed;
|
||||
|
||||
#[derive(Typed)]
|
||||
#[serde(rename_all = "invalid/case")]
|
||||
enum Test {
|
||||
HelloWorld,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
5
tests/ui/invalid_rename.stderr
Normal file
5
tests/ui/invalid_rename.stderr
Normal file
@@ -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")]
|
||||
| ^^^^^^^^^^^^^^
|
||||
6
tests/ui/tuple_struct.rs
Normal file
6
tests/ui/tuple_struct.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use lua_typed::Typed;
|
||||
|
||||
#[derive(Typed)]
|
||||
pub struct Test(u8);
|
||||
|
||||
fn main() {}
|
||||
5
tests/ui/tuple_struct.stderr
Normal file
5
tests/ui/tuple_struct.stderr
Normal file
@@ -0,0 +1,5 @@
|
||||
error: Tuple structs are not supported by Typed
|
||||
--> tests/ui/tuple_struct.rs:4:16
|
||||
|
|
||||
4 | pub struct Test(u8);
|
||||
| ^^^^
|
||||
9
tests/ui/union.rs
Normal file
9
tests/ui/union.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use lua_typed::Typed;
|
||||
|
||||
#[derive(Typed)]
|
||||
union MyUnion {
|
||||
f1: u32,
|
||||
f2: f32,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
5
tests/ui/union.stderr
Normal file
5
tests/ui/union.stderr
Normal file
@@ -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