use std::future::Future;
use serde_json::Value;
#[derive(Debug, Clone)]
pub enum ScanMode {
Standalone,
Attached { session_id: String },
}
#[derive(Debug, Clone)]
pub struct ScanResult {
pub method: String,
pub result: Value,
}
#[derive(Debug)]
pub enum ScanError {
Transport(String),
UnsupportedMode(ScanMode),
Aborted(String),
}
impl std::fmt::Display for ScanError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Transport(m) => write!(f, "upstream transport error: {m}"),
Self::UnsupportedMode(mode) => write!(f, "scan mode {mode:?} not supported"),
Self::Aborted(m) => write!(f, "scan aborted: {m}"),
}
}
}
impl std::error::Error for ScanError {}
pub trait SchemaScanner: Send + Sync + 'static {
fn scan(
&self,
upstream_id: &str,
mode: ScanMode,
) -> impl Future<Output = Result<Vec<ScanResult>, ScanError>> + Send;
}
#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
use super::*;
struct MockScanner;
impl SchemaScanner for MockScanner {
async fn scan(
&self,
_upstream_id: &str,
_mode: ScanMode,
) -> Result<Vec<ScanResult>, ScanError> {
Ok(vec![])
}
}
#[tokio::test]
async fn scanner_trait__has_at_least_one_impl() {
let s = MockScanner;
let out = s.scan("my-proxy", ScanMode::Standalone).await.unwrap();
assert!(out.is_empty());
}
#[test]
fn scan_error__display_covers_all_variants() {
assert_eq!(
ScanError::Transport("boom".into()).to_string(),
"upstream transport error: boom"
);
assert!(
ScanError::UnsupportedMode(ScanMode::Standalone)
.to_string()
.contains("not supported")
);
assert_eq!(
ScanError::Aborted("cancelled".into()).to_string(),
"scan aborted: cancelled"
);
}
}