//! Dynamic registry of Importer implementations. Populated at boot. use std::collections::HashMap; use std::sync::Arc; use super::trait_def::{Importer, ImporterInfo}; pub struct ImporterRegistry { importers: HashMap<&'static str, Arc>, } impl ImporterRegistry { pub fn new() -> Self { Self { importers: HashMap::new(), } } /// Populate with all built-in importers. Feature flags select which ship. pub fn with_defaults() -> Self { let mut r = Self::new(); #[cfg(feature = "importer-claude")] r.register(Arc::new(super::sources::claude::ClaudeImporter)); #[cfg(feature = "importer-chatgpt")] r.register(Arc::new(super::sources::chatgpt::ChatGptImporter)); #[cfg(feature = "importer-codex")] r.register(Arc::new(super::sources::codex::CodexImporter)); r } pub fn register(&mut self, importer: Arc) { self.importers.insert(importer.source_name(), importer); } pub fn get(&self, name: &str) -> Option> { self.importers.get(name).cloned() } pub fn list(&self) -> Vec { self.importers .values() .map(|i| ImporterInfo { source_name: i.source_name().to_string(), display_name: pretty_name(i.source_name()), config_shape: i.config_shape(), }) .collect() } } fn pretty_name(source: &str) -> String { match source { "claude" => "Claude Code".into(), "chatgpt" => "ChatGPT (OpenAI)".into(), "codex" => "OpenAI Codex".into(), other => other.to_string(), } } impl Default for ImporterRegistry { fn default() -> Self { Self::with_defaults() } } #[cfg(test)] mod tests { use super::*; #[test] fn defaults_include_claude_when_feature_enabled() { let reg = ImporterRegistry::with_defaults(); let list = reg.list(); #[cfg(feature = "importer-claude")] { assert!(list.iter().any(|i| i.source_name == "claude")); assert!(reg.get("claude").is_some()); } #[cfg(not(feature = "importer-claude"))] { let _ = list; assert!(reg.get("claude").is_none()); } } #[test] fn pretty_name_known_sources() { assert_eq!(pretty_name("claude"), "Claude Code"); assert_eq!(pretty_name("chatgpt"), "ChatGPT (OpenAI)"); assert_eq!(pretty_name("codex"), "OpenAI Codex"); assert_eq!(pretty_name("custom"), "custom"); } }