1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//! Discovering

use crate::model::metadata;
use crate::model::metadata::SourceMetadata;
use std::fmt::Debug;
use std::future::Future;
use std::ops::Deref;
use std::time::SystemTime;
use url::Url;
use walker_common::utils::url::Urlify;

/// Discovery configuration
pub struct DiscoverConfig {
    /// The URL to locate the provider metadata.
    ///
    /// If `full` is `true`, this must be the full path to the `provider-metadata.json`, otherwise
    /// it `/.well-known/csaf/provider-metadata.json` will be appended.
    pub source: String,

    /// Only report documents which have changed since the provided date. If a document has no
    /// change information, or this field is [`None`], it wil always be reported.
    pub since: Option<SystemTime>,

    /// Keys which can be used for validation
    pub keys: Vec<metadata::Key>,
}

impl DiscoverConfig {
    pub fn with_since(mut self, since: impl Into<Option<SystemTime>>) -> Self {
        self.since = since.into();
        self
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DiscoveredSbom {
    /// The URL of the SBOM
    pub url: Url,
    /// The "last changed" date from the change information
    pub modified: SystemTime,
}

impl Urlify for DiscoveredSbom {
    fn url(&self) -> &Url {
        &self.url
    }
}

#[derive(Debug)]
pub struct DiscoveredContext<'c> {
    pub metadata: &'c SourceMetadata,
}

impl<'c> Deref for DiscoveredContext<'c> {
    type Target = SourceMetadata;

    fn deref(&self) -> &Self::Target {
        self.metadata
    }
}

/// Visiting discovered SBOMs
pub trait DiscoveredVisitor {
    type Error: std::fmt::Display + Debug;
    type Context;

    fn visit_context(
        &self,
        context: &DiscoveredContext,
    ) -> impl Future<Output = Result<Self::Context, Self::Error>>;

    fn visit_sbom(
        &self,
        context: &Self::Context,
        sbom: DiscoveredSbom,
    ) -> impl Future<Output = Result<(), Self::Error>>;
}

impl<F, E, Fut> DiscoveredVisitor for F
where
    F: Fn(DiscoveredSbom) -> Fut,
    Fut: Future<Output = Result<(), E>>,
    E: std::fmt::Display + Debug,
{
    type Error = E;
    type Context = ();

    async fn visit_context(
        &self,
        _context: &DiscoveredContext<'_>,
    ) -> Result<Self::Context, Self::Error> {
        Ok(())
    }

    async fn visit_sbom(
        &self,
        _context: &Self::Context,
        sbom: DiscoveredSbom,
    ) -> Result<(), Self::Error> {
        self(sbom).await
    }
}