JMAP Registry API implementation - part 6

This commit is contained in:
mdecimus
2026-03-01 18:30:04 +01:00
parent 07d5748f6b
commit 9ccc2ed6f0
74 changed files with 2417 additions and 785 deletions

View File

@@ -5,24 +5,46 @@
*/
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"))]
pub struct SetError<P: Property> {
#[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")]
pub type_: SetErrorType,
type_: SetErrorType,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<Cow<'static, str>>,
description: Option<Cow<'static, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<Vec<InvalidProperty<P>>>,
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)]
@@ -89,6 +111,15 @@ pub enum SetErrorType {
NodeHasChildren,
#[serde(rename = "calendarHasEvent")]
CalendarHasEvent,
// Stalwart registry errors
#[serde(rename = "objectIsLinked")]
ObjectIsLinked,
#[serde(rename = "invalidForeignKey")]
InvalidForeignKey,
#[serde(rename = "primaryKeyViolation")]
PrimaryKeyViolation,
#[serde(rename = "validationFailed")]
ValidationFailed,
}
impl SetErrorType {
@@ -122,27 +153,34 @@ impl SetErrorType {
SetErrorType::AddressBookHasContents => "addressBookHasContents",
SetErrorType::NodeHasChildren => "nodeHasChildren",
SetErrorType::CalendarHasEvent => "calendarHasEvent",
SetErrorType::ObjectIsLinked => "objectIsLinked",
SetErrorType::InvalidForeignKey => "invalidForeignKey",
SetErrorType::PrimaryKeyViolation => "primaryKeyViolation",
SetErrorType::ValidationFailed => "validationFailed",
}
}
}
impl<T: Property> SetError<T> {
pub fn new(type_: SetErrorType) -> Self {
SetError {
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.description = description.into().into();
self.0.description = description.into().into();
self
}
pub fn with_property(mut self, property: impl Into<InvalidProperty<T>>) -> Self {
self.properties = vec![property.into()].into();
self.0.properties = vec![property.into()].into();
self
}
@@ -150,7 +188,7 @@ impl<T: Property> SetError<T> {
mut self,
properties: impl IntoIterator<Item = impl Into<InvalidProperty<T>>>,
) -> Self {
self.properties = properties
self.0.properties = properties
.into_iter()
.map(Into::into)
.collect::<Vec<_>>()
@@ -158,8 +196,23 @@ impl<T: Property> SetError<T> {
self
}
pub fn with_object_id(mut self, object_id: ObjectId) -> Self {
self.0.object_id = object_id.into();
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.existing_id = id.into();
self.0.existing_id = id.into();
self
}
@@ -253,3 +306,17 @@ impl<T: Property> serde::Serialize for InvalidProperty<T> {
}
}
}
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(),
}))
}
}