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
// Copyright 2023-2024 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 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::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 collection_id(&self, collection_href: &str) -> Result<CollectionId> {
        self.inner.collection_id(collection_href)
    }

    fn href_for_collection_id(&self, _id: &CollectionId) -> Result<Href> {
        Err(ErrorKind::ReadOnly.into())
    }

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

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