csaf_walker/source/
dispatch.rs

1use super::{HttpSourceError, Source};
2use crate::discover::{DiscoveredAdvisory, DistributionContext};
3use crate::model::metadata::ProviderMetadata;
4use crate::retrieve::RetrievedAdvisory;
5use crate::source::{FileSource, HttpSource};
6use walker_common::{
7    utils::openpgp::PublicKey,
8    validate::source::{Key, KeySource, KeySourceError, MapSourceError},
9};
10
11/// A common source type, dispatching to the known implementations.
12///
13/// This helps to create implementations which don't need to know the exact type. Unfortunately we
14/// cannot just "box" this, as the [`Source`] needs to implement [`Clone`], which requires [`Sized`],
15/// which prevents us from using `dyn` ("cannot be made into an object").
16///
17/// There may be a better way around this, feel free to send a PR ;-)
18#[derive(Clone, Debug)]
19pub enum DispatchSource {
20    File(FileSource),
21    Http(HttpSource),
22}
23
24impl From<FileSource> for DispatchSource {
25    fn from(value: FileSource) -> Self {
26        Self::File(value)
27    }
28}
29
30impl From<HttpSource> for DispatchSource {
31    fn from(value: HttpSource) -> Self {
32        Self::Http(value)
33    }
34}
35
36#[derive(Debug, thiserror::Error)]
37pub enum DispatchSourceError {
38    #[error(transparent)]
39    File(anyhow::Error),
40    #[error(transparent)]
41    Http(HttpSourceError),
42}
43
44impl walker_common::source::Source for DispatchSource {
45    type Error = DispatchSourceError;
46    type Retrieved = RetrievedAdvisory;
47}
48
49impl Source for DispatchSource {
50    async fn load_metadata(&self) -> Result<ProviderMetadata, Self::Error> {
51        match self {
52            Self::File(source) => source
53                .load_metadata()
54                .await
55                .map_err(DispatchSourceError::File),
56            Self::Http(source) => source
57                .load_metadata()
58                .await
59                .map_err(DispatchSourceError::Http),
60        }
61    }
62
63    async fn load_index(
64        &self,
65        context: DistributionContext,
66    ) -> Result<Vec<DiscoveredAdvisory>, Self::Error> {
67        match self {
68            Self::File(source) => source
69                .load_index(context)
70                .await
71                .map_err(DispatchSourceError::File),
72            Self::Http(source) => source
73                .load_index(context)
74                .await
75                .map_err(DispatchSourceError::Http),
76        }
77    }
78
79    async fn load_advisory(
80        &self,
81        advisory: DiscoveredAdvisory,
82    ) -> Result<RetrievedAdvisory, Self::Error> {
83        match self {
84            Self::File(source) => source
85                .load_advisory(advisory)
86                .await
87                .map_err(DispatchSourceError::File),
88            Self::Http(source) => source
89                .load_advisory(advisory)
90                .await
91                .map_err(DispatchSourceError::Http),
92        }
93    }
94}
95
96impl KeySource for DispatchSource {
97    type Error = anyhow::Error;
98
99    async fn load_public_key(
100        &self,
101        key: Key<'_>,
102    ) -> Result<PublicKey, KeySourceError<Self::Error>> {
103        match self {
104            Self::File(source) => source.load_public_key(key).await,
105            Self::Http(source) => source
106                .load_public_key(key)
107                .await
108                .map_source(|err| err.into()),
109        }
110    }
111}