Generate everything strum does myself (no duplicate prop names)

This commit is contained in:
Lennart
2025-01-18 20:00:26 +01:00
parent 39beee2f69
commit 8d1202234d
16 changed files with 69 additions and 74 deletions

View File

@@ -12,7 +12,6 @@ actix-web = { workspace = true }
async-trait = { workspace = true }
thiserror = { workspace = true }
quick-xml = { workspace = true }
strum = { workspace = true }
tracing = { workspace = true }
tracing-actix-web = { workspace = true }
futures-util = { workspace = true }

View File

@@ -16,19 +16,14 @@ use rustical_dav::resource::{Resource, ResourceService};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_store::auth::User;
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
use rustical_xml::EnumVariants;
use rustical_xml::{EnumUnitVariants, EnumVariants};
use rustical_xml::{XmlDeserialize, XmlSerialize};
use std::marker::PhantomData;
use std::str::FromStr;
use std::sync::Arc;
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
#[strum_discriminants(
name(CalendarPropName),
derive(EnumString, IntoStaticStr),
strum(serialize_all = "kebab-case")
)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[xml(unit_variants_ident = "CalendarPropName")]
pub enum CalendarProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]

View File

@@ -9,10 +9,9 @@ use rustical_dav::{
xml::Resourcetype,
};
use rustical_store::{auth::User, CalendarObject, CalendarStore};
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use serde::Deserialize;
use std::sync::Arc;
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
pub struct CalendarObjectResourceService<C: CalendarStore> {
cal_store: Arc<C>,
@@ -24,12 +23,8 @@ impl<C: CalendarStore> CalendarObjectResourceService<C> {
}
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
#[strum_discriminants(
name(CalendarObjectPropName),
derive(EnumString, IntoStaticStr),
strum(serialize_all = "kebab-case")
)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[xml(unit_variants_ident = "CalendarObjectPropName")]
pub enum CalendarObjectProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]

View File

@@ -8,24 +8,18 @@ use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_store::auth::User;
use rustical_store::CalendarStore;
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use std::sync::Arc;
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
#[derive(Clone)]
pub struct CalendarSetResource {
pub(crate) principal: String,
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
#[strum_discriminants(
name(PrincipalPropName),
derive(EnumString, IntoStaticStr),
strum(serialize_all = "kebab-case")
)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[xml(unit_variants_ident = "PrincipalPropName")]
pub enum PrincipalProp {
// WebDAV Access Control (RFC 3744)
#[strum_discriminants(strum(serialize = "principal-URL"))]
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
PrincipalUrl(HrefElement),
}

View File

@@ -6,8 +6,7 @@ use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_store::auth::User;
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
#[derive(Clone)]
pub struct PrincipalResource {
@@ -18,12 +17,8 @@ pub struct PrincipalResource {
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
#[strum_discriminants(
name(PrincipalPropName),
derive(EnumString, IntoStaticStr),
strum(serialize_all = "kebab-case")
)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[xml(unit_variants_ident = "PrincipalPropName")]
pub enum PrincipalProp {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Displayname(String),
@@ -35,7 +30,6 @@ pub enum PrincipalProp {
CalendarUserAddressSet(HrefElement),
// WebDAV Access Control (RFC 3744)
#[strum_discriminants(strum(serialize = "principal-URL"))]
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
PrincipalUrl(HrefElement),

View File

@@ -12,7 +12,6 @@ actix-web = { workspace = true }
async-trait = { workspace = true }
thiserror = { workspace = true }
quick-xml = { workspace = true }
strum = { workspace = true }
tracing = { workspace = true }
tracing-actix-web = { workspace = true }
futures-util = { workspace = true }

View File

@@ -8,10 +8,9 @@ use rustical_dav::{
xml::Resourcetype,
};
use rustical_store::{auth::User, AddressObject, AddressbookStore};
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use serde::Deserialize;
use std::sync::Arc;
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
use super::methods::{get_object, put_object};
@@ -20,12 +19,8 @@ pub struct AddressObjectResourceService<AS: AddressbookStore> {
addr_store: Arc<AS>,
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
#[strum_discriminants(
name(AddressObjectPropName),
derive(EnumString, IntoStaticStr),
strum(serialize_all = "kebab-case")
)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[xml(unit_variants_ident = "AddressObjectPropName")]
pub enum AddressObjectProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]

View File

@@ -16,11 +16,10 @@ use rustical_dav::resource::{Resource, ResourceService};
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
use rustical_store::auth::User;
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use std::marker::PhantomData;
use std::str::FromStr;
use std::sync::Arc;
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
pub struct AddressbookResourceService<AS: AddressbookStore, S: SubscriptionStore> {
addr_store: Arc<AS>,
@@ -36,12 +35,8 @@ impl<A: AddressbookStore, S: SubscriptionStore> AddressbookResourceService<A, S>
}
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
#[strum_discriminants(
name(AddressbookPropName),
derive(EnumString, IntoStaticStr),
strum(serialize_all = "kebab-case")
)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[xml(unit_variants_ident = "AddressbookPropName")]
pub enum AddressbookProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]

