/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use std::borrow::Cow; use request::TextMatch; pub mod property; pub mod request; pub mod response; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))] pub struct NamedElement { pub ns: Namespace, pub element: Element, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))] #[repr(u8)] pub enum Namespace { Dav, CalDav, CardDav, CalendarServer, } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))] pub struct Namespaces { pub(crate) cal: bool, pub(crate) card: bool, pub(crate) cs: bool, } impl Namespaces { pub fn set(&mut self, ns: Namespace) { match ns { Namespace::CalDav => self.cal = true, Namespace::CardDav => self.card = true, Namespace::CalendarServer => self.cs = true, Namespace::Dav => {} } } } impl Namespace { pub fn try_parse(value: &[u8]) -> Option { hashify::tiny_map!(value, "DAV:" => Namespace::Dav, "urn:ietf:params:xml:ns:caldav" => Namespace::CalDav, "urn:ietf:params:xml:ns:carddav" => Namespace::CardDav, "http://calendarserver.org/ns/" => Namespace::CalendarServer, "http://calendarserver.org/ns" => Namespace::CalendarServer ) } pub fn prefix(&self) -> &str { match self { Namespace::Dav => "D", Namespace::CalDav => "A", Namespace::CardDav => "B", Namespace::CalendarServer => "C", } } pub fn namespace(&self) -> &'static str { match self { Namespace::Dav => "DAV:", Namespace::CalDav => "urn:ietf:params:xml:ns:caldav", Namespace::CardDav => "urn:ietf:params:xml:ns:carddav", Namespace::CalendarServer => "http://calendarserver.org/ns/", } } } impl AsRef for Namespace { fn as_ref(&self) -> &str { self.namespace() } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))] pub enum Element { Abstract, Ace, Acl, AclPrincipalPropSet, AclRestrictions, Activelock, ActivityCheckoutSet, ActivityCollectionSet, ActivitySet, ActivityVersionSet, Add, AddMember, AddedVersion, AddressData, AddressDataType, Addressbook, AddressbookDescription, AddressbookHomeSet, AddressbookMultiget, AddressbookQuery, After, All, Allcomp, AllowClientDefinedUri, AllowedAttendeeSchedulingObjectChange, AllowedOrganizerSchedulingObjectChange, AllowedPrincipal, Allprop, AlternateUriSet, And, AnyOtherProperty, ApplyToVersion, ApplyToPrincipalCollectionSet, Ascending, Authenticated, AutoMergeSet, AutoUpdate, AutoVersion, Baseline, BaselineCollection, BaselineControl, BaselineControlResponse, BaselineControlledCollection, BaselineControlledCollectionSet, Basicsearch, Basicsearchschema, Before, Bind, BindResponse, BindingName, Calendar, CalendarAvailability, CalendarData, CalendarDescription, CalendarHomeSet, CalendarMultiget, CalendarQuery, CalendarTimezone, CalendarTimezoneId, CalendarUserAddressSet, CalendarUserType, Caseless, ChangedVersion, CheckedIn, CheckedOut, Checkin, CheckinActivity, CheckinFork, CheckinResponse, Checkout, CheckoutCheckin, CheckoutFork, CheckoutResponse, CheckoutSet, CheckoutUnlockedCheckin, Collection, Comment, CommonAncestor, Comp, CompFilter, CompareBaseline, CompareBaselineReport, ConflictPreview, Contains, Creationdate, CreatorDisplayname, CurrentActivitySet, CurrentUserPrincipal, CurrentUserPrivilegeSet, CurrentWorkspaceSet, Datatype, DefaultCalendarNeeded, DeletedVersion, Deny, DenyBeforeGrant, Depth, Descending, Description, Discouraged, Displayname, Eq, Error, Exclusive, Expand, ExpandProperty, Filter, First, Forbidden, ForkOk, FreeBusyQuery, From, Getcontentlanguage, Getcontentlength, Getcontenttype, Getctag, Getetag, Getlastmodified, Grammar, Grant, GrantOnly, Group, GroupMemberSet, GroupMembership, Gt, Gte, Href, IgnorePreview, Include, IncludeVersions, Inherited, InheritedAclSet, Invert, IsCollection, IsDefined, IsNotDefined, KeepCheckedOut, Label, LabelName, LabelNameSet, LabelResponse, LanguageDefined, LanguageMatches, Last, LatestActivityVersion, LatestActivityVersionReport, Like, Limit, LimitFreebusySet, LimitRecurrenceSet, LimitedNumberOfAces, Literal, LocateByHistory, Location, LockTokenSubmitted, Lockdiscovery, LockedCheckout, Lockentry, Lockinfo, Lockroot, Lockscope, Locktoken, Locktype, Lt, Lte, ManagedAttachmentsServerUrl, Match, MaxAttachmentSize, MaxAttachmentsPerResource, MaxAttendeesPerInstance, MaxDateTime, MaxInstances, MaxResourceSize, Merge, MergePreview, MergePreviewReport, MergeSet, MinDateTime, MissingRequiredPrincipal, Mkactivity, MkactivityResponse, Mkcalendar, MkcalendarResponse, Mkcol, MkcolResponse, Mkredirectref, MkredirectrefResponse, Mkworkspace, MkworkspaceResponse, Mount, Multistatus, NeedPrivileges, New, NoAbstract, NoAceConflict, NoAutoMerge, NoCheckout, NoConflictingLock, NoInheritedAceConflict, NoInvert, NoProtectedAceConflict, NoUidConflict, Not, NotSupportedPrivilege, Nresults, Opaque, Opdesc, Open, OperandLiteral, OperandProperty, OperandTypedLiteral, Operators, Options, OptionsResponse, Or, Order, OrderMember, Orderby, OrderingType, Orderpatch, OrderpatchResponse, Owner, ParamFilter, Parent, ParentSet, Permanent, Position, PredecessorSet, Principal, PrincipalUrl, PrincipalAddress, PrincipalCollectionSet, PrincipalMatch, PrincipalProperty, PrincipalPropertySearch, PrincipalSearchProperty, PrincipalSearchPropertySet, Privilege, Prop, PropFilter, Propdesc, Properties, Property, PropertySearch, Propertyupdate, Propfind, Propname, Propstat, Protected, QuerySchema, QuerySchemaDiscovery, QuotaAvailableBytes, QuotaUsedBytes, Read, ReadAcl, ReadCurrentUserPrivilegeSet, ReadFreeBusy, Rebind, RebindResponse, Recipient, RecognizedPrincipal, RedirectLifetime, Redirectref, Reftarget, Remove, Report, RequestStatus, RequiredPrincipal, Resource, ResourceId, Resourcetype, Response, Responsedescription, RootVersion, SameOrganizerInAllComponents, ScheduleCalendarTransp, ScheduleDefaultCalendarUrl, ScheduleDeliver, ScheduleDeliverInvite, ScheduleDeliverReply, ScheduleInbox, ScheduleInboxUrl, ScheduleOutbox, ScheduleOutboxUrl, ScheduleQueryFreebusy, ScheduleResponse, ScheduleSend, ScheduleSendFreebusy, ScheduleSendInvite, ScheduleSendReply, ScheduleTag, Scope, Score, Searchable, Segment, Select, Selectable, Self_, Set, Shared, Sortable, Source, Status, SubactivitySet, SubbaselineSet, SuccessorSet, SupportedAddressData, SupportedCalendarComponentSet, SupportedCalendarData, SupportedCollation, SupportedCollationSet, SupportedFilter, SupportedLiveProperty, SupportedLivePropertySet, SupportedMethod, SupportedMethodSet, SupportedPrivilege, SupportedPrivilegeSet, SupportedQueryGrammar, SupportedQueryGrammarSet, SupportedReport, SupportedReportSet, SupportedRscale, SupportedRscaleSet, Supportedlock, SyncCollection, SyncLevel, SyncToken, Target, Temporary, TextMatch, TimeRange, Timeout, Timezone, TimezoneId, TimezoneServiceSet, Transparent, TypedLiteral, Unauthenticated, Unbind, UnbindResponse, Uncheckout, UncheckoutResponse, UniqueSchedulingObjectResource, Unlock, Unreserved, Update, UpdatePreview, Updateredirectref, UpdateredirectrefResponse, Url, Username, ValidOrganizer, ValidScheduleDefaultCalendarUrl, ValidSchedulingMessage, Version, VersionControl, VersionControlResponse, VersionControlledBinding, VersionControlledBindingSet, VersionControlledConfiguration, VersionHistory, VersionHistoryCollectionSet, VersionHistorySet, VersionName, VersionSet, VersionTree, Where, Workspace, WorkspaceCheckoutSet, WorkspaceCollectionSet, Write, WriteAcl, WriteContent, WriteProperties, } impl Element { pub fn try_parse(value: &[u8]) -> Option<&Self> { hashify::map!(value, Element, "abstract" => Element::Abstract, "ace" => Element::Ace, "acl" => Element::Acl, "acl-principal-prop-set" => Element::AclPrincipalPropSet, "acl-restrictions" => Element::AclRestrictions, "activelock" => Element::Activelock, "activity-checkout-set" => Element::ActivityCheckoutSet, "activity-collection-set" => Element::ActivityCollectionSet, "activity-set" => Element::ActivitySet, "activity-version-set" => Element::ActivityVersionSet, "add" => Element::Add, "add-member" => Element::AddMember, "added-version" => Element::AddedVersion, "address-data" => Element::AddressData, "address-data-type" => Element::AddressDataType, "addressbook" => Element::Addressbook, "addressbook-description" => Element::AddressbookDescription, "addressbook-home-set" => Element::AddressbookHomeSet, "addressbook-multiget" => Element::AddressbookMultiget, "addressbook-query" => Element::AddressbookQuery, "after" => Element::After, "all" => Element::All, "allcomp" => Element::Allcomp, "allow-client-defined-uri" => Element::AllowClientDefinedUri, "allowed-attendee-scheduling-object-change" => Element::AllowedAttendeeSchedulingObjectChange, "allowed-organizer-scheduling-object-change" => Element::AllowedOrganizerSchedulingObjectChange, "allowed-principal" => Element::AllowedPrincipal, "allprop" => Element::Allprop, "alternate-URI-set" => Element::AlternateUriSet, "and" => Element::And, "any-other-property" => Element::AnyOtherProperty, "apply-to-version" => Element::ApplyToVersion, "apply-to-principal-collection-set" => Element::ApplyToPrincipalCollectionSet, "ascending" => Element::Ascending, "authenticated" => Element::Authenticated, "auto-merge-set" => Element::AutoMergeSet, "auto-update" => Element::AutoUpdate, "auto-version" => Element::AutoVersion, "baseline" => Element::Baseline, "baseline-collection" => Element::BaselineCollection, "baseline-control" => Element::BaselineControl, "baseline-control-response" => Element::BaselineControlResponse, "baseline-controlled-collection" => Element::BaselineControlledCollection, "baseline-controlled-collection-set" => Element::BaselineControlledCollectionSet, "basicsearch" => Element::Basicsearch, "basicsearchschema" => Element::Basicsearchschema, "before" => Element::Before, "bind" => Element::Bind, "bind-response" => Element::BindResponse, "binding-name" => Element::BindingName, "calendar" => Element::Calendar, "calendar-availability" => Element::CalendarAvailability, "calendar-data" => Element::CalendarData, "calendar-description" => Element::CalendarDescription, "calendar-home-set" => Element::CalendarHomeSet, "calendar-multiget" => Element::CalendarMultiget, "calendar-query" => Element::CalendarQuery, "calendar-timezone" => Element::CalendarTimezone, "calendar-timezone-id" => Element::CalendarTimezoneId, "calendar-user-address-set" => Element::CalendarUserAddressSet, "calendar-user-type" => Element::CalendarUserType, "caseless" => Element::Caseless, "changed-version" => Element::ChangedVersion, "checked-in" => Element::CheckedIn, "checked-out" => Element::CheckedOut, "checkin" => Element::Checkin, "checkin-activity" => Element::CheckinActivity, "checkin-fork" => Element::CheckinFork, "checkin-response" => Element::CheckinResponse, "checkout" => Element::Checkout, "checkout-checkin" => Element::CheckoutCheckin, "checkout-fork" => Element::CheckoutFork, "checkout-response" => Element::CheckoutResponse, "checkout-set" => Element::CheckoutSet, "checkout-unlocked-checkin" => Element::CheckoutUnlockedCheckin, "collection" => Element::Collection, "comment" => Element::Comment, "common-ancestor" => Element::CommonAncestor, "comp" => Element::Comp, "comp-filter" => Element::CompFilter, "compare-baseline" => Element::CompareBaseline, "compare-baseline-report" => Element::CompareBaselineReport, "conflict-preview" => Element::ConflictPreview, "contains" => Element::Contains, "creationdate" => Element::Creationdate, "creator-displayname" => Element::CreatorDisplayname, "current-activity-set" => Element::CurrentActivitySet, "current-user-principal" => Element::CurrentUserPrincipal, "current-user-privilege-set" => Element::CurrentUserPrivilegeSet, "current-workspace-set" => Element::CurrentWorkspaceSet, "datatype" => Element::Datatype, "default-calendar-needed" => Element::DefaultCalendarNeeded, "deleted-version" => Element::DeletedVersion, "deny" => Element::Deny, "deny-before-grant" => Element::DenyBeforeGrant, "depth" => Element::Depth, "descending" => Element::Descending, "description" => Element::Description, "discouraged" => Element::Discouraged, "displayname" => Element::Displayname, "eq" => Element::Eq, "error" => Element::Error, "exclusive" => Element::Exclusive, "expand" => Element::Expand, "expand-property" => Element::ExpandProperty, "filter" => Element::Filter, "first" => Element::First, "forbidden" => Element::Forbidden, "fork-ok" => Element::ForkOk, "free-busy-query" => Element::FreeBusyQuery, "from" => Element::From, "getcontentlanguage" => Element::Getcontentlanguage, "getcontentlength" => Element::Getcontentlength, "getcontenttype" => Element::Getcontenttype, "getetag" => Element::Getetag, "getctag" => Element::Getctag, "getlastmodified" => Element::Getlastmodified, "grammar" => Element::Grammar, "grant" => Element::Grant, "grant-only" => Element::GrantOnly, "group" => Element::Group, "group-member-set" => Element::GroupMemberSet, "group-membership" => Element::GroupMembership, "gt" => Element::Gt, "gte" => Element::Gte, "href" => Element::Href, "ignore-preview" => Element::IgnorePreview, "include" => Element::Include, "include-versions" => Element::IncludeVersions, "inherited" => Element::Inherited, "inherited-acl-set" => Element::InheritedAclSet, "invert" => Element::Invert, "is-collection" => Element::IsCollection, "is-defined" => Element::IsDefined, "is-not-defined" => Element::IsNotDefined, "keep-checked-out" => Element::KeepCheckedOut, "label" => Element::Label, "label-name" => Element::LabelName, "label-name-set" => Element::LabelNameSet, "label-response" => Element::LabelResponse, "language-defined" => Element::LanguageDefined, "language-matches" => Element::LanguageMatches, "last" => Element::Last, "latest-activity-version" => Element::LatestActivityVersion, "latest-activity-version-report" => Element::LatestActivityVersionReport, "like" => Element::Like, "limit" => Element::Limit, "limit-freebusy-set" => Element::LimitFreebusySet, "limit-recurrence-set" => Element::LimitRecurrenceSet, "limited-number-of-aces" => Element::LimitedNumberOfAces, "literal" => Element::Literal, "locate-by-history" => Element::LocateByHistory, "location" => Element::Location, "lock-token-submitted" => Element::LockTokenSubmitted, "lockdiscovery" => Element::Lockdiscovery, "locked-checkout" => Element::LockedCheckout, "lockentry" => Element::Lockentry, "lockinfo" => Element::Lockinfo, "lockroot" => Element::Lockroot, "lockscope" => Element::Lockscope, "locktoken" => Element::Locktoken, "locktype" => Element::Locktype, "lt" => Element::Lt, "lte" => Element::Lte, "managed-attachments-server-URL" => Element::ManagedAttachmentsServerUrl, "match" => Element::Match, "max-attachment-size" => Element::MaxAttachmentSize, "max-attachments-per-resource" => Element::MaxAttachmentsPerResource, "max-attendees-per-instance" => Element::MaxAttendeesPerInstance, "max-date-time" => Element::MaxDateTime, "max-instances" => Element::MaxInstances, "max-resource-size" => Element::MaxResourceSize, "merge" => Element::Merge, "merge-preview" => Element::MergePreview, "merge-preview-report" => Element::MergePreviewReport, "merge-set" => Element::MergeSet, "min-date-time" => Element::MinDateTime, "missing-required-principal" => Element::MissingRequiredPrincipal, "mkactivity" => Element::Mkactivity, "mkactivity-response" => Element::MkactivityResponse, "mkcalendar" => Element::Mkcalendar, "mkcalendar-response" => Element::MkcalendarResponse, "mkcol" => Element::Mkcol, "mkcol-response" => Element::MkcolResponse, "mkredirectref" => Element::Mkredirectref, "mkredirectref-response" => Element::MkredirectrefResponse, "mkworkspace" => Element::Mkworkspace, "mkworkspace-response" => Element::MkworkspaceResponse, "mount" => Element::Mount, "multistatus" => Element::Multistatus, "need-privileges" => Element::NeedPrivileges, "new" => Element::New, "no-abstract" => Element::NoAbstract, "no-ace-conflict" => Element::NoAceConflict, "no-auto-merge" => Element::NoAutoMerge, "no-checkout" => Element::NoCheckout, "no-conflicting-lock" => Element::NoConflictingLock, "no-inherited-ace-conflict" => Element::NoInheritedAceConflict, "no-invert" => Element::NoInvert, "no-protected-ace-conflict" => Element::NoProtectedAceConflict, "no-uid-conflict" => Element::NoUidConflict, "not" => Element::Not, "not-supported-privilege" => Element::NotSupportedPrivilege, "nresults" => Element::Nresults, "opaque" => Element::Opaque, "opdesc" => Element::Opdesc, "open" => Element::Open, "operand-literal" => Element::OperandLiteral, "operand-property" => Element::OperandProperty, "operand-typed-literal" => Element::OperandTypedLiteral, "operators" => Element::Operators, "options" => Element::Options, "options-response" => Element::OptionsResponse, "or" => Element::Or, "order" => Element::Order, "order-member" => Element::OrderMember, "orderby" => Element::Orderby, "ordering-type" => Element::OrderingType, "orderpatch" => Element::Orderpatch, "orderpatch-response" => Element::OrderpatchResponse, "owner" => Element::Owner, "param-filter" => Element::ParamFilter, "parent" => Element::Parent, "parent-set" => Element::ParentSet, "permanent" => Element::Permanent, "position" => Element::Position, "predecessor-set" => Element::PredecessorSet, "principal" => Element::Principal, "principal-URL" => Element::PrincipalUrl, "principal-address" => Element::PrincipalAddress, "principal-collection-set" => Element::PrincipalCollectionSet, "principal-match" => Element::PrincipalMatch, "principal-property" => Element::PrincipalProperty, "principal-property-search" => Element::PrincipalPropertySearch, "principal-search-property" => Element::PrincipalSearchProperty, "principal-search-property-set" => Element::PrincipalSearchPropertySet, "privilege" => Element::Privilege, "prop" => Element::Prop, "prop-filter" => Element::PropFilter, "propdesc" => Element::Propdesc, "properties" => Element::Properties, "property" => Element::Property, "property-search" => Element::PropertySearch, "propertyupdate" => Element::Propertyupdate, "propfind" => Element::Propfind, "propname" => Element::Propname, "propstat" => Element::Propstat, "protected" => Element::Protected, "query-schema" => Element::QuerySchema, "query-schema-discovery" => Element::QuerySchemaDiscovery, "quota-available-bytes" => Element::QuotaAvailableBytes, "quota-used-bytes" => Element::QuotaUsedBytes, "read" => Element::Read, "read-acl" => Element::ReadAcl, "read-current-user-privilege-set" => Element::ReadCurrentUserPrivilegeSet, "read-free-busy" => Element::ReadFreeBusy, "rebind" => Element::Rebind, "rebind-response" => Element::RebindResponse, "recipient" => Element::Recipient, "recognized-principal" => Element::RecognizedPrincipal, "redirect-lifetime" => Element::RedirectLifetime, "redirectref" => Element::Redirectref, "reftarget" => Element::Reftarget, "remove" => Element::Remove, "report" => Element::Report, "request-status" => Element::RequestStatus, "required-principal" => Element::RequiredPrincipal, "resource" => Element::Resource, "resource-id" => Element::ResourceId, "resourcetype" => Element::Resourcetype, "response" => Element::Response, "responsedescription" => Element::Responsedescription, "root-version" => Element::RootVersion, "same-organizer-in-all-components" => Element::SameOrganizerInAllComponents, "schedule-calendar-transp" => Element::ScheduleCalendarTransp, "schedule-default-calendar-URL" => Element::ScheduleDefaultCalendarUrl, "schedule-deliver" => Element::ScheduleDeliver, "schedule-deliver-invite" => Element::ScheduleDeliverInvite, "schedule-deliver-reply" => Element::ScheduleDeliverReply, "schedule-inbox" => Element::ScheduleInbox, "schedule-inbox-URL" => Element::ScheduleInboxUrl, "schedule-outbox" => Element::ScheduleOutbox, "schedule-outbox-URL" => Element::ScheduleOutboxUrl, "schedule-query-freebusy" => Element::ScheduleQueryFreebusy, "schedule-response" => Element::ScheduleResponse, "schedule-send" => Element::ScheduleSend, "schedule-send-freebusy" => Element::ScheduleSendFreebusy, "schedule-send-invite" => Element::ScheduleSendInvite, "schedule-send-reply" => Element::ScheduleSendReply, "schedule-tag" => Element::ScheduleTag, "scope" => Element::Scope, "score" => Element::Score, "searchable" => Element::Searchable, "segment" => Element::Segment, "select" => Element::Select, "selectable" => Element::Selectable, "self" => Element::Self_, "set" => Element::Set, "shared" => Element::Shared, "sortable" => Element::Sortable, "source" => Element::Source, "status" => Element::Status, "subactivity-set" => Element::SubactivitySet, "subbaseline-set" => Element::SubbaselineSet, "successor-set" => Element::SuccessorSet, "supported-address-data" => Element::SupportedAddressData, "supported-calendar-component-set" => Element::SupportedCalendarComponentSet, "supported-calendar-data" => Element::SupportedCalendarData, "supported-collation" => Element::SupportedCollation, "supported-collation-set" => Element::SupportedCollationSet, "supported-filter" => Element::SupportedFilter, "supported-live-property" => Element::SupportedLiveProperty, "supported-live-property-set" => Element::SupportedLivePropertySet, "supported-method" => Element::SupportedMethod, "supported-method-set" => Element::SupportedMethodSet, "supported-privilege" => Element::SupportedPrivilege, "supported-privilege-set" => Element::SupportedPrivilegeSet, "supported-query-grammar" => Element::SupportedQueryGrammar, "supported-query-grammar-set" => Element::SupportedQueryGrammarSet, "supported-report" => Element::SupportedReport, "supported-report-set" => Element::SupportedReportSet, "supported-rscale" => Element::SupportedRscale, "supported-rscale-set" => Element::SupportedRscaleSet, "supportedlock" => Element::Supportedlock, "sync-collection" => Element::SyncCollection, "sync-level" => Element::SyncLevel, "sync-token" => Element::SyncToken, "target" => Element::Target, "temporary" => Element::Temporary, "text-match" => Element::TextMatch, "time-range" => Element::TimeRange, "timeout" => Element::Timeout, "timezone" => Element::Timezone, "timezone-id" => Element::TimezoneId, "timezone-service-set" => Element::TimezoneServiceSet, "transparent" => Element::Transparent, "typed-literal" => Element::TypedLiteral, "unauthenticated" => Element::Unauthenticated, "unbind" => Element::Unbind, "unbind-response" => Element::UnbindResponse, "uncheckout" => Element::Uncheckout, "uncheckout-response" => Element::UncheckoutResponse, "unique-scheduling-object-resource" => Element::UniqueSchedulingObjectResource, "unlock" => Element::Unlock, "unreserved" => Element::Unreserved, "update" => Element::Update, "update-preview" => Element::UpdatePreview, "updateredirectref" => Element::Updateredirectref, "updateredirectref-response" => Element::UpdateredirectrefResponse, "url" => Element::Url, "username" => Element::Username, "valid-organizer" => Element::ValidOrganizer, "valid-schedule-default-calendar-URL" => Element::ValidScheduleDefaultCalendarUrl, "valid-scheduling-message" => Element::ValidSchedulingMessage, "version" => Element::Version, "version-control" => Element::VersionControl, "version-control-response" => Element::VersionControlResponse, "version-controlled-binding" => Element::VersionControlledBinding, "version-controlled-binding-set" => Element::VersionControlledBindingSet, "version-controlled-configuration" => Element::VersionControlledConfiguration, "version-history" => Element::VersionHistory, "version-history-collection-set" => Element::VersionHistoryCollectionSet, "version-history-set" => Element::VersionHistorySet, "version-name" => Element::VersionName, "version-set" => Element::VersionSet, "version-tree" => Element::VersionTree, "where" => Element::Where, "workspace" => Element::Workspace, "workspace-checkout-set" => Element::WorkspaceCheckoutSet, "workspace-collection-set" => Element::WorkspaceCollectionSet, "write" => Element::Write, "write-acl" => Element::WriteAcl, "write-content" => Element::WriteContent, "write-properties" => Element::WriteProperties, ) } } impl AsRef for Element { fn as_ref(&self) -> &str { match self { Element::Abstract => "abstract", Element::Ace => "ace", Element::Acl => "acl", Element::AclPrincipalPropSet => "acl-principal-prop-set", Element::AclRestrictions => "acl-restrictions", Element::Activelock => "activelock", Element::ActivityCheckoutSet => "activity-checkout-set", Element::ActivityCollectionSet => "activity-collection-set", Element::ActivitySet => "activity-set", Element::ActivityVersionSet => "activity-version-set", Element::Add => "add", Element::AddMember => "add-member", Element::AddedVersion => "added-version", Element::AddressData => "address-data", Element::AddressDataType => "address-data-type", Element::Addressbook => "addressbook", Element::AddressbookDescription => "addressbook-description", Element::AddressbookHomeSet => "addressbook-home-set", Element::AddressbookMultiget => "addressbook-multiget", Element::AddressbookQuery => "addressbook-query", Element::After => "after", Element::All => "all", Element::Allcomp => "allcomp", Element::AllowClientDefinedUri => "allow-client-defined-uri", Element::AllowedAttendeeSchedulingObjectChange => { "allowed-attendee-scheduling-object-change" } Element::AllowedOrganizerSchedulingObjectChange => { "allowed-organizer-scheduling-object-change" } Element::AllowedPrincipal => "allowed-principal", Element::Allprop => "allprop", Element::AlternateUriSet => "alternate-URI-set", Element::And => "and", Element::AnyOtherProperty => "any-other-property", Element::ApplyToVersion => "apply-to-version", Element::ApplyToPrincipalCollectionSet => "apply-to-principal-collection-set", Element::Ascending => "ascending", Element::Authenticated => "authenticated", Element::AutoMergeSet => "auto-merge-set", Element::AutoUpdate => "auto-update", Element::AutoVersion => "auto-version", Element::Baseline => "baseline", Element::BaselineCollection => "baseline-collection", Element::BaselineControl => "baseline-control", Element::BaselineControlResponse => "baseline-control-response", Element::BaselineControlledCollection => "baseline-controlled-collection", Element::BaselineControlledCollectionSet => "baseline-controlled-collection-set", Element::Basicsearch => "basicsearch", Element::Basicsearchschema => "basicsearchschema", Element::Before => "before", Element::Bind => "bind", Element::BindResponse => "bind-response", Element::BindingName => "binding-name", Element::Calendar => "calendar", Element::CalendarAvailability => "calendar-availability", Element::CalendarData => "calendar-data", Element::CalendarDescription => "calendar-description", Element::CalendarHomeSet => "calendar-home-set", Element::CalendarMultiget => "calendar-multiget", Element::CalendarQuery => "calendar-query", Element::CalendarTimezone => "calendar-timezone", Element::CalendarTimezoneId => "calendar-timezone-id", Element::CalendarUserAddressSet => "calendar-user-address-set", Element::CalendarUserType => "calendar-user-type", Element::Caseless => "caseless", Element::ChangedVersion => "changed-version", Element::CheckedIn => "checked-in", Element::CheckedOut => "checked-out", Element::Checkin => "checkin", Element::CheckinActivity => "checkin-activity", Element::CheckinFork => "checkin-fork", Element::CheckinResponse => "checkin-response", Element::Checkout => "checkout", Element::CheckoutCheckin => "checkout-checkin", Element::CheckoutFork => "checkout-fork", Element::CheckoutResponse => "checkout-response", Element::CheckoutSet => "checkout-set", Element::CheckoutUnlockedCheckin => "checkout-unlocked-checkin", Element::Collection => "collection", Element::Comment => "comment", Element::CommonAncestor => "common-ancestor", Element::Comp => "comp", Element::CompFilter => "comp-filter", Element::CompareBaseline => "compare-baseline", Element::CompareBaselineReport => "compare-baseline-report", Element::ConflictPreview => "conflict-preview", Element::Contains => "contains", Element::Creationdate => "creationdate", Element::CreatorDisplayname => "creator-displayname", Element::CurrentActivitySet => "current-activity-set", Element::CurrentUserPrincipal => "current-user-principal", Element::CurrentUserPrivilegeSet => "current-user-privilege-set", Element::CurrentWorkspaceSet => "current-workspace-set", Element::Datatype => "datatype", Element::DefaultCalendarNeeded => "default-calendar-needed", Element::DeletedVersion => "deleted-version", Element::Deny => "deny", Element::DenyBeforeGrant => "deny-before-grant", Element::Depth => "depth", Element::Descending => "descending", Element::Description => "description", Element::Discouraged => "discouraged", Element::Displayname => "displayname", Element::Eq => "eq", Element::Error => "error", Element::Exclusive => "exclusive", Element::Expand => "expand", Element::ExpandProperty => "expand-property", Element::Filter => "filter", Element::First => "first", Element::Forbidden => "forbidden", Element::ForkOk => "fork-ok", Element::FreeBusyQuery => "free-busy-query", Element::From => "from", Element::Getcontentlanguage => "getcontentlanguage", Element::Getcontentlength => "getcontentlength", Element::Getcontenttype => "getcontenttype", Element::Getetag => "getetag", Element::Getctag => "getctag", Element::Getlastmodified => "getlastmodified", Element::Grammar => "grammar", Element::Grant => "grant", Element::GrantOnly => "grant-only", Element::Group => "group", Element::GroupMemberSet => "group-member-set", Element::GroupMembership => "group-membership", Element::Gt => "gt", Element::Gte => "gte", Element::Href => "href", Element::IgnorePreview => "ignore-preview", Element::Include => "include", Element::IncludeVersions => "include-versions", Element::Inherited => "inherited", Element::InheritedAclSet => "inherited-acl-set", Element::Invert => "invert", Element::IsCollection => "is-collection", Element::IsDefined => "is-defined", Element::IsNotDefined => "is-not-defined", Element::KeepCheckedOut => "keep-checked-out", Element::Label => "label", Element::LabelName => "label-name", Element::LabelNameSet => "label-name-set", Element::LabelResponse => "label-response", Element::LanguageDefined => "language-defined", Element::LanguageMatches => "language-matches", Element::Last => "last", Element::LatestActivityVersion => "latest-activity-version", Element::LatestActivityVersionReport => "latest-activity-version-report", Element::Like => "like", Element::Limit => "limit", Element::LimitFreebusySet => "limit-freebusy-set", Element::LimitRecurrenceSet => "limit-recurrence-set", Element::LimitedNumberOfAces => "limited-number-of-aces", Element::Literal => "literal", Element::LocateByHistory => "locate-by-history", Element::Location => "location", Element::LockTokenSubmitted => "lock-token-submitted", Element::Lockdiscovery => "lockdiscovery", Element::LockedCheckout => "locked-checkout", Element::Lockentry => "lockentry", Element::Lockinfo => "lockinfo", Element::Lockroot => "lockroot", Element::Lockscope => "lockscope", Element::Locktoken => "locktoken", Element::Locktype => "locktype", Element::Lt => "lt", Element::Lte => "lte", Element::ManagedAttachmentsServerUrl => "managed-attachments-server-URL", Element::Match => "match", Element::MaxAttachmentSize => "max-attachment-size", Element::MaxAttachmentsPerResource => "max-attachments-per-resource", Element::MaxAttendeesPerInstance => "max-attendees-per-instance", Element::MaxDateTime => "max-date-time", Element::MaxInstances => "max-instances", Element::MaxResourceSize => "max-resource-size", Element::Merge => "merge", Element::MergePreview => "merge-preview", Element::MergePreviewReport => "merge-preview-report", Element::MergeSet => "merge-set", Element::MinDateTime => "min-date-time", Element::MissingRequiredPrincipal => "missing-required-principal", Element::Mkactivity => "mkactivity", Element::MkactivityResponse => "mkactivity-response", Element::Mkcalendar => "mkcalendar", Element::MkcalendarResponse => "mkcalendar-response", Element::Mkcol => "mkcol", Element::MkcolResponse => "mkcol-response", Element::Mkredirectref => "mkredirectref", Element::MkredirectrefResponse => "mkredirectref-response", Element::Mkworkspace => "mkworkspace", Element::MkworkspaceResponse => "mkworkspace-response", Element::Mount => "mount", Element::Multistatus => "multistatus", Element::NeedPrivileges => "need-privileges", Element::New => "new", Element::NoAbstract => "no-abstract", Element::NoAceConflict => "no-ace-conflict", Element::NoAutoMerge => "no-auto-merge", Element::NoCheckout => "no-checkout", Element::NoConflictingLock => "no-conflicting-lock", Element::NoInheritedAceConflict => "no-inherited-ace-conflict", Element::NoInvert => "no-invert", Element::NoProtectedAceConflict => "no-protected-ace-conflict", Element::NoUidConflict => "no-uid-conflict", Element::Not => "not", Element::NotSupportedPrivilege => "not-supported-privilege", Element::Nresults => "nresults", Element::Opaque => "opaque", Element::Opdesc => "opdesc", Element::Open => "open", Element::OperandLiteral => "operand-literal", Element::OperandProperty => "operand-property", Element::OperandTypedLiteral => "operand-typed-literal", Element::Operators => "operators", Element::Options => "options", Element::OptionsResponse => "options-response", Element::Or => "or", Element::Order => "order", Element::OrderMember => "order-member", Element::Orderby => "orderby", Element::OrderingType => "ordering-type", Element::Orderpatch => "orderpatch", Element::OrderpatchResponse => "orderpatch-response", Element::Owner => "owner", Element::ParamFilter => "param-filter", Element::Parent => "parent", Element::ParentSet => "parent-set", Element::Permanent => "permanent", Element::Position => "position", Element::PredecessorSet => "predecessor-set", Element::Principal => "principal", Element::PrincipalUrl => "principal-URL", Element::PrincipalAddress => "principal-address", Element::PrincipalCollectionSet => "principal-collection-set", Element::PrincipalMatch => "principal-match", Element::PrincipalProperty => "principal-property", Element::PrincipalPropertySearch => "principal-property-search", Element::PrincipalSearchProperty => "principal-search-property", Element::PrincipalSearchPropertySet => "principal-search-property-set", Element::Privilege => "privilege", Element::Prop => "prop", Element::PropFilter => "prop-filter", Element::Propdesc => "propdesc", Element::Properties => "properties", Element::Property => "property", Element::PropertySearch => "property-search", Element::Propertyupdate => "propertyupdate", Element::Propfind => "propfind", Element::Propname => "propname", Element::Propstat => "propstat", Element::Protected => "protected", Element::QuerySchema => "query-schema", Element::QuerySchemaDiscovery => "query-schema-discovery", Element::QuotaAvailableBytes => "quota-available-bytes", Element::QuotaUsedBytes => "quota-used-bytes", Element::Read => "read", Element::ReadAcl => "read-acl", Element::ReadCurrentUserPrivilegeSet => "read-current-user-privilege-set", Element::ReadFreeBusy => "read-free-busy", Element::Rebind => "rebind", Element::RebindResponse => "rebind-response", Element::Recipient => "recipient", Element::RecognizedPrincipal => "recognized-principal", Element::RedirectLifetime => "redirect-lifetime", Element::Redirectref => "redirectref", Element::Reftarget => "reftarget", Element::Remove => "remove", Element::Report => "report", Element::RequestStatus => "request-status", Element::RequiredPrincipal => "required-principal", Element::Resource => "resource", Element::ResourceId => "resource-id", Element::Resourcetype => "resourcetype", Element::Response => "response", Element::Responsedescription => "responsedescription", Element::RootVersion => "root-version", Element::SameOrganizerInAllComponents => "same-organizer-in-all-components", Element::ScheduleCalendarTransp => "schedule-calendar-transp", Element::ScheduleDefaultCalendarUrl => "schedule-default-calendar-URL", Element::ScheduleDeliver => "schedule-deliver", Element::ScheduleDeliverInvite => "schedule-deliver-invite", Element::ScheduleDeliverReply => "schedule-deliver-reply", Element::ScheduleInbox => "schedule-inbox", Element::ScheduleInboxUrl => "schedule-inbox-URL", Element::ScheduleOutbox => "schedule-outbox", Element::ScheduleOutboxUrl => "schedule-outbox-URL", Element::ScheduleQueryFreebusy => "schedule-query-freebusy", Element::ScheduleResponse => "schedule-response", Element::ScheduleSend => "schedule-send", Element::ScheduleSendFreebusy => "schedule-send-freebusy", Element::ScheduleSendInvite => "schedule-send-invite", Element::ScheduleSendReply => "schedule-send-reply", Element::ScheduleTag => "schedule-tag", Element::Scope => "scope", Element::Score => "score", Element::Searchable => "searchable", Element::Segment => "segment", Element::Select => "select", Element::Selectable => "selectable", Element::Self_ => "self", Element::Set => "set", Element::Shared => "shared", Element::Sortable => "sortable", Element::Source => "source", Element::Status => "status", Element::SubactivitySet => "subactivity-set", Element::SubbaselineSet => "subbaseline-set", Element::SuccessorSet => "successor-set", Element::SupportedAddressData => "supported-address-data", Element::SupportedCalendarComponentSet => "supported-calendar-component-set", Element::SupportedCalendarData => "supported-calendar-data", Element::SupportedCollation => "supported-collation", Element::SupportedCollationSet => "supported-collation-set", Element::SupportedFilter => "supported-filter", Element::SupportedLiveProperty => "supported-live-property", Element::SupportedLivePropertySet => "supported-live-property-set", Element::SupportedMethod => "supported-method", Element::SupportedMethodSet => "supported-method-set", Element::SupportedPrivilege => "supported-privilege", Element::SupportedPrivilegeSet => "supported-privilege-set", Element::SupportedQueryGrammar => "supported-query-grammar", Element::SupportedQueryGrammarSet => "supported-query-grammar-set", Element::SupportedReport => "supported-report", Element::SupportedReportSet => "supported-report-set", Element::SupportedRscale => "supported-rscale", Element::SupportedRscaleSet => "supported-rscale-set", Element::Supportedlock => "supportedlock", Element::SyncCollection => "sync-collection", Element::SyncLevel => "sync-level", Element::SyncToken => "sync-token", Element::Target => "target", Element::Temporary => "temporary", Element::TextMatch => "text-match", Element::TimeRange => "time-range", Element::Timeout => "timeout", Element::Timezone => "timezone", Element::TimezoneId => "timezone-id", Element::TimezoneServiceSet => "timezone-service-set", Element::Transparent => "transparent", Element::TypedLiteral => "typed-literal", Element::Unauthenticated => "unauthenticated", Element::Unbind => "unbind", Element::UnbindResponse => "unbind-response", Element::Uncheckout => "uncheckout", Element::UncheckoutResponse => "uncheckout-response", Element::UniqueSchedulingObjectResource => "unique-scheduling-object-resource", Element::Unlock => "unlock", Element::Unreserved => "unreserved", Element::Update => "update", Element::UpdatePreview => "update-preview", Element::Updateredirectref => "updateredirectref", Element::UpdateredirectrefResponse => "updateredirectref-response", Element::Url => "url", Element::Username => "username", Element::ValidOrganizer => "valid-organizer", Element::ValidScheduleDefaultCalendarUrl => "valid-schedule-default-calendar-URL", Element::ValidSchedulingMessage => "valid-scheduling-message", Element::Version => "version", Element::VersionControl => "version-control", Element::VersionControlResponse => "version-control-response", Element::VersionControlledBinding => "version-controlled-binding", Element::VersionControlledBindingSet => "version-controlled-binding-set", Element::VersionControlledConfiguration => "version-controlled-configuration", Element::VersionHistory => "version-history", Element::VersionHistoryCollectionSet => "version-history-collection-set", Element::VersionHistorySet => "version-history-set", Element::VersionName => "version-name", Element::VersionSet => "version-set", Element::VersionTree => "version-tree", Element::Where => "where", Element::Workspace => "workspace", Element::WorkspaceCheckoutSet => "workspace-checkout-set", Element::WorkspaceCollectionSet => "workspace-collection-set", Element::Write => "write", Element::WriteAcl => "write-acl", Element::WriteContent => "write-content", Element::WriteProperties => "write-properties", } } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum Attribute { Caseless(bool), XsiType(XsiType), AllowPCData(bool), Name(T), Namespace(Namespace), ContentType(String), XmlLanguage(String), Version(String), NoValue(bool), TestAllOf(bool), MatchType(MatchType), NegateCondition(bool), Collation(Collation), Start(T), End(T), Unknown { param: String, value: String }, } pub trait AttributeValue { fn from_str(s: &str) -> Option where Self: Sized; } impl Attribute { pub fn from_param(param: &[u8], value: Cow<'_, str>) -> Option> { hashify::fnc_map!(param, "caseless" => { if let Some(b) = YesNo::from_str(value.as_ref()) { return Some(Attribute::Caseless(b)); } }, "xsi:type" => { return Some(Attribute::XsiType(XsiType::from_str(value.as_ref()).unwrap_or(XsiType::Unsupported))); }, "allow-pcdata" => { if let Some(b) = YesNo::from_str(value.as_ref()) { return Some(Attribute::AllowPCData(b)); } }, "novalue" => { if let Some(b) = YesNo::from_str(value.as_ref()) { return Some(Attribute::NoValue(b)); } }, "negate-condition" => { if let Some(b) = YesNo::from_str(value.as_ref()) { return Some(Attribute::NegateCondition(b)); } }, "name" => { if let Some(value) = T::from_str(value.as_ref()) { return Some(Attribute::Name(value)); } }, "namespace" => { if let Some(ns) = Namespace::try_parse(value.as_bytes()) { return Some(Attribute::Namespace(ns)); } }, "content-type" => { return Some(Attribute::ContentType(value.into_owned())); }, "version" => { return Some(Attribute::Version(value.into_owned())); }, "test" => { return Some(Attribute::TestAllOf(value.eq("allof"))); }, "match-type" => { if let Some(mt) = MatchType::try_parse(value.as_ref()) { return Some(Attribute::MatchType(mt)); } }, "collation" => { if let Some(c) = Collation::try_parse(value.as_ref()) { return Some(Attribute::Collation(c)); } }, "start" => { if let Some(value) = T::from_str(value.as_ref()) { return Some(Attribute::Start(value)); } }, "end" => { if let Some(value) = T::from_str(value.as_ref()) { return Some(Attribute::End(value)); } }, "xml:lang" => { return Some(Attribute::XmlLanguage(value.into_owned())); }, "xmlns" => { return None; }, _ => { if param.starts_with(b"xmlns:") { return None; } } ); Some(Attribute::Unknown { param: String::from_utf8_lossy(param).into_owned(), value: value.into_owned(), }) } } impl AttributeValue for String { fn from_str(s: &str) -> Option { Some(s.to_string()) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))] pub enum Collation { AsciiNumeric, AsciiCasemap, Octet, UnicodeCasemap, } impl Collation { pub fn try_parse(s: &str) -> Option { hashify::tiny_map!(s.as_bytes(), "i;ascii-numeric" => Collation::AsciiNumeric, "i;ascii-casemap" => Collation::AsciiCasemap, "i;octet" => Collation::Octet, "i;unicode-casemap" => Collation::UnicodeCasemap, ) } pub fn as_str(&self) -> &'static str { match self { Collation::AsciiNumeric => "i;ascii-numeric", Collation::AsciiCasemap => "i;ascii-casemap", Collation::Octet => "i;octet", Collation::UnicodeCasemap => "i;unicode-casemap", } } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))] pub enum MatchType { Equals, Contains, StartsWith, EndsWith, } impl MatchType { pub fn try_parse(s: &str) -> Option { hashify::tiny_map!(s.as_bytes(), "equals" => MatchType::Equals, "contains" => MatchType::Contains, "starts-with" => MatchType::StartsWith, "ends-with" => MatchType::EndsWith, ) } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum XsiType { String, Boolean, Decimal, Float, Double, Duration, DateTime, Time, Date, GYearMonth, GYear, GMonthDay, GDay, GMonth, HexBinary, Base64Binary, AnyUri, QName, Notation, Unsupported, } impl XsiType { fn from_str(s: &str) -> Option { hashify::tiny_map!(s.as_bytes(), "xs:string" => XsiType::String, "xs:boolean" => XsiType::Boolean, "xs:decimal" => XsiType::Decimal, "xs:float" => XsiType::Float, "xs:double" => XsiType::Double, "xs:duration" => XsiType::Duration, "xs:dateTime" => XsiType::DateTime, "xs:time" => XsiType::Time, "xs:date" => XsiType::Date, "xs:gYearMonth" => XsiType::GYearMonth, "xs:gYear" => XsiType::GYear, "xs:gMonthDay" => XsiType::GMonthDay, "xs:gDay" => XsiType::GDay, "xs:gMonth" => XsiType::GMonth, "xs:hexBinary" => XsiType::HexBinary, "xs:base64Binary" => XsiType::Base64Binary, "xs:anyURI" => XsiType::AnyUri, "xs:QName" => XsiType::QName, "xs:NOTATION" => XsiType::Notation, ) } } struct YesNo; impl YesNo { fn from_str(s: &str) -> Option { hashify::tiny_map!(s.as_bytes(), "yes" => true, "no" => false, ) } } impl TextMatch { pub fn matches(&self, text: &str) -> bool { match self.collation { Collation::Octet => { (match self.match_type { MatchType::Equals => text == self.value, MatchType::Contains => text.contains(&self.value), MatchType::StartsWith => text.starts_with(&self.value), MatchType::EndsWith => text.ends_with(&self.value), }) ^ self.negate } _ => { (match self.match_type { MatchType::Equals => text.to_lowercase() == self.value.to_lowercase(), MatchType::Contains => text.to_lowercase().contains(&self.value.to_lowercase()), MatchType::StartsWith => { text.to_lowercase().starts_with(&self.value.to_lowercase()) } MatchType::EndsWith => { text.to_lowercase().ends_with(&self.value.to_lowercase()) } }) ^ self.negate } } } }