#![forbid(unsafe_code)]
use crate::log::{ScannerLog, builder::LogImpls};
use chrono::{DateTime, Utc};
use futures::future::try_join_all;
use luct_client::Client;
use luct_core::{CtLog, Fingerprint, LogId, store::AsyncSearchableStore, v1::SignedTreeHead};
use std::collections::BTreeMap;
pub use {
config::{ScannerConfig, ScannerConfigBuilder},
error::ScannerError,
report::{Report, SctReport, SthReport},
utils::Validated,
};
type HashOutput = [u8; 32];
mod config;
mod error;
mod log;
mod report;
mod stats;
mod sth;
mod utils;
pub trait ScannerImpl {
type Client: Client + Clone;
type ReportStore: AsyncSearchableStore<Key = Fingerprint, Value = Report>;
type SthStore: AsyncSearchableStore<Key = u64, Value = Validated<SignedTreeHead>>;
}
pub struct Scanner<S: ScannerImpl> {
config: ScannerConfig,
logs: BTreeMap<LogId, ScannerLog<S>>,
report_store: S::ReportStore,
client: S::Client,
time_source: Box<dyn Fn() -> DateTime<Utc>>,
}
#[allow(clippy::type_complexity)]
impl<S: ScannerImpl> Scanner<S> {
pub fn logs<'a>(&'a self) -> Box<dyn Iterator<Item = &'a CtLog> + 'a> {
Box::new(self.logs.values().map(|val| val.client().log()))
}
pub fn new<F: Fn() -> DateTime<Utc> + 'static>(
config: ScannerConfig,
report_store: S::ReportStore,
client: S::Client,
time_source: F,
) -> Self {
Self {
config,
logs: BTreeMap::new(),
report_store,
client,
time_source: Box::new(time_source) as _,
}
}
pub fn add_log(&mut self, log: &CtLog, sth_store: S::SthStore) -> &mut Self {
let impls = LogImpls {
client: self.client.clone(),
sth_store,
};
let scanner_log = ScannerLog::new(log, impls);
let log_id = scanner_log.client().log().log_id().clone();
self.logs.insert(log_id, scanner_log);
self
}
pub async fn refresh_all_logs(&self) -> Result<(), ScannerError> {
let updates = self
.logs
.values()
.map(|log| log.update_sth())
.collect::<Vec<_>>();
try_join_all(updates).await?;
Ok(())
}
}