csaf_walker/
discover.rs

1//! Discovering
2
3use crate::model::metadata::ProviderMetadata;
4use std::fmt::Debug;
5use std::future::Future;
6use std::sync::Arc;
7use std::time::SystemTime;
8use url::Url;
9use walker_common::utils::url::Urlify;
10
11/// Discovery configuration
12pub struct DiscoverConfig {
13    /// The source to locate the provider metadata.
14    ///
15    /// This can be either a full path to a provider-metadata.json, or a base domain used by the
16    /// CSAF metadata discovery process.
17    pub source: String,
18
19    /// Only report documents which have changed since the provided date. If a document has no
20    /// change information, or this field is [`None`], it will always be reported.
21    pub since: Option<SystemTime>,
22}
23
24impl DiscoverConfig {
25    pub fn with_since(mut self, since: impl Into<Option<SystemTime>>) -> Self {
26        self.since = since.into();
27        self
28    }
29}
30
31impl From<&str> for DiscoverConfig {
32    fn from(value: &str) -> Self {
33        Self {
34            since: None,
35            source: value.to_string(),
36        }
37    }
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub enum DistributionContext {
42    Directory(Url),
43    Feed(Url),
44}
45
46impl DistributionContext {
47    /// Get the URL of the distribution
48    pub fn url(&self) -> &Url {
49        match self {
50            Self::Directory(url) => url,
51            Self::Feed(url) => url,
52        }
53    }
54}
55
56#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct DiscoveredAdvisory {
58    /// A reference to the distribution and rolie information
59    pub context: Arc<DistributionContext>,
60    /// The URL of the advisory
61    pub url: Url,
62    /// The URL of the digest
63    pub digest: Option<Url>,
64    /// The URL of the signature
65    pub signature: Option<Url>,
66    /// The "last changed" date from the change information
67    pub modified: SystemTime,
68}
69
70/// Get a document as [`DiscoveredAdvisory`]
71pub trait AsDiscovered: Debug {
72    fn as_discovered(&self) -> &DiscoveredAdvisory;
73}
74
75impl AsDiscovered for DiscoveredAdvisory {
76    fn as_discovered(&self) -> &DiscoveredAdvisory {
77        self
78    }
79}
80
81impl Urlify for DiscoveredAdvisory {
82    fn url(&self) -> &Url {
83        &self.url
84    }
85
86    fn relative_base_and_url(&self) -> Option<(&Url, String)> {
87        self.context
88            .url()
89            .make_relative(&self.url)
90            .map(|relative| (self.context.url(), relative))
91    }
92}
93
94#[derive(Debug)]
95pub struct DiscoveredContext<'c> {
96    pub metadata: &'c ProviderMetadata,
97}
98
99/// Visiting discovered advisories
100pub trait DiscoveredVisitor {
101    type Error: std::fmt::Display + Debug;
102    type Context;
103
104    fn visit_context(
105        &self,
106        context: &DiscoveredContext,
107    ) -> impl Future<Output = Result<Self::Context, Self::Error>>;
108
109    fn visit_advisory(
110        &self,
111        context: &Self::Context,
112        advisory: DiscoveredAdvisory,
113    ) -> impl Future<Output = Result<(), Self::Error>>;
114}
115
116impl<F, E, Fut> DiscoveredVisitor for F
117where
118    F: Fn(DiscoveredAdvisory) -> Fut,
119    Fut: Future<Output = Result<(), E>>,
120    E: std::fmt::Display + Debug,
121{
122    type Error = E;
123    type Context = ();
124
125    async fn visit_context(
126        &self,
127        _context: &DiscoveredContext<'_>,
128    ) -> Result<Self::Context, Self::Error> {
129        Ok(())
130    }
131
132    async fn visit_advisory(
133        &self,
134        _ctx: &Self::Context,
135        advisory: DiscoveredAdvisory,
136    ) -> Result<(), Self::Error> {
137        self(advisory).await
138    }
139}