vstorage/
readonly.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
// Copyright 2023-2025 Hugo Osvaldo Barrera
//
// SPDX-License-Identifier: EUPL-1.2

//! Wrappers for using storages in read-only mode.
//!
//! These wrappers wrap around a normal [`Storage`] instance, but return [`ReadOnly`] for
//! any write operations.
//!
//! [`ReadOnly`]: ErrorKind::ReadOnly

use std::marker::PhantomData;
use std::time::Duration;

use async_trait::async_trait;

use crate::base::Collection;
use crate::base::FetchedItem;
use crate::base::Item;
use crate::base::ListedProperty;
use crate::base::Storage;
use crate::disco::Discovery;
use crate::watch::StorageMonitor;
use crate::CollectionId;
use crate::Href;
use crate::{ErrorKind, Etag, Result};

/// A wrapper around a [`Storage`] that disallows any write operations.
///
/// # Example
///
/// ```
/// # use vstorage::vdir::VdirStorage;
/// # use crate::vstorage::base::IcsItem;
/// # use camino::Utf8PathBuf;
/// # use vstorage::readonly::ReadOnlyStorage;
/// let orig = VdirStorage::<IcsItem>::new(
///     Utf8PathBuf::from("/path/to/storage/"),
///     String::from("ics"),
/// );
///
/// let read_only = ReadOnlyStorage::from(orig);
/// ```
pub struct ReadOnlyStorage<S: Storage<I>, I: Item> {
    inner: S,
    phantom: PhantomData<I>,
}

#[async_trait]
impl<S: Storage<I>, I: Item> Storage<I> for ReadOnlyStorage<S, I> {
    async fn check(&self) -> Result<()> {
        self.inner.check().await
    }

    async fn discover_collections(&self) -> Result<Discovery> {
        self.inner.discover_collections().await
    }

    async fn create_collection(&self, _href: &str) -> Result<Collection> {
        Err(ErrorKind::ReadOnly.into())
    }

    async fn destroy_collection(&self, _href: &str) -> Result<()> {
        Err(ErrorKind::ReadOnly.into())
    }

    async fn list_items(&self, collection_href: &str) -> Result<Vec<crate::base::ItemRef>> {
        self.inner.list_items(collection_href).await
    }

    async fn get_item(&self, href: &str) -> Result<(I, Etag)> {
        self.inner.get_item(href).await
    }

    async fn get_many_items(&self, hrefs: &[&str]) -> Result<Vec<FetchedItem<I>>> {
        self.inner.get_many_items(hrefs).await
    }

    async fn get_all_items(&self, collection_href: &str) -> Result<Vec<FetchedItem<I>>> {
        self.inner.get_all_items(collection_href).await
    }

    async fn add_item(&self, _: &str, _: &I) -> Result<crate::base::ItemRef> {
        Err(ErrorKind::ReadOnly.into())
    }

    async fn update_item(&self, _: &str, _: &Etag, _: &I) -> Result<Etag> {
        Err(ErrorKind::ReadOnly.into())
    }

    async fn set_property(&self, _: &str, _: I::Property, _: &str) -> Result<()> {
        Err(ErrorKind::ReadOnly.into())
    }

    async fn unset_property(&self, _: &str, _: I::Property) -> Result<()> {
        Err(ErrorKind::ReadOnly.into())
    }

    async fn get_property(&self, href: &str, meta: I::Property) -> Result<Option<String>> {
        self.inner.get_property(href, meta).await
    }

    async fn delete_item(&self, _: &str, _: &Etag) -> Result<()> {
        Err(ErrorKind::ReadOnly.into())
    }

    fn href_for_collection_id(&self, id: &CollectionId) -> Result<Href> {
        self.inner.href_for_collection_id(id)
    }

    async fn list_properties(
        &self,
        collection_href: &str,
    ) -> Result<Vec<ListedProperty<I::Property>>> {
        self.inner.list_properties(collection_href).await
    }

    async fn monitor(&self, interval: Duration) -> Result<Box<dyn StorageMonitor>> {
        self.inner.monitor(interval).await
    }
}

impl<S: Storage<I>, I: Item> From<S> for ReadOnlyStorage<S, I> {
    fn from(value: S) -> Self {
        Self {
            inner: value,
            phantom: PhantomData,
        }
    }
}