pydantic model generator for atproto lexicons
1//! lexicon file parsing
2
3use std::fs;
4use std::io;
5use std::path::Path;
6
7use atrium_lex::LexiconDoc;
8use thiserror::Error;
9
10#[derive(Error, Debug)]
11pub enum ParseError {
12 #[error("not a directory: {0}")]
13 NotADirectory(String),
14
15 #[error("io error: {0}")]
16 Io(#[from] io::Error),
17
18}
19
20/// parse all lexicon files from a directory recursively
21pub fn parse_lexicons(dir: &Path) -> Result<Vec<LexiconDoc>, ParseError> {
22 if !dir.is_dir() {
23 return Err(ParseError::NotADirectory(dir.display().to_string()));
24 }
25
26 let mut docs = Vec::new();
27 visit_dir(dir, &mut docs)?;
28 docs.sort_by(|a, b| a.id.cmp(&b.id));
29 Ok(docs)
30}
31
32fn visit_dir(dir: &Path, docs: &mut Vec<LexiconDoc>) -> Result<(), ParseError> {
33 for entry in fs::read_dir(dir)? {
34 let path = entry?.path();
35
36 if path.is_dir() {
37 visit_dir(&path, docs)?;
38 } else if path.extension().is_some_and(|e| e == "json") {
39 let content = fs::read_to_string(&path)?;
40
41 // skip non-lexicon json files silently
42 if let Ok(doc) = serde_json::from_str::<LexiconDoc>(&content) {
43 docs.push(doc);
44 }
45 }
46 }
47 Ok(())
48}