mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 22:52:22 +00:00
Generate everything strum does myself (no duplicate prop names)
This commit is contained in:
@@ -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 }
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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()),
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)];
|
||||
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user