Skip to main content

st/inputs/
mod.rs

1//! Universal Input Adapter System for Smart Tree
2//!
3//! Transform any context source into visualizable trees:
4//! - File systems (traditional)
5//! - QCP quantum contexts
6//! - SSE event streams
7//! - OpenAPI specifications
8//! - MEM8 consciousness streams
9//! - And more...
10
11use anyhow::Result;
12use async_trait::async_trait;
13use serde::{Deserialize, Serialize};
14use std::path::PathBuf;
15
16pub mod filesystem;
17pub mod mem8;
18pub mod openapi;
19pub mod qcp;
20pub mod sse;
21
22/// Represents any kind of context node
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ContextNode {
25    /// Unique identifier
26    pub id: String,
27
28    /// Display name
29    pub name: String,
30
31    /// Node type (file, api_endpoint, quantum_state, event, etc.)
32    pub node_type: NodeType,
33
34    /// Quantum properties if applicable
35    pub quantum_state: Option<QuantumState>,
36
37    /// Child nodes
38    pub children: Vec<ContextNode>,
39
40    /// Metadata specific to the input type
41    pub metadata: serde_json::Value,
42
43    /// Entanglements with other nodes
44    pub entanglements: Vec<Entanglement>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub enum NodeType {
49    // Traditional
50    Directory,
51    File,
52
53    // API-related
54    ApiEndpoint,
55    ApiSchema,
56    WebSocketChannel,
57
58    // Quantum
59    QuantumWave,
60    EntangledState,
61    Superposition,
62
63    // Event streams
64    EventSource,
65    EventType,
66
67    // MEM8
68    MemoryWave,
69    ConsciousnessStream,
70    EmotionalContext,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct QuantumState {
75    /// Wave amplitude (0.0 - 1.0)
76    pub amplitude: f64,
77
78    /// Frequency in Hz
79    pub frequency: f64,
80
81    /// Phase offset
82    pub phase: f64,
83
84    /// Collapse probability
85    pub collapse_probability: f64,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct Entanglement {
90    /// ID of entangled node
91    pub target_id: String,
92
93    /// Strength of entanglement (0.0 - 1.0)
94    pub strength: f64,
95
96    /// Type of relationship
97    pub relationship: String,
98}
99
100/// Trait for all input adapters
101#[async_trait]
102pub trait InputAdapter: Send + Sync {
103    /// Name of the adapter
104    fn name(&self) -> &'static str;
105
106    /// Supported input formats/extensions
107    fn supported_formats(&self) -> Vec<&'static str>;
108
109    /// Can this adapter handle the given input?
110    async fn can_handle(&self, input: &InputSource) -> bool;
111
112    /// Parse input into context nodes
113    async fn parse(&self, input: InputSource) -> Result<ContextNode>;
114
115    /// Get quantum wave signature if applicable
116    fn wave_signature(&self) -> Option<String> {
117        None
118    }
119}
120
121/// Represents an input source
122#[derive(Debug, Clone)]
123pub enum InputSource {
124    /// Local file system path
125    Path(PathBuf),
126
127    /// URL (HTTP/HTTPS/WSS)
128    Url(String),
129
130    /// Raw data with format hint
131    Raw {
132        data: Vec<u8>,
133        format_hint: Option<String>,
134    },
135
136    /// QCP endpoint with query
137    QcpQuery { endpoint: String, query: String },
138
139    /// MEM8 consciousness stream
140    Mem8Stream {
141        stream_id: String,
142        temporal_range: Option<(i64, i64)>,
143    },
144}
145
146/// Universal input processor
147pub struct InputProcessor {
148    adapters: Vec<Box<dyn InputAdapter>>,
149}
150
151impl Default for InputProcessor {
152    fn default() -> Self {
153        Self::new()
154    }
155}
156
157impl InputProcessor {
158    /// Create a new input processor with all adapters
159    pub fn new() -> Self {
160        Self {
161            adapters: vec![
162                Box::new(filesystem::FileSystemAdapter),
163                Box::new(qcp::QcpAdapter::new()),
164                Box::new(sse::SseAdapter),
165                Box::new(openapi::OpenApiAdapter),
166                Box::new(mem8::Mem8Adapter),
167            ],
168        }
169    }
170
171    /// Process any input source into context nodes
172    pub async fn process(&self, input: InputSource) -> Result<ContextNode> {
173        // Find the first adapter that can handle this input
174        for adapter in &self.adapters {
175            if adapter.can_handle(&input).await {
176                eprintln!("🌊 Using {} adapter for input", adapter.name());
177                return adapter.parse(input).await;
178            }
179        }
180
181        anyhow::bail!("No adapter found for input source")
182    }
183
184    /// Auto-detect input type from string
185    pub fn detect_input_type(input: &str) -> InputSource {
186        if input.starts_with("http://") || input.starts_with("https://") {
187            InputSource::Url(input.to_string())
188        } else if input.starts_with("qcp://") {
189            let parts: Vec<&str> = input.splitn(2, "://").collect();
190            InputSource::QcpQuery {
191                endpoint: "https://qcp.q8.is".to_string(),
192                query: parts.get(1).unwrap_or(&"").to_string(),
193            }
194        } else if input.starts_with("mem8://") {
195            let stream_id = input.trim_start_matches("mem8://");
196            InputSource::Mem8Stream {
197                stream_id: stream_id.to_string(),
198                temporal_range: None,
199            }
200        } else {
201            InputSource::Path(PathBuf::from(input))
202        }
203    }
204}
205
206/// Convert context nodes to Smart Tree's FileNode format
207pub fn context_to_file_nodes(context: ContextNode) -> Vec<crate::FileNode> {
208    let mut nodes = Vec::new();
209    convert_node(&context, &mut nodes, 0);
210    nodes
211}
212
213fn convert_node(context: &ContextNode, nodes: &mut Vec<crate::FileNode>, depth: usize) {
214    use crate::scanner::FileType;
215    use crate::{FileCategory, FileNode, FilesystemType};
216    use std::time::SystemTime;
217
218    let node = FileNode {
219        path: PathBuf::from(&context.id),
220        is_dir: !context.children.is_empty(),
221        size: context
222            .metadata
223            .get("size")
224            .and_then(|s| s.as_u64())
225            .unwrap_or(0),
226        modified: SystemTime::now(), // Use metadata time if available
227        permissions: 0o755,
228        uid: 1000,
229        gid: 1000,
230        is_symlink: false,
231        is_hidden: false,
232        permission_denied: false,
233        is_ignored: false,
234        depth,
235        file_type: match context.node_type {
236            NodeType::Directory => FileType::Directory,
237            NodeType::File => FileType::RegularFile,
238            NodeType::ApiEndpoint => FileType::RegularFile,
239            NodeType::QuantumWave => FileType::RegularFile,
240            NodeType::EventSource => FileType::RegularFile,
241            NodeType::MemoryWave => FileType::RegularFile,
242            _ => FileType::RegularFile,
243        },
244        category: match context.node_type {
245            NodeType::ApiEndpoint => FileCategory::Json,
246            NodeType::QuantumWave => FileCategory::Binary,
247            NodeType::EventSource => FileCategory::Json,
248            NodeType::MemoryWave => FileCategory::Binary,
249            _ => FileCategory::Unknown,
250        },
251        search_matches: None,
252        filesystem_type: FilesystemType::Unknown,
253        git_branch: None,
254        // Smart scanning fields
255        traversal_context: None,
256        interest: None,
257        security_findings: Vec::new(),
258        change_status: None,
259        content_hash: None,
260    };
261
262    nodes.push(node);
263
264    // Recursively convert children
265    for child in &context.children {
266        convert_node(child, nodes, depth + 1);
267    }
268}