mcpr_core/protocol/schema_manager/
scanner.rs1use std::future::Future;
8
9use serde_json::Value;
10
11#[derive(Debug, Clone)]
13pub enum ScanMode {
14 Standalone,
19 Attached { session_id: String },
25}
26
27#[derive(Debug, Clone)]
33pub struct ScanResult {
34 pub method: String,
35 pub result: Value,
36}
37
38#[derive(Debug)]
40pub enum ScanError {
41 Transport(String),
42 UnsupportedMode(ScanMode),
43 Aborted(String),
44}
45
46impl std::fmt::Display for ScanError {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 Self::Transport(m) => write!(f, "upstream transport error: {m}"),
50 Self::UnsupportedMode(mode) => write!(f, "scan mode {mode:?} not supported"),
51 Self::Aborted(m) => write!(f, "scan aborted: {m}"),
52 }
53 }
54}
55
56impl std::error::Error for ScanError {}
57
58pub trait SchemaScanner: Send + Sync + 'static {
64 fn scan(
65 &self,
66 upstream_id: &str,
67 mode: ScanMode,
68 ) -> impl Future<Output = Result<Vec<ScanResult>, ScanError>> + Send;
69}
70
71#[cfg(test)]
72#[allow(non_snake_case)]
73mod tests {
74 use super::*;
75
76 struct MockScanner;
77
78 impl SchemaScanner for MockScanner {
79 async fn scan(
80 &self,
81 _upstream_id: &str,
82 _mode: ScanMode,
83 ) -> Result<Vec<ScanResult>, ScanError> {
84 Ok(vec![])
85 }
86 }
87
88 #[tokio::test]
89 async fn scanner_trait__has_at_least_one_impl() {
90 let s = MockScanner;
91 let out = s.scan("my-proxy", ScanMode::Standalone).await.unwrap();
92 assert!(out.is_empty());
93 }
94
95 #[test]
96 fn scan_error__display_covers_all_variants() {
97 assert_eq!(
98 ScanError::Transport("boom".into()).to_string(),
99 "upstream transport error: boom"
100 );
101 assert!(
102 ScanError::UnsupportedMode(ScanMode::Standalone)
103 .to_string()
104 .contains("not supported")
105 );
106 assert_eq!(
107 ScanError::Aborted("cancelled".into()).to_string(),
108 "scan aborted: cancelled"
109 );
110 }
111}