serde_semver_derive/
lib.rs1use quote::quote;
2use syn::{
3 parse::{Parse, ParseStream},
4 spanned::Spanned as _,
5 Token,
6};
7
8struct UnitStruct {
9 #[allow(dead_code)]
10 vis: syn::Visibility,
11 attrs: Vec<syn::Attribute>,
12 #[allow(dead_code)]
13 struct_token: Token![struct],
14 name: syn::Ident,
15 #[allow(dead_code)]
16 semi_token: Token![;],
17}
18
19impl Parse for UnitStruct {
20 fn parse(input: ParseStream) -> Result<Self, syn::Error> {
21 Ok(UnitStruct {
22 attrs: input.call(syn::Attribute::parse_outer)?,
23 vis: input.parse()?,
24 struct_token: input.parse()?,
25 name: input.parse()?,
26 semi_token: input.parse()?,
27 })
28 }
29}
30
31#[proc_macro_derive(SemverReq, attributes(version))]
32pub fn derive_semver_req(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
33 let input = syn::parse_macro_input!(input as UnitStruct);
34 f_derive_semver_req(input)
35 .unwrap_or_else(|err| err.to_compile_error())
36 .into()
37}
38
39fn f_derive_semver_req(input: UnitStruct) -> Result<proc_macro2::TokenStream, syn::Error> {
40 let UnitStruct { attrs, name, .. } = input;
41
42 let mut attrs_iter = attrs.iter().filter_map(|attr| {
43 let ident = attr.path.get_ident()?;
44 (ident == "version").then(|| attr)
45 });
46
47 let version_attr = attrs_iter.next().ok_or_else(|| {
48 syn::Error::new(
49 name.span(),
50 r#"#[version("X.Y.Z")] attribute must be specified"#,
51 )
52 })?;
53
54 if let Some(dup_attr) = attrs_iter.next() {
55 return Err(syn::Error::new(
56 dup_attr.span(),
57 r#"duplicated version attribute"#,
58 ));
59 }
60
61 let version_lit: syn::LitStr = version_attr.parse_args()?;
62 let version_text = version_lit.value();
63 semver::Version::parse(&version_text)
64 .map_err(|err| syn::Error::new(version_lit.span(), err.to_string()))?;
65
66 let expanded = quote! {
67 impl #name {
68 pub fn version() -> ::serde_semver::semver::Version {
69 ::serde_semver::semver::Version::parse(#version_text).unwrap()
70 }
71 }
72
73 impl ::serde_semver::serde::Serialize for #name {
74 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75 where
76 S: ::serde_semver::serde::Serializer,
77 {
78 use ::serde_semver::semver::{Version, VersionReq};
79 use ::serde_semver::serde::de::Error;
80 Self::version().serialize(serializer)
81 }
82 }
83
84 impl<'de> ::serde_semver::serde::Deserialize<'de> for #name {
85 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
86 where
87 D: ::serde_semver::serde::Deserializer<'de>,
88 {
89 use ::serde_semver::semver::{Version, VersionReq};
90 use ::serde_semver::serde::de::Error;
91 let input_version = Version::deserialize(deserializer)?;
92 let req = VersionReq::parse(&input_version.to_string()).unwrap();
93 let target_version = Self::version();
94
95 if !req.matches(&target_version) {
96 return Err(D::Error::custom(
97 format!(r#"input version {} is not compatible with version {}"#, input_version, target_version)
98 ));
99 }
100
101 Ok(Self)
102 }
103 }
104 };
105
106 Ok(expanded)
107}