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 "last changed" date from the change information
63    pub modified: SystemTime,
64}
65
66/// Get a document as [`DiscoveredAdvisory`]
67pub trait AsDiscovered: Debug {
68    fn as_discovered(&self) -> &DiscoveredAdvisory;
69}
70
71impl AsDiscovered for DiscoveredAdvisory {
72    fn as_discovered(&self) -> &DiscoveredAdvisory {
73        self
74    }
75}
76
77impl Urlify for DiscoveredAdvisory {
78    fn url(&self) -> &Url {
79        &self.url
80    }
81
82    fn relative_base_and_url(&self) -> Option<(&Url, String)> {
83        self.context
84            .url()
85            .make_relative(&self.url)
86            .map(|relative| (self.context.url(), relative))
87    }
88}
89
90#[derive(Debug)]
91pub struct DiscoveredContext<'c> {
92    pub metadata: &'c ProviderMetadata,
93}
94
95/// Visiting discovered advisories
96pub trait DiscoveredVisitor {
97    type Error: std::fmt::Display + Debug;
98    type Context;
99
100    fn visit_context(
101        &self,
102        context: &DiscoveredContext,
103    ) -> impl Future<Output = Result<Self::Context, Self::Error>>;
104
105    fn visit_advisory(
106        &self,
107        context: &Self::Context,
108        advisory: DiscoveredAdvisory,
109    ) -> impl Future<Output = Result<(), Self::Error>>;
110}
111
112impl<F, E, Fut> DiscoveredVisitor for F
113where
114    F: Fn(DiscoveredAdvisory) -> Fut,
115    Fut: Future<Output = Result<(), E>>,
116    E: std::fmt::Display + Debug,
117{
118    type Error = E;
119    type Context = ();
120
121    async fn visit_context(
122        &self,
123        _context: &DiscoveredContext<'_>,
124    ) -> Result<Self::Context, Self::Error> {
125        Ok(())
126    }
127
128    async fn visit_advisory(
129        &self,
130        _ctx: &Self::Context,
131        advisory: DiscoveredAdvisory,
132    ) -> Result<(), Self::Error> {
133        self(advisory).await
134    }
135}