use std::{fmt, rc::Rc};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
use serde_json::Value;
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RestorationKey(String);
impl RestorationKey {
pub fn new(segment: impl Into<String>) -> Result<Self, RestorationKeyError> {
let segment = segment.into();
if segment.is_empty() {
return Err(RestorationKeyError::Empty);
}
if segment.contains('\0') {
return Err(RestorationKeyError::ContainsNul);
}
Ok(Self(segment))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn into_inner(self) -> String {
self.0
}
}
impl TryFrom<String> for RestorationKey {
type Error = RestorationKeyError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl TryFrom<&str> for RestorationKey {
type Error = RestorationKeyError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl fmt::Display for RestorationKey {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
impl Serialize for RestorationKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for RestorationKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)
.and_then(|segment| Self::new(segment).map_err(de::Error::custom))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
pub enum RestorationKeyError {
#[error("a restoration key cannot be empty")]
Empty,
#[error("a restoration key cannot contain NUL")]
ContainsNul,
}
pub trait RestorationBackend {
fn read_value(&self, path: &[RestorationKey]) -> Option<Value>;
fn write_value(&self, path: &[RestorationKey], value: Value);
fn remove_value(&self, path: &[RestorationKey]);
fn note_restoration_outcome(&self, _restored: u64, _invalid: u64) {}
}
#[derive(Clone)]
pub struct RestorationScope {
backend: Rc<dyn RestorationBackend>,
path: Vec<RestorationKey>,
}
impl fmt::Debug for RestorationScope {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("RestorationScope")
.field("path", &self.path)
.finish_non_exhaustive()
}
}
impl RestorationScope {
#[must_use]
pub fn root(backend: Rc<dyn RestorationBackend>) -> Self {
Self {
backend,
path: Vec::new(),
}
}
#[must_use]
pub fn new(
backend: Rc<dyn RestorationBackend>,
path: impl IntoIterator<Item = RestorationKey>,
) -> Self {
Self {
backend,
path: path.into_iter().collect(),
}
}
#[must_use]
pub fn child_unchecked(&self, key: RestorationKey) -> Self {
let mut path = self.path.clone();
path.push(key);
Self {
backend: self.backend.clone(),
path,
}
}
#[must_use]
pub fn child(&self, key: RestorationKey) -> Self {
self.child_unchecked(key)
}
#[must_use]
pub fn is_root(&self) -> bool {
self.path.is_empty()
}
#[must_use]
pub fn path(&self) -> &[RestorationKey] {
&self.path
}
#[must_use]
pub fn get_json(&self, key: &RestorationKey) -> Option<Value> {
self.backend.read_value(&self.value_path(key))
}
pub fn set_json(&self, key: &RestorationKey, value: Value) {
self.backend.write_value(&self.value_path(key), value);
}
pub fn remove(&self, key: &RestorationKey) {
self.backend.remove_value(&self.value_path(key));
}
pub fn note_restoration_outcome(&self, restored: u64, invalid: u64) {
self.backend.note_restoration_outcome(restored, invalid);
}
fn value_path(&self, key: &RestorationKey) -> Vec<RestorationKey> {
let mut path = self.path.clone();
path.push(key.clone());
path
}
}