Import upstream v0.16.22, stripped

Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f
Enterprise-only files removed or emptied: 63
Enterprise-only snippets removed: 117 in 50 files
Dangling module declarations removed: 5
Cargo edits turning enterprise off: 14
Verification: clean
Enterprise feature gates left for rebuilt features: 19 in 18 files

Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
2026-09-18 10:21:56 -07:00
commit 7dae9b29fd
1650 changed files with 485521 additions and 0 deletions
+182
View File
@@ -0,0 +1,182 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use serde::Serialize;
use serde::ser::SerializeMap;
use std::fmt::Display;
#[derive(Debug)]
pub enum MethodError {
InvalidArguments(String),
RequestTooLarge,
StateMismatch,
AnchorNotFound,
UnsupportedFilter(String),
UnsupportedSort(String),
ServerFail(String),
UnknownMethod(String),
ServerUnavailable,
ServerPartialFail,
InvalidResultReference(String),
Forbidden(String),
AccountNotFound,
AccountNotSupportedByMethod,
AccountReadOnly,
NotFound,
CannotCalculateChanges,
UnknownDataType,
}
#[derive(Debug)]
pub struct MethodErrorWrapper(trc::Error);
impl From<trc::Error> for MethodErrorWrapper {
fn from(value: trc::Error) -> Self {
MethodErrorWrapper(value)
}
}
impl Display for MethodError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
MethodError::InvalidArguments(err) => write!(f, "Invalid arguments: {}", err),
MethodError::RequestTooLarge => write!(f, "Request too large"),
MethodError::StateMismatch => write!(f, "State mismatch"),
MethodError::AnchorNotFound => write!(f, "Anchor not found"),
MethodError::UnsupportedFilter(err) => write!(f, "Unsupported filter: {}", err),
MethodError::UnsupportedSort(err) => write!(f, "Unsupported sort: {}", err),
MethodError::ServerFail(err) => write!(f, "Server error: {}", err),
MethodError::UnknownMethod(err) => write!(f, "Unknown method: {}", err),
MethodError::ServerUnavailable => write!(f, "Server unavailable"),
MethodError::ServerPartialFail => write!(f, "Server partial fail"),
MethodError::InvalidResultReference(err) => {
write!(f, "Invalid result reference: {}", err)
}
MethodError::Forbidden(err) => write!(f, "Forbidden: {}", err),
MethodError::AccountNotFound => write!(f, "Account not found"),
MethodError::AccountNotSupportedByMethod => {
write!(f, "Account not supported by method")
}
MethodError::AccountReadOnly => write!(f, "Account read only"),
MethodError::NotFound => write!(f, "Not found"),
MethodError::UnknownDataType => write!(f, "Unknown data type"),
MethodError::CannotCalculateChanges => write!(f, "Cannot calculate changes"),
}
}
}
impl Serialize for MethodErrorWrapper {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(2.into())?;
let description = self.0.value(trc::Key::Details).and_then(|v| v.as_str());
let (error_type, description) = match self.0.as_ref() {
trc::EventType::Jmap(cause) => match cause {
trc::JmapEvent::InvalidArguments => {
("invalidArguments", description.unwrap_or_default())
}
trc::JmapEvent::RequestTooLarge => (
"requestTooLarge",
concat!(
"The number of ids requested by the client exceeds the maximum number ",
"the server is willing to process in a single method call."
),
),
trc::JmapEvent::StateMismatch => (
"stateMismatch",
concat!(
"An \"ifInState\" argument was supplied, but ",
"it does not match the current state."
),
),
trc::JmapEvent::AnchorNotFound => (
"anchorNotFound",
concat!(
"An anchor argument was supplied, but it ",
"cannot be found in the results of the query."
),
),
trc::JmapEvent::UnsupportedFilter => {
("unsupportedFilter", description.unwrap_or_default())
}
trc::JmapEvent::UnsupportedSort => {
("unsupportedSort", description.unwrap_or_default())
}
trc::JmapEvent::NotFound => ("serverPartialFail", {
concat!(
"One or more items are no longer available on the ",
"server, please try again."
)
}),
trc::JmapEvent::UnknownMethod => ("unknownMethod", description.unwrap_or_default()),
trc::JmapEvent::InvalidResultReference => {
("invalidResultReference", description.unwrap_or_default())
}
trc::JmapEvent::Forbidden => ("forbidden", description.unwrap_or_default()),
trc::JmapEvent::AccountNotFound => (
"accountNotFound",
"The accountId does not correspond to a valid account",
),
trc::JmapEvent::AccountNotSupportedByMethod => (
"accountNotSupportedByMethod",
concat!(
"The accountId given corresponds to a valid account, ",
"but the account does not support this method or data type."
),
),
trc::JmapEvent::AccountReadOnly => (
"accountReadOnly",
"This method modifies state, but the account is read-only.",
),
trc::JmapEvent::UnknownDataType => (
"unknownDataType",
concat!(
"The server does not recognise this data type, ",
"or the capability to enable it is not present ",
"in the current Request Object."
),
),
trc::JmapEvent::CannotCalculateChanges => (
"cannotCalculateChanges",
concat!(
"The server cannot calculate the changes ",
"between the old and new states."
),
),
trc::JmapEvent::UnknownCapability
| trc::JmapEvent::NotJson
| trc::JmapEvent::NotRequest => (
"serverUnavailable",
concat!(
"This server is temporarily unavailable. ",
"Attempting this same operation later may succeed."
),
),
_ => (
"serverUnavailable",
"This server is temporarily unavailable.",
),
},
_ => (
"serverUnavailable",
concat!(
"This server is temporarily unavailable. ",
"Attempting this same operation later may succeed."
),
),
};
map.serialize_entry("type", error_type)?;
if !description.is_empty() {
map.serialize_entry("description", description)?;
}
map.end()
}
}
+9
View File
@@ -0,0 +1,9 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
pub mod method;
pub mod request;
pub mod set;
+425
View File
@@ -0,0 +1,425 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::{borrow::Cow, fmt::Display};
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum RequestLimitError {
#[serde(rename = "maxSizeRequest")]
SizeRequest,
#[serde(rename = "maxSizeUpload")]
SizeUpload,
#[serde(rename = "maxCallsInRequest")]
CallsIn,
#[serde(rename = "maxConcurrentRequests")]
ConcurrentRequest,
#[serde(rename = "maxConcurrentUpload")]
ConcurrentUpload,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum RequestErrorType {
#[serde(rename = "urn:ietf:params:jmap:error:unknownCapability")]
UnknownCapability,
#[serde(rename = "urn:ietf:params:jmap:error:notJSON")]
NotJSON,
#[serde(rename = "urn:ietf:params:jmap:error:notRequest")]
NotRequest,
#[serde(rename = "urn:ietf:params:jmap:error:limit")]
Limit,
#[serde(rename = "about:blank")]
Other,
}
#[derive(Debug, Clone)]
pub struct RateLimitPolicy {
pub name: &'static str,
pub limit: u64,
pub remaining: u64,
pub window: Option<u64>,
pub reset: Option<u64>,
pub unit: RateLimitUnit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RateLimitUnit {
Requests,
ContentBytes,
ConcurrentRequests,
}
impl RateLimitUnit {
pub fn as_str(self) -> &'static str {
match self {
RateLimitUnit::Requests => "requests",
RateLimitUnit::ContentBytes => "content-bytes",
RateLimitUnit::ConcurrentRequests => "concurrent-requests",
}
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct RequestError<'x> {
#[serde(rename = "type")]
pub p_type: RequestErrorType,
pub status: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<Cow<'x, str>>,
pub detail: Cow<'x, str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<RequestLimitError>,
#[serde(skip)]
pub rate_limit: Vec<RateLimitPolicy>,
#[serde(skip)]
pub retry_after: Option<u64>,
}
impl<'x> RequestError<'x> {
pub fn blank(
status: u16,
title: impl Into<Cow<'x, str>>,
detail: impl Into<Cow<'x, str>>,
) -> Self {
RequestError {
p_type: RequestErrorType::Other,
status,
title: Some(title.into()),
detail: detail.into(),
limit: None,
rate_limit: Vec::new(),
retry_after: None,
}
}
pub fn with_rate_limit(mut self, policy: RateLimitPolicy) -> Self {
if let Some(reset) = policy.reset
&& self.retry_after.is_none_or(|r| reset > r)
{
self.retry_after = Some(reset);
}
self.rate_limit.push(policy);
self
}
pub fn with_retry_after(mut self, seconds: u64) -> Self {
if self.retry_after.is_none_or(|r| seconds > r) {
self.retry_after = Some(seconds);
}
self
}
pub fn internal_server_error() -> Self {
RequestError::blank(
500,
"Internal Server Error",
concat!(
"There was a problem while processing your request. ",
"Please contact the system administrator if this problem persists."
),
)
}
pub fn unavailable() -> Self {
RequestError::blank(
503,
"Temporarily Unavailable",
concat!(
"There was a temporary problem while processing your request. ",
"Please try again in a few moments."
),
)
}
pub fn invalid_parameters() -> Self {
RequestError::blank(
400,
"Invalid Parameters",
"One or multiple parameters could not be parsed.",
)
}
pub fn forbidden() -> Self {
RequestError::blank(
403,
"Forbidden",
"You do not have enough permissions to access this resource.",
)
}
pub fn over_blob_quota(max_files: usize, max_bytes: usize) -> Self {
RequestError::blank(
429,
"Quota exceeded",
format!(
"You have exceeded the blob upload quota of {} files or {} bytes.",
max_files, max_bytes
),
)
}
pub fn over_quota() -> Self {
RequestError::blank(
403,
"Quota exceeded",
"You have exceeded your account quota.",
)
}
pub fn tenant_over_quota() -> Self {
RequestError::blank(
403,
"Tenant quota exceeded",
"Your organization has exceeded its quota.",
)
}
pub fn too_many_requests() -> Self {
RequestError::blank(
429,
"Too Many Requests",
"Your request has been rate limited. Please try again in a few seconds.",
)
}
pub fn too_many_auth_attempts() -> Self {
RequestError::blank(
429,
"Too Many Authentication Attempts",
"Your request has been rate limited. Please try again in a few minutes.",
)
}
pub fn limit(limit_type: RequestLimitError) -> Self {
RequestError {
p_type: RequestErrorType::Limit,
status: 400,
title: None,
detail: match limit_type {
RequestLimitError::SizeRequest => concat!(
"The request is larger than the server ",
"is willing to process."
),
RequestLimitError::SizeUpload => concat!(
"The uploaded file is larger than the server ",
"is willing to process."
),
RequestLimitError::CallsIn => concat!(
"The request exceeds the maximum number ",
"of calls in a single request."
),
RequestLimitError::ConcurrentRequest => concat!(
"The request exceeds the maximum number ",
"of concurrent requests."
),
RequestLimitError::ConcurrentUpload => concat!(
"The request exceeds the maximum number ",
"of concurrent uploads."
),
}
.into(),
limit: Some(limit_type),
rate_limit: Vec::new(),
retry_after: None,
}
}
pub fn not_found() -> Self {
RequestError::blank(
404,
"Not Found",
"The requested resource does not exist on this server.",
)
}
pub fn unauthorized() -> Self {
RequestError::blank(401, "Unauthorized", "You have to authenticate first.")
}
pub fn unknown_capability(capability: &'_ str) -> RequestError<'_> {
RequestError {
p_type: RequestErrorType::UnknownCapability,
limit: None,
title: None,
status: 400,
detail: format!(
concat!(
"The Request object used capability ",
"'{}', which is not supported",
"by this server."
),
capability
)
.into(),
rate_limit: Vec::new(),
retry_after: None,
}
}
pub fn not_json(detail: &'_ str) -> RequestError<'_> {
RequestError {
p_type: RequestErrorType::NotJSON,
limit: None,
title: None,
status: 400,
detail: format!("Failed to parse JSON: {detail}").into(),
rate_limit: Vec::new(),
retry_after: None,
}
}
pub fn not_request(detail: impl Into<Cow<'x, str>>) -> RequestError<'x> {
RequestError {
p_type: RequestErrorType::NotRequest,
limit: None,
title: None,
status: 400,
detail: detail.into(),
rate_limit: Vec::new(),
retry_after: None,
}
}
}
impl RateLimitPolicy {
pub fn new(name: &'static str, limit: u64) -> Self {
RateLimitPolicy {
name,
limit,
remaining: 0,
window: None,
reset: None,
unit: RateLimitUnit::Requests,
}
}
pub fn with_window(mut self, window: u64) -> Self {
self.window = Some(window);
self
}
pub fn with_reset(mut self, reset: u64) -> Self {
self.reset = Some(reset);
self
}
pub fn with_remaining(mut self, remaining: u64) -> Self {
self.remaining = remaining;
self
}
pub fn with_unit(mut self, unit: RateLimitUnit) -> Self {
self.unit = unit;
self
}
pub fn fmt_policy(&self, out: &mut String) {
use std::fmt::Write;
let _ = write!(out, "\"{}\";q={}", self.name, self.limit);
if let Some(window) = self.window {
let _ = write!(out, ";w={window}");
}
if !matches!(self.unit, RateLimitUnit::Requests) {
let _ = write!(out, ";qu=\"{}\"", self.unit.as_str());
}
}
pub fn fmt_state(&self, out: &mut String) {
use std::fmt::Write;
let _ = write!(out, "\"{}\";r={}", self.name, self.remaining);
if let Some(reset) = self.reset {
let _ = write!(out, ";t={reset}");
}
}
}
impl<'x> RequestError<'x> {
pub fn rate_limit_policy_header(&self) -> Option<String> {
if self.rate_limit.is_empty() {
return None;
}
let mut out = String::new();
for (i, policy) in self.rate_limit.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
policy.fmt_policy(&mut out);
}
Some(out)
}
pub fn rate_limit_state_header(&self) -> Option<String> {
if self.rate_limit.is_empty() {
return None;
}
let mut out = String::new();
for (i, policy) in self.rate_limit.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
policy.fmt_state(&mut out);
}
Some(out)
}
}
impl Display for RequestError<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.detail)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rate_limit_headers_match_spec() {
// Spec example: RateLimit-Policy: "burst";q=100;w=60,"daily";q=1000;w=86400
let mut p1 = String::new();
RateLimitPolicy::new("burst", 100)
.with_window(60)
.fmt_policy(&mut p1);
assert_eq!(p1, r#""burst";q=100;w=60"#);
// Spec example: RateLimit-Policy: "peruser";q=65535;qu="content-bytes";w=10
let mut p2 = String::new();
RateLimitPolicy::new("peruser", 65535)
.with_window(10)
.with_unit(RateLimitUnit::ContentBytes)
.fmt_policy(&mut p2);
assert_eq!(p2, r#""peruser";q=65535;w=10;qu="content-bytes""#);
// Spec example: RateLimit: "default";r=50;t=30
let mut s = String::new();
RateLimitPolicy::new("default", 100)
.with_remaining(50)
.with_reset(30)
.fmt_state(&mut s);
assert_eq!(s, r#""default";r=50;t=30"#);
// Two policies in one header
let err = RequestError::too_many_requests()
.with_rate_limit(
RateLimitPolicy::new("burst", 100)
.with_window(60)
.with_reset(30),
)
.with_rate_limit(
RateLimitPolicy::new("daily", 1000)
.with_window(86400)
.with_reset(3600),
);
assert_eq!(
err.rate_limit_policy_header().as_deref(),
Some(r#""burst";q=100;w=60, "daily";q=1000;w=86400"#),
);
assert_eq!(
err.rate_limit_state_header().as_deref(),
Some(r#""burst";r=0;t=30, "daily";r=0;t=3600"#),
);
assert_eq!(err.retry_after, Some(3600));
}
}
+352
View File
@@ -0,0 +1,352 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use jmap_tools::{Key, Property};
use registry::types::{
error::{PatchError, ValidationError},
id::ObjectId,
};
use std::borrow::Cow;
use types::id::Id;
#[derive(Debug, Clone, serde::Serialize)]
#[serde(bound(serialize = "InvalidProperty<P>: serde::Serialize"))]
#[serde(transparent)]
#[repr(transparent)]
pub struct SetError<P: Property>(Box<SetErrorInner<P>>);
#[derive(Debug, Clone, serde::Serialize)]
#[serde(bound(serialize = "InvalidProperty<P>: serde::Serialize"))]
struct SetErrorInner<P: Property> {
#[serde(rename = "type")]
type_: SetErrorType,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<Cow<'static, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
properties: Option<Vec<InvalidProperty<P>>>,
#[serde(rename = "existingId")]
#[serde(skip_serializing_if = "Option::is_none")]
existing_id: Option<Id>,
#[serde(rename = "objectId")]
#[serde(skip_serializing_if = "Option::is_none")]
object_id: Option<ObjectId>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(rename = "linkedObjects")]
linked_objects: Vec<ObjectId>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(rename = "validationErrors")]
validation_errors: Vec<ValidationError>,
}
#[derive(Debug, Clone)]
pub enum InvalidProperty<T: Property> {
Property(Key<'static, T>),
Path(Vec<Key<'static, T>>),
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum SetErrorType {
#[serde(rename = "forbidden")]
Forbidden,
#[serde(rename = "overQuota")]
OverQuota,
#[serde(rename = "tooLarge")]
TooLarge,
#[serde(rename = "rateLimit")]
RateLimit,
#[serde(rename = "notFound")]
NotFound,
#[serde(rename = "invalidPatch")]
InvalidPatch,
#[serde(rename = "willDestroy")]
WillDestroy,
#[serde(rename = "invalidProperties")]
InvalidProperties,
#[serde(rename = "singleton")]
Singleton,
#[serde(rename = "mailboxHasChild")]
MailboxHasChild,
#[serde(rename = "mailboxHasEmail")]
MailboxHasEmail,
#[serde(rename = "blobNotFound")]
BlobNotFound,
#[serde(rename = "tooManyKeywords")]
TooManyKeywords,
#[serde(rename = "tooManyMailboxes")]
TooManyMailboxes,
#[serde(rename = "forbiddenFrom")]
ForbiddenFrom,
#[serde(rename = "invalidEmail")]
InvalidEmail,
#[serde(rename = "tooManyRecipients")]
TooManyRecipients,
#[serde(rename = "noRecipients")]
NoRecipients,
#[serde(rename = "invalidRecipients")]
InvalidRecipients,
#[serde(rename = "forbiddenMailFrom")]
ForbiddenMailFrom,
#[serde(rename = "forbiddenToSend")]
ForbiddenToSend,
#[serde(rename = "cannotUnsend")]
CannotUnsend,
#[serde(rename = "alreadyExists")]
AlreadyExists,
#[serde(rename = "invalidScript")]
InvalidScript,
#[serde(rename = "scriptIsActive")]
ScriptIsActive,
#[serde(rename = "addressBookHasContents")]
AddressBookHasContents,
#[serde(rename = "nodeHasChildren")]
NodeHasChildren,
#[serde(rename = "calendarHasEvent")]
CalendarHasEvent,
#[serde(rename = "noSupportedScheduleMethods")]
NoSupportedScheduleMethods,
// Stalwart registry errors
#[serde(rename = "objectIsLinked")]
ObjectIsLinked,
#[serde(rename = "invalidForeignKey")]
InvalidForeignKey,
#[serde(rename = "primaryKeyViolation")]
PrimaryKeyViolation,
#[serde(rename = "validationFailed")]
ValidationFailed,
}
impl SetErrorType {
pub fn as_str(&self) -> &'static str {
match self {
SetErrorType::Forbidden => "forbidden",
SetErrorType::OverQuota => "overQuota",
SetErrorType::TooLarge => "tooLarge",
SetErrorType::RateLimit => "rateLimit",
SetErrorType::NotFound => "notFound",
SetErrorType::InvalidPatch => "invalidPatch",
SetErrorType::WillDestroy => "willDestroy",
SetErrorType::InvalidProperties => "invalidProperties",
SetErrorType::Singleton => "singleton",
SetErrorType::BlobNotFound => "blobNotFound",
SetErrorType::MailboxHasChild => "mailboxHasChild",
SetErrorType::MailboxHasEmail => "mailboxHasEmail",
SetErrorType::TooManyKeywords => "tooManyKeywords",
SetErrorType::TooManyMailboxes => "tooManyMailboxes",
SetErrorType::ForbiddenFrom => "forbiddenFrom",
SetErrorType::InvalidEmail => "invalidEmail",
SetErrorType::TooManyRecipients => "tooManyRecipients",
SetErrorType::NoRecipients => "noRecipients",
SetErrorType::InvalidRecipients => "invalidRecipients",
SetErrorType::ForbiddenMailFrom => "forbiddenMailFrom",
SetErrorType::ForbiddenToSend => "forbiddenToSend",
SetErrorType::CannotUnsend => "cannotUnsend",
SetErrorType::AlreadyExists => "alreadyExists",
SetErrorType::InvalidScript => "invalidScript",
SetErrorType::ScriptIsActive => "scriptIsActive",
SetErrorType::AddressBookHasContents => "addressBookHasContents",
SetErrorType::NodeHasChildren => "nodeHasChildren",
SetErrorType::CalendarHasEvent => "calendarHasEvent",
SetErrorType::NoSupportedScheduleMethods => "noSupportedScheduleMethods",
SetErrorType::ObjectIsLinked => "objectIsLinked",
SetErrorType::InvalidForeignKey => "invalidForeignKey",
SetErrorType::PrimaryKeyViolation => "primaryKeyViolation",
SetErrorType::ValidationFailed => "validationFailed",
}
}
}
impl<T: Property> SetError<T> {
pub fn new(type_: SetErrorType) -> Self {
SetError(Box::new(SetErrorInner {
type_,
description: None,
properties: None,
existing_id: None,
object_id: None,
linked_objects: Vec::new(),
validation_errors: Vec::new(),
}))
}
pub fn with_description(mut self, description: impl Into<Cow<'static, str>>) -> Self {
self.0.description = description.into().into();
self
}
pub fn error_type(&self) -> &SetErrorType {
&self.0.type_
}
pub fn description(&self) -> Option<&str> {
self.0.description.as_deref()
}
pub fn validation_errors(&self) -> &[ValidationError] {
&self.0.validation_errors
}
pub fn with_property(mut self, property: impl Into<InvalidProperty<T>>) -> Self {
self.0.properties = vec![property.into()].into();
self
}
pub fn with_properties(
mut self,
properties: impl IntoIterator<Item = impl Into<InvalidProperty<T>>>,
) -> Self {
self.0.properties = properties
.into_iter()
.map(Into::into)
.collect::<Vec<_>>()
.into();
self
}
pub fn with_object_id(mut self, object_id: ObjectId) -> Self {
self.0.object_id = object_id.into();
self
}
pub fn with_object_id_opt(mut self, object_id: Option<ObjectId>) -> Self {
self.0.object_id = object_id;
self
}
pub fn with_linked_objects(mut self, linked_objects: Vec<ObjectId>) -> Self {
self.0.linked_objects = linked_objects;
self
}
pub fn with_validation_errors(mut self, validation_errors: Vec<ValidationError>) -> Self {
self.0.validation_errors = validation_errors;
self
}
pub fn with_existing_id(mut self, id: Id) -> Self {
self.0.existing_id = id.into();
self
}
pub fn invalid_properties() -> Self {
Self::new(SetErrorType::InvalidProperties)
}
pub fn invalid_patch() -> Self {
Self::new(SetErrorType::InvalidPatch)
}
pub fn forbidden() -> Self {
Self::new(SetErrorType::Forbidden)
}
pub fn not_found() -> Self {
Self::new(SetErrorType::NotFound)
}
pub fn blob_not_found() -> Self {
Self::new(SetErrorType::BlobNotFound)
}
pub fn over_quota() -> Self {
Self::new(SetErrorType::OverQuota).with_description("Account quota exceeded.")
}
pub fn already_exists() -> Self {
Self::new(SetErrorType::AlreadyExists)
}
pub fn no_supported_schedule_methods(calendar_address: &str) -> Self {
Self::new(SetErrorType::NoSupportedScheduleMethods).with_description(format!(
"No supported scheduling method for calendar address {calendar_address}."
))
}
pub fn too_large() -> Self {
Self::new(SetErrorType::TooLarge)
}
pub fn will_destroy() -> Self {
Self::new(SetErrorType::WillDestroy).with_description("ID will be destroyed.")
}
pub fn singleton() -> Self {
Self::new(SetErrorType::Singleton)
.with_description("Singletons cannot be created or destroyed.")
}
pub fn address_book_has_contents() -> Self {
Self::new(SetErrorType::AddressBookHasContents)
.with_description("Address book is not empty.")
}
pub fn node_has_children() -> Self {
Self::new(SetErrorType::NodeHasChildren).with_description("Cannot delete non-empty folder.")
}
pub fn calendar_has_event() -> Self {
Self::new(SetErrorType::CalendarHasEvent).with_description("Calendar is not empty.")
}
}
impl<T: Property> From<T> for InvalidProperty<T> {
fn from(property: T) -> Self {
InvalidProperty::Property(Key::Property(property))
}
}
impl<T: Property> From<(T, T)> for InvalidProperty<T> {
fn from((a, b): (T, T)) -> Self {
InvalidProperty::Path(vec![Key::Property(a), Key::Property(b)])
}
}
impl<T: Property> From<Key<'static, T>> for InvalidProperty<T> {
fn from(property: Key<'static, T>) -> Self {
InvalidProperty::Property(property)
}
}
impl<T: Property> serde::Serialize for InvalidProperty<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
InvalidProperty::Property(p) => p.serialize(serializer),
InvalidProperty::Path(p) => {
use std::fmt::Write;
let mut path = String::with_capacity(64);
for (i, p) in p.iter().enumerate() {
if i > 0 {
path.push('/');
}
let _ = write!(path, "{}", p.to_string());
}
path.serialize(serializer)
}
}
}
}
impl From<PatchError> for SetError<registry::schema::properties::Property> {
fn from(err: PatchError) -> Self {
SetError(Box::new(SetErrorInner {
type_: SetErrorType::InvalidPatch,
description: err.message.into(),
properties: Some(vec![InvalidProperty::Property(Key::Owned(err.path))]),
existing_id: None,
object_id: None,
linked_objects: Vec::new(),
validation_errors: Vec::new(),
}))
}
}