sbom_walker/source/
dispatch.rs

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