View File

@@ -7,9 +7,8 @@ use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_store::auth::User;
use rustical_store::AddressbookStore;
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use std::sync::Arc;
use strum::{EnumDiscriminants, EnumString, IntoStaticStr};
pub struct PrincipalResourceService<A: AddressbookStore> {
addr_store: Arc<A>,
@@ -26,18 +25,13 @@ pub struct PrincipalResource {
principal: String,
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumDiscriminants, Clone, EnumVariants)]
#[strum_discriminants(
name(PrincipalPropName),
derive(EnumString, IntoStaticStr),
strum(serialize_all = "kebab-case")
)]
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)]
#[xml(unit_variants_ident = "PrincipalPropName")]
pub enum PrincipalProp {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Displayname(String),
// WebDAV Access Control (RFC 3744)
#[strum_discriminants(strum(serialize = "principal-URL"))]
#[xml(rename = b"principal-URL")]
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
PrincipalUrl(HrefElement),

View File

@@ -9,6 +9,7 @@ use actix_web::http::StatusCode;
use actix_web::web::Data;
use actix_web::{web::Path, HttpRequest};
use itertools::Itertools;
use quick_xml::name::Namespace;
use rustical_store::auth::User;
use rustical_xml::Unparsed;
use rustical_xml::XmlDeserialize;
@@ -97,11 +98,11 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
match property {
SetPropertyPropWrapper::Valid(prop) => {
let propname: <R::Resource as Resource>::PropName = prop.clone().into();
let propname: &str = propname.into();
let (ns, propname): (Option<Namespace>, &str) = propname.into();
match resource.set_prop(prop) {
Ok(()) => props_ok.push((None, propname.to_owned())),
Ok(()) => props_ok.push((ns, propname.to_owned())),
Err(Error::PropReadOnly) => {
props_conflict.push((None, propname.to_owned()))
props_conflict.push((ns, propname.to_owned()))
}
Err(err) => return Err(err.into()),
};

View File

@@ -60,7 +60,9 @@ pub enum CommonPropertiesPropName {
}
pub trait Resource: Clone + 'static {
type PropName: ResourcePropName + From<Self::Prop> + Into<&'static str>;
type PropName: ResourcePropName
+ From<Self::Prop>
+ Into<(Option<Namespace<'static>>, &'static str)>;
type Prop: ResourceProp + PartialEq + Clone + EnumVariants;
type Error: ResponseError + From<crate::Error>;
type PrincipalResource: Resource + NamedRoute;

View File

@@ -3,6 +3,7 @@ use crate::resource::{NamedRoute, Resource, ResourceService};
use crate::xml::{Resourcetype, ResourcetypeInner};
use actix_web::dev::ResourceMap;
use async_trait::async_trait;
use quick_xml::name::Namespace;
use rustical_store::auth::User;
use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize};
use serde::Serialize;
@@ -22,6 +23,12 @@ impl<PR: Resource> Default for RootResource<PR> {
#[strum(serialize_all = "kebab-case")]
pub enum RootResourcePropName {}
impl From<RootResourcePropName> for (Option<Namespace<'static>>, &'static str) {
fn from(_value: RootResourcePropName) -> Self {
(None, "unreachable")
}
}
#[derive(XmlDeserialize, XmlSerialize, Serialize, Clone, PartialEq, EnumVariants)]
pub enum RootResourceProp {}

View File

@@ -22,7 +22,7 @@ pub struct VariantAttrs {
#[darling(attributes(xml))]
pub struct EnumAttrs {
pub untagged: Flag,
pub unit_variants_name: Option<String>,
pub unit_variants_ident: Option<syn::Ident>,
}
#[derive(Default, FromDeriveInput, Clone)]

View File

@@ -2,7 +2,6 @@ use super::{attrs::EnumAttrs, Variant};
use crate::attrs::VariantAttrs;
use core::panic;
use darling::{FromDeriveInput, FromVariant};
use proc_macro2::Span;
use quote::quote;
use syn::{DataEnum, DeriveInput};
@@ -245,11 +244,11 @@ impl Enum {
if self.attrs.untagged.is_present() {
panic!("EnumUnitVariants not implemented for untagged enums");
}
let unit_enum_ident = if let Some(name) = &self.attrs.unit_variants_name {
syn::Ident::new(name, Span::call_site())
} else {
panic!("unit_variants_name not set");
};
let unit_enum_ident = self
.attrs
.unit_variants_ident
.as_ref()
.expect("unit_variants_ident no set");
let tagged_variants: Vec<_> = self
.variants
@@ -287,8 +286,15 @@ impl Enum {
quote! { #ident::#variant_ident { .. } => #unit_enum_ident::#variant_ident }
});
let str_to_unit_branches = tagged_variants.iter().map(|variant| {
let variant_ident = &variant.variant.ident;
let b_xml_name = variant.xml_name().value();
let xml_name = String::from_utf8_lossy(&b_xml_name);
quote! { #xml_name => Ok(#unit_enum_ident::#variant_ident) }
});
quote! {
enum #unit_enum_ident {
pub enum #unit_enum_ident {
#(#variant_idents),*
}
@@ -307,6 +313,17 @@ impl Enum {
}
}
}
impl ::std::str::FromStr for #unit_enum_ident {
type Err = ::rustical_xml::FromStrError;
fn from_str(val: &str) -> Result<Self, Self::Err> {
match val {
#(#str_to_unit_branches),*,
_ => Err(::rustical_xml::FromStrError)
}
}
}
}
}
}

View File

@@ -24,6 +24,9 @@ pub trait XmlRootTag {
fn root_ns_prefixes() -> HashMap<Namespace<'static>, &'static [u8]>;
}
#[derive(Debug)]
pub struct FromStrError;
pub trait EnumVariants {
const TAGGED_VARIANTS: &'static [(Option<Namespace<'static>>, &'static str)];

View File

@@ -1,3 +1,5 @@
use std::str::FromStr;
use quick_xml::name::Namespace;
use rustical_xml::EnumVariants;
use xml_derive::EnumUnitVariants;
@@ -16,7 +18,7 @@ enum ExtensionProp {
}
#[derive(EnumVariants, EnumUnitVariants)]
#[xml(unit_variants_name = "CalendarPropName")]
#[xml(unit_variants_ident = "CalendarPropName")]
enum CalendarProp {
// WebDAV (RFC 2518)
#[xml(ns = "NS_DAV")]
@@ -73,4 +75,7 @@ fn test_enum_unit_variants() {
let propname: CalendarPropName = CalendarProp::Displayname(None).into();
let displayname: (Option<Namespace>, &str) = propname.into();
assert_eq!(displayname, (Some(NS_DAV), "displayname"));
let propname: CalendarPropName = FromStr::from_str("displayname").unwrap();
assert_eq!(displayname, (Some(NS_DAV), "displayname"));
}