vstorage/sync/error.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
// Copyright 2023-2024 Hugo Osvaldo Barrera
//
// SPDX-License-Identifier: EUPL-1.2
//! Types for granular error management.
//!
//! These types represent non-fatal errors which may occurs during synchronisation.
use crate::base::Item;
use super::{
execute::ExecutionError,
plan::{CollectionAction, ItemAction, PropertyAction, ResolvedMapping},
};
/// An error synchronising two items between storages.
///
/// This error contains details on a non-fatal error that occurred during synchronisation. It
/// contains enough data to provide a meaningful description of what has gone wrong.
///
/// Use the [`std::fmt::Display`] implementation for a quick description.
#[derive(Debug)]
pub struct SyncError<I: Item> {
action: SomeAction<I>,
error: ExecutionError,
}
impl<I: Item> SyncError<I> {
#[must_use]
pub fn item(action: ItemAction<I>, error: ExecutionError) -> Self {
Self {
action: SomeAction::Item(Box::from(action)),
error,
}
}
#[must_use]
pub fn collection(
action: CollectionAction,
mapping: ResolvedMapping,
error: ExecutionError,
) -> Self {
Self {
action: SomeAction::Collection { action, mapping },
error,
}
}
#[must_use]
pub fn property(action: PropertyAction, error: ExecutionError) -> Self {
Self {
action: SomeAction::Property(action),
error,
}
}
}
impl<I: Item> std::fmt::Display for SyncError<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error executing {}: {}", self.action, self.error)
}
}
impl<I: Item> std::error::Error for SyncError<I> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
/// An action that has failed to execute. See [`SyncError`].
#[derive(Debug)]
pub enum SomeAction<I: Item> {
Item(Box<ItemAction<I>>),
// TODO: this is missing the details of the collection itself (e.g.: alias?).
Collection {
action: CollectionAction,
mapping: ResolvedMapping,
},
// FIXME: missing details of property. Do I need Property::display ?
Property(PropertyAction),
}
impl<I: Item> std::fmt::Display for SomeAction<I> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SomeAction::Item(action) => {
write!(f, "item action '{action}'")
}
SomeAction::Collection { action, mapping } => {
write!(f, "collection action '{action}' for '{}'", mapping.alias())
}
SomeAction::Property(action) => {
write!(f, "property action '{action}'")
}
}
}
}
#[cfg(test)]
mod test {
use std::backtrace::Backtrace;
use crate::{
base::IcsItem,
sync::{
declare::DeclaredMapping,
error::SomeAction,
execute::ExecutionError,
plan::{CollectionAction, ItemAction, ResolvedMapping},
status::{ItemState, Side},
},
};
use super::SyncError;
#[test]
fn test_syncerror_item_display() {
let err = SyncError {
action: SomeAction::Item(Box::from(ItemAction::Create {
side: Side::A,
source: ItemState::<IcsItem> {
href: "/path/to/some/file.vcf".into(),
uid: "d99ed506-dceb-49f2-a1c9-efa63c68acd0".into(),
etag: "123890".into(),
hash: "0000000000000000000000000000000000000000000000000000000000000000"
.parse()
.unwrap(),
data: None,
},
})),
error: ExecutionError::Storage(crate::Error {
kind: crate::ErrorKind::AccessDenied,
source: Some(Box::from(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"Not enough mana",
))),
backtrace: Backtrace::capture(),
}),
};
let msg = err.to_string();
let expected = concat!(
"Error executing item action 'create in storage a (uid: d99ed506-dceb-49f2-a1c9-efa63c68acd0)': ",
"access to the resource was denied: ",
"Not enough mana"
);
assert_eq!(msg, expected);
}
// #[test]
// fn test_syncerror_collection_display() {
// let err = SyncError::<IcsItem> {
// action: SomeAction::Collection {
// action: CollectionAction::CreateInB,
// alias: "guests",
// },
// error: ExecutionError::Storage(crate::Error {
// kind: crate::ErrorKind::AccessDenied,
// source: Some(Box::from(std::io::Error::new(
// std::io::ErrorKind::PermissionDenied,
// "Creating new collections is forbidden",
// ))),
// backtrace: Backtrace::capture(),
// }),
// };
// let msg = err.to_string();
// let expected = concat!(
// "Error executing collection action 'create in storage b' for 'guests': ",
// "access to the resource was denied: ",
// "Creating new collections is forbidden"
// );
// assert_eq!(msg, expected);
// }
}