1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ContextNode {
25 pub id: String,
27
28 pub name: String,
30
31 pub node_type: NodeType,
33
34 pub quantum_state: Option<QuantumState>,
36
37 pub children: Vec<ContextNode>,
39
40 pub metadata: serde_json::Value,
42
43 pub entanglements: Vec<Entanglement>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub enum NodeType {
49 Directory,
51 File,
52
53 ApiEndpoint,
55 ApiSchema,
56 WebSocketChannel,
57
58 QuantumWave,
60 EntangledState,
61 Superposition,
62
63 EventSource,
65 EventType,
66
67 MemoryWave,
69 ConsciousnessStream,
70 EmotionalContext,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct QuantumState {
75 pub amplitude: f64,
77
78 pub frequency: f64,
80
81 pub phase: f64,
83
84 pub collapse_probability: f64,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct Entanglement {
90 pub target_id: String,
92
93 pub strength: f64,
95
96 pub relationship: String,
98}
99
100#[async_trait]
102pub trait InputAdapter: Send + Sync {
103 fn name(&self) -> &'static str;
105
106 fn supported_formats(&self) -> Vec<&'static str>;
108
109 async fn can_handle(&self, input: &InputSource) -> bool;
111
112 async fn parse(&self, input: InputSource) -> Result<ContextNode>;
114
115 fn wave_signature(&self) -> Option<String> {
117 None
118 }
119}
120
121#[derive(Debug, Clone)]
123pub enum InputSource {
124 Path(PathBuf),
126
127 Url(String),
129
130 Raw {
132 data: Vec<u8>,
133 format_hint: Option<String>,
134 },
135
136 QcpQuery { endpoint: String, query: String },
138
139 Mem8Stream {
141 stream_id: String,
142 temporal_range: Option<(i64, i64)>,
143 },
144}
145
146pub 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 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 pub async fn process(&self, input: InputSource) -> Result<ContextNode> {
173 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 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
206pub 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(), 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 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 for child in &context.children {
266 convert_node(child, nodes, depth + 1);
267 }
268}