A better Rust ATProto crate
at main 118 lines 3.8 kB view raw
1mod atproto; 2mod git; 3mod http; 4mod jsonfile; 5mod local; 6mod slices; 7 8pub use atproto::AtProtoSource; 9pub use git::GitSource; 10pub use http::HttpSource; 11use jacquard_common::IntoStatic; 12pub use jsonfile::JsonFileSource; 13pub use local::LocalSource; 14pub use slices::SlicesSource; 15 16use jacquard_lexicon::lexicon::LexiconDoc; 17use miette::{IntoDiagnostic, Result}; 18use std::collections::HashMap; 19use std::future::Future; 20 21#[derive(Debug, Clone)] 22pub struct Source { 23 pub name: String, 24 pub source_type: SourceType, 25 pub explicit_priority: Option<i32>, 26} 27 28impl Source { 29 /// Get effective priority based on type and explicit override 30 pub fn priority(&self) -> i32 { 31 if let Some(p) = self.explicit_priority { 32 return p; 33 } 34 35 // Default priorities 36 match &self.source_type { 37 SourceType::Local(_) => 100, // Highest - dev work 38 SourceType::JsonFile(_) => 75, // High - bundled exports 39 SourceType::Slices(_) => 60, // High-middle - slices network 40 SourceType::AtProto(_) => 50, // Middle - canonical published 41 SourceType::Http(_) => 25, // Lower middle - indexed samples 42 SourceType::Git(_) => 0, // Lowest - might be stale 43 } 44 } 45 46 pub async fn fetch(&self) -> Result<HashMap<String, LexiconDoc<'_>>> { 47 self.source_type.fetch().await 48 } 49} 50 51#[derive(Debug, Clone)] 52pub enum SourceType { 53 AtProto(AtProtoSource), 54 Git(GitSource), 55 Http(HttpSource), 56 JsonFile(JsonFileSource), 57 Local(LocalSource), 58 Slices(SlicesSource), 59} 60 61pub trait LexiconSource { 62 fn fetch(&self) -> impl Future<Output = Result<HashMap<String, LexiconDoc<'_>>>> + Send; 63} 64 65impl LexiconSource for SourceType { 66 async fn fetch(&self) -> Result<HashMap<String, LexiconDoc<'_>>> { 67 match self { 68 SourceType::AtProto(s) => s.fetch().await, 69 SourceType::Git(s) => s.fetch().await, 70 SourceType::Http(s) => s.fetch().await, 71 SourceType::JsonFile(s) => s.fetch().await, 72 SourceType::Local(s) => s.fetch().await, 73 SourceType::Slices(s) => s.fetch().await, 74 } 75 } 76} 77 78pub fn parse_from_index_or_lexicon_file( 79 content: &str, 80) -> miette::Result<(String, LexiconDoc<'static>)> { 81 let value: serde_json::Value = serde_json::from_str(content).into_diagnostic()?; 82 if let Some(map) = value.as_object() { 83 if map.contains_key("schema") && map.contains_key("authority") { 84 if let Some(schema) = map.get("schema") { 85 let schema = serde_json::to_string(schema).into_diagnostic()?; 86 match serde_json::from_str::<LexiconDoc>(&schema) { 87 Ok(doc) => { 88 let nsid = doc.id.to_string(); 89 let doc = doc.into_static(); 90 Ok((nsid, doc)) 91 } 92 Err(e) => { 93 // Not a lexicon, skip 94 Err(miette::miette!("Invalid lexicon file: {e}")) 95 } 96 } 97 } else { 98 Err(miette::miette!("Invalid lexicon file")) 99 } 100 } else if map.contains_key("id") && map.contains_key("lexicon") { 101 match serde_json::from_str::<LexiconDoc>(&content) { 102 Ok(doc) => { 103 let nsid = doc.id.to_string(); 104 let doc = doc.into_static(); 105 Ok((nsid, doc)) 106 } 107 Err(e) => { 108 // Not a lexicon, skip 109 Err(miette::miette!("Invalid lexicon file: {e}")) 110 } 111 } 112 } else { 113 Err(miette::miette!("Invalid lexicon file")) 114 } 115 } else { 116 Err(miette::miette!("Invalid lexicon file")) 117 } 118}