Skip to content

Commit 00865e0

Browse files
committed
Add warnings for unknown serde attributes
1 parent f34ab22 commit 00865e0

File tree

4 files changed

+121
-7
lines changed

4 files changed

+121
-7
lines changed

Cargo.lock

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cw-schema-derive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ proc-macro = true
99
[dependencies]
1010
heck = "0.5.0"
1111
itertools = { version = "0.13.0", default-features = false }
12+
owo-colors = { version = "4.0.0", features = ["supports-colors"] }
1213
proc-macro2 = "1.0.86"
1314
quote = "1.0.36"
1415
syn = { version = "2.0.72", features = ["full"] }

packages/cw-schema-derive/src/expand.rs

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,40 @@
11
// TODO: CLEAN ALL THIS SHIT UP WHAT THE FUCK IS THIS
22

33
use crate::bail;
4+
use owo_colors::{OwoColorize, Stream, Style};
45
use proc_macro2::TokenStream;
56
use quote::{format_ident, quote};
6-
use std::borrow::Cow;
7+
use std::{
8+
borrow::Cow,
9+
fmt::Display,
10+
io::{self, Write as _},
11+
};
712
use syn::{DataEnum, DataStruct, DataUnion, DeriveInput, Lit};
813

14+
fn print_warning(title: impl Display, content: impl Display) -> io::Result<()> {
15+
let mut sink = io::stderr();
16+
17+
macro_rules! apply_style {
18+
($style:ident => $content:expr) => {{
19+
//$content.if_supports_color(Stream::Stderr, |txt| txt.style($style))
20+
$content.style($style)
21+
}};
22+
}
23+
24+
let bold_yellow = Style::new().bold().yellow();
25+
let bold = Style::new().bold();
26+
let blue = Style::new().blue();
27+
28+
write!(sink, "{}", apply_style!(bold_yellow => "warning"))?;
29+
writeln!(sink, "{}", apply_style!(bold => format_args!(": {title}")))?;
30+
31+
writeln!(sink, "{}", apply_style!(blue => " | "))?;
32+
write!(sink, "{}", apply_style!(blue => " | "))?;
33+
writeln!(sink, "{content}")?;
34+
35+
Ok(())
36+
}
37+
938
type Converter = fn(&str) -> String;
1039

1140
fn case_converter(case: &syn::LitStr) -> syn::Result<Converter> {
@@ -32,11 +61,13 @@ fn case_converter(case: &syn::LitStr) -> syn::Result<Converter> {
3261
Ok(converter)
3362
}
3463

64+
#[inline]
3565
fn maybe_case_converter(case: Option<&syn::LitStr>) -> syn::Result<Converter> {
3666
case.map(case_converter)
3767
.unwrap_or_else(|| Ok(|txt: &str| txt.to_string()))
3868
}
3969

70+
#[inline]
4071
fn ident_adapter(converter: Converter) -> impl Fn(&syn::Ident) -> syn::Ident {
4172
move |ident: &syn::Ident| format_ident!("{}", converter(&ident.to_string()))
4273
}
@@ -63,6 +94,18 @@ impl SerdeContainerOptions {
6394
} else if meta.path.is_ident("untagged") {
6495
options.untagged = true;
6596
} else {
97+
print_warning(
98+
"unknown serde attribute",
99+
format!(
100+
"unknown attribute \"{}\"",
101+
meta.path
102+
.get_ident()
103+
.map(|ident| ident.to_string())
104+
.unwrap_or_else(|| "[error]".into())
105+
),
106+
)
107+
.unwrap();
108+
66109
// TODO: support other serde attributes
67110
//
68111
// For now we simply clear the buffer to avoid errors
@@ -118,6 +161,51 @@ impl ContainerOptions {
118161
}
119162
}
120163

164+
struct SerdeFieldOptions {
165+
rename: Option<syn::LitStr>,
166+
}
167+
168+
impl SerdeFieldOptions {
169+
fn parse(attributes: &[syn::Attribute]) -> syn::Result<Self> {
170+
let mut options = SerdeFieldOptions { rename: None };
171+
172+
for attribute in attributes
173+
.iter()
174+
.filter(|attr| attr.path().is_ident("serde"))
175+
{
176+
attribute.parse_nested_meta(|meta| {
177+
if meta.path.is_ident("rename") {
178+
options.rename = Some(meta.value()?.parse()?);
179+
} else {
180+
print_warning(
181+
"unknown serde attribute",
182+
format!(
183+
"unknown attribute \"{}\"",
184+
meta.path
185+
.get_ident()
186+
.map(|ident| ident.to_string())
187+
.unwrap_or_else(|| "[error]".into())
188+
),
189+
)
190+
.unwrap();
191+
192+
// TODO: support other serde attributes
193+
//
194+
// For now we simply clear the buffer to avoid errors
195+
let _ = meta
196+
.value()
197+
.map(|val| val.parse::<TokenStream>().unwrap())
198+
.unwrap_or_else(|_| meta.input.cursor().token_stream());
199+
}
200+
201+
Ok(())
202+
})?;
203+
}
204+
205+
Ok(options)
206+
}
207+
}
208+
121209
#[inline]
122210
fn normalize_option<T: quote::ToTokens>(value: Option<T>) -> TokenStream {
123211
match value {
@@ -154,10 +242,10 @@ fn extract_documentation(attributes: &[syn::Attribute]) -> syn::Result<Option<St
154242
Ok(Some(docs))
155243
}
156244

157-
fn patch_type_params<'a>(
158-
options: &ContainerOptions,
159-
type_params: impl Iterator<Item = &'a mut syn::TypeParam>,
160-
) {
245+
fn patch_type_params<'a, I>(options: &ContainerOptions, type_params: I)
246+
where
247+
I: Iterator<Item = &'a mut syn::TypeParam>,
248+
{
161249
let crate_path = &options.crate_path;
162250

163251
for param in type_params {
@@ -371,7 +459,6 @@ fn expand_union(_meta: ContainerMeta, input: DataUnion) -> syn::Result<TokenStre
371459
pub fn expand(input: DeriveInput) -> syn::Result<TokenStream> {
372460
let options = ContainerOptions::parse(&input.attrs)?;
373461
let serde_options = SerdeContainerOptions::parse(&input.attrs)?;
374-
375462
let description = extract_documentation(&input.attrs)?;
376463

377464
let meta = ContainerMeta {

packages/cw-schema/tests/derive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ enum Baz {
4343
}
4444

4545
#[derive(Schemaifier)]
46-
#[serde(rename_all = "camelCase")]
46+
#[serde(rename_all = "camelCase", thingy = "lmao")]
4747
/// Quux struct!
4848
pub struct Quux {
4949
/// Quux field!

0 commit comments

Comments
 (0)