csaf_walker/source/
descriptor.rs

1use crate::{
2    discover::DiscoverConfig,
3    metadata::MetadataRetriever,
4    source::{DispatchSource, FileOptions, FileSource, HttpOptions, HttpSource},
5};
6use anyhow::bail;
7use fluent_uri::UriRef;
8use std::path::PathBuf;
9use std::str::FromStr;
10use url::Url;
11use walker_common::fetcher::{Fetcher, FetcherOptions};
12
13/// A descriptor of the source.
14#[derive(Clone, Debug)]
15pub enum SourceDescriptor {
16    /// A local file source
17    File(PathBuf),
18    /// A remote URL source, pointing to the `provider-metadata.json`
19    Url(Url),
20    /// A source discovered by the lookup process, given the domain.
21    Lookup(String),
22}
23
24impl FromStr for SourceDescriptor {
25    type Err = anyhow::Error;
26
27    fn from_str(source: &str) -> Result<Self, Self::Err> {
28        match UriRef::parse(source) {
29            Ok(uri) => match uri.scheme().map(|s| s.as_str()) {
30                Some("https") => Ok(SourceDescriptor::Url(Url::parse(source)?)),
31                Some("file") => Ok(SourceDescriptor::File(PathBuf::from(uri.path().as_str()))),
32                Some(other) => bail!("URLs with scheme '{other}' are not supported"),
33                None => Ok(SourceDescriptor::Lookup(source.to_string())),
34            },
35            Err(err) => {
36                log::debug!("Failed to handle source as URL: {err}");
37                Ok(SourceDescriptor::Lookup(source.to_string()))
38            }
39        }
40    }
41}
42
43impl SourceDescriptor {
44    /// Parse a string into a source descriptor.
45    pub fn parse(source: impl AsRef<str>) -> anyhow::Result<Self> {
46        Self::from_str(source.as_ref())
47    }
48
49    /// If possible, turn this into a source.
50    pub async fn into_source(
51        self,
52        discover: DiscoverConfig,
53        fetcher: FetcherOptions,
54    ) -> anyhow::Result<DispatchSource> {
55        match self {
56            Self::File(path) => {
57                Ok(FileSource::new(path, FileOptions::new().since(discover.since))?.into())
58            }
59            Self::Url(url) => Ok(HttpSource::new(
60                url,
61                Fetcher::new(fetcher).await?,
62                HttpOptions::new().since(discover.since),
63            )
64            .into()),
65            Self::Lookup(source) => {
66                let fetcher = Fetcher::new(fetcher).await?;
67                Ok(HttpSource::new(
68                    MetadataRetriever::new(source),
69                    fetcher,
70                    HttpOptions::new().since(discover.since),
71                )
72                .into())
73            }
74        }
75    }
76}