debian_packaging/repository/
proxy_writer.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5/*! A repository writer that doesn't actually write. */
6
7use {
8    crate::{
9        error::{DebianError, Result},
10        io::ContentDigest,
11        repository::{
12            RepositoryPathVerification, RepositoryPathVerificationState, RepositoryWrite,
13            RepositoryWriter,
14        },
15    },
16    async_trait::async_trait,
17    futures::AsyncRead,
18    std::{borrow::Cow, pin::Pin, sync::Mutex},
19};
20
21/// How [RepositoryWriter::verify_path()] should behave for [ProxyWriter] instances.
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum ProxyVerifyBehavior {
24    /// Always call the inner [RepositoryWriter::verify_path()].
25    Proxy,
26    AlwaysExistsNoIntegrityCheck,
27    AlwaysExistsIntegrityVerified,
28    AlwaysExistsIntegrityMismatch,
29    AlwaysMissing,
30}
31
32/// A [RepositoryWriter] that proxies operations to an inner writer.
33///
34/// The behavior of each I/O operation can be configured to facilitate customizing
35/// behavior. It also records operations performed. This makes this type useful as part
36/// of testing and simulating what would  occur.
37pub struct ProxyWriter<W> {
38    inner: W,
39    verify_behavior: ProxyVerifyBehavior,
40    /// List of paths that were written.
41    path_writes: Mutex<Vec<String>>,
42}
43
44impl<W: RepositoryWriter + Send> ProxyWriter<W> {
45    /// Construct a new instance by wrapping an existing writer.
46    ///
47    /// The default behavior for path verification is to call the inner writer.
48    pub fn new(writer: W) -> Self {
49        Self {
50            inner: writer,
51            verify_behavior: ProxyVerifyBehavior::Proxy,
52            path_writes: Mutex::new(vec![]),
53        }
54    }
55
56    /// Return the inner writer, consuming self.
57    pub fn into_inner(self) -> W {
58        self.inner
59    }
60
61    /// Set the behavior for [RepositoryWriter::verify_path()].
62    pub fn set_verify_behavior(&mut self, behavior: ProxyVerifyBehavior) {
63        self.verify_behavior = behavior;
64    }
65}
66
67#[async_trait]
68impl<W: RepositoryWriter + Send> RepositoryWriter for ProxyWriter<W> {
69    async fn verify_path<'path>(
70        &self,
71        path: &'path str,
72        expected_content: Option<(u64, ContentDigest)>,
73    ) -> Result<RepositoryPathVerification<'path>> {
74        match self.verify_behavior {
75            ProxyVerifyBehavior::Proxy => self.inner.verify_path(path, expected_content).await,
76            ProxyVerifyBehavior::AlwaysExistsIntegrityVerified => Ok(RepositoryPathVerification {
77                path,
78                state: RepositoryPathVerificationState::ExistsIntegrityVerified,
79            }),
80            ProxyVerifyBehavior::AlwaysExistsNoIntegrityCheck => Ok(RepositoryPathVerification {
81                path,
82                state: RepositoryPathVerificationState::ExistsNoIntegrityCheck,
83            }),
84            ProxyVerifyBehavior::AlwaysExistsIntegrityMismatch => Ok(RepositoryPathVerification {
85                path,
86                state: RepositoryPathVerificationState::ExistsIntegrityMismatch,
87            }),
88            ProxyVerifyBehavior::AlwaysMissing => Ok(RepositoryPathVerification {
89                path,
90                state: RepositoryPathVerificationState::Missing,
91            }),
92        }
93    }
94
95    async fn write_path<'path, 'reader>(
96        &self,
97        path: Cow<'path, str>,
98        reader: Pin<Box<dyn AsyncRead + Send + 'reader>>,
99    ) -> Result<RepositoryWrite<'path>> {
100        let res = self.inner.write_path(path.clone(), reader).await?;
101
102        self.path_writes
103            .lock()
104            .map_err(|_| {
105                DebianError::RepositoryIoPath(
106                    path.to_string(),
107                    std::io::Error::new(
108                        std::io::ErrorKind::Other,
109                        "error acquiring write paths mutex",
110                    ),
111                )
112            })?
113            .push(path.to_string());
114
115        Ok(res)
116    }
117}