1#![forbid(unsafe_code)]
4
5use crate::log::{ScannerLog, builder::LogImpls};
6use chrono::{DateTime, Utc};
7use futures::future::try_join_all;
8use luct_client::Client;
9use luct_core::{CtLog, Fingerprint, LogId, store::AsyncSearchableStore, v1::SignedTreeHead};
10use std::collections::BTreeMap;
11pub use {
12 config::{ScannerConfig, ScannerConfigBuilder},
13 error::ScannerError,
14 report::{Report, SctReport, SthReport},
15 utils::Validated,
16};
17
18type HashOutput = [u8; 32];
19
20mod config;
21mod error;
22mod log;
23mod report;
24mod stats;
25mod sth;
26mod utils;
27
28pub trait ScannerImpl {
32 type Client: Client + Clone;
34 type ReportStore: AsyncSearchableStore<Key = Fingerprint, Value = Report>;
36 type SthStore: AsyncSearchableStore<Key = u64, Value = Validated<SignedTreeHead>>;
38}
39
40pub struct Scanner<S: ScannerImpl> {
45 config: ScannerConfig,
46 logs: BTreeMap<LogId, ScannerLog<S>>,
47 report_store: S::ReportStore,
48 client: S::Client,
49 time_source: Box<dyn Fn() -> DateTime<Utc>>,
50}
51
52#[allow(clippy::type_complexity)]
53impl<S: ScannerImpl> Scanner<S> {
54 pub fn logs<'a>(&'a self) -> Box<dyn Iterator<Item = &'a CtLog> + 'a> {
55 Box::new(self.logs.values().map(|val| val.client().log()))
56 }
57
58 pub fn new<F: Fn() -> DateTime<Utc> + 'static>(
59 config: ScannerConfig,
60 report_store: S::ReportStore,
61 client: S::Client,
62 time_source: F,
63 ) -> Self {
64 Self {
65 config,
66 logs: BTreeMap::new(),
67 report_store,
68 client,
69 time_source: Box::new(time_source) as _,
70 }
71 }
72
73 pub fn add_log(&mut self, log: &CtLog, sth_store: S::SthStore) -> &mut Self {
74 let impls = LogImpls {
75 client: self.client.clone(),
76 sth_store,
77 };
78 let scanner_log = ScannerLog::new(log, impls);
79 let log_id = scanner_log.client().log().log_id().clone();
80
81 self.logs.insert(log_id, scanner_log);
82 self
83 }
84
85 pub async fn refresh_all_logs(&self) -> Result<(), ScannerError> {
87 let updates = self
88 .logs
89 .values()
90 .map(|log| log.update_sth())
91 .collect::<Vec<_>>();
92
93 try_join_all(updates).await?;
94
95 Ok(())
96 }
97}