A better Rust ATProto crate
at main 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 crate::lexicon::LexiconDoc; 17use async_trait::async_trait; 18use miette::{IntoDiagnostic, Result}; 19use std::collections::HashMap; 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 61#[async_trait] 62pub trait LexiconSource { 63 fn fetch(&self) -> impl Future<Output = Result<HashMap<String, LexiconDoc<'_>>>>; 64} 65 66impl LexiconSource for SourceType { 67 async fn fetch(&self) -> Result<HashMap<String, LexiconDoc<'_>>> { 68 match self { 69 SourceType::AtProto(s) => s.fetch().await, 70 SourceType::Git(s) => s.fetch().await, 71 SourceType::Http(s) => s.fetch().await, 72 SourceType::JsonFile(s) => s.fetch().await, 73 SourceType::Local(s) => s.fetch().await, 74 SourceType::Slices(s) => s.fetch().await, 75 } 76 } 77} 78 79pub fn parse_from_index_or_lexicon_file( 80 content: &str, 81) -> miette::Result<(String, LexiconDoc<'static>)> { 82 let value: serde_json::Value = serde_json::from_str(content).into_diagnostic()?; 83 if let Some(map) = value.as_object() { 84 if map.contains_key("schema") && map.contains_key("authority") { 85 if let Some(schema) = map.get("schema") { 86 let schema = serde_json::to_string(schema).into_diagnostic()?; 87 match serde_json::from_str::<LexiconDoc>(&schema) { 88 Ok(doc) => { 89 let nsid = doc.id.to_string(); 90 let doc = doc.into_static(); 91 Ok((nsid, doc)) 92 } 93 Err(_) => { 94 // Not a lexicon, skip 95 Err(miette::miette!("Invalid lexicon file")) 96 } 97 } 98 } else { 99 Err(miette::miette!("Invalid lexicon file")) 100 } 101 } else if map.contains_key("id") && map.contains_key("lexicon") { 102 match serde_json::from_str::<LexiconDoc>(&content) { 103 Ok(doc) => { 104 let nsid = doc.id.to_string(); 105 let doc = doc.into_static(); 106 Ok((nsid, doc)) 107 } 108 Err(_) => { 109 // Not a lexicon, skip 110 Err(miette::miette!("Invalid lexicon file")) 111 } 112 } 113 } else { 114 Err(miette::miette!("Invalid lexicon file")) 115 } 116 } else { 117 Err(miette::miette!("Invalid lexicon file")) 118 } 119}