1use super::LexiconSource;
2use crate::{fetch::sources::parse_from_index_or_lexicon_file, lexicon::LexiconDoc};
3use jacquard_common::IntoStatic;
4use miette::{IntoDiagnostic, Result, miette};
5use std::collections::HashMap;
6use tempfile::TempDir;
7use tokio::process::Command;
8
9#[derive(Debug, Clone)]
10pub struct GitSource {
11 pub repo: String,
12 pub git_ref: Option<String>,
13 pub pattern: String,
14}
15
16impl LexiconSource for GitSource {
17 async fn fetch(&self) -> Result<HashMap<String, LexiconDoc<'_>>> {
18 // Create temp directory for clone
19 let temp_dir = TempDir::new().into_diagnostic()?;
20 let clone_path = temp_dir.path();
21
22 // Shallow clone
23 let mut clone_cmd = Command::new("git");
24 clone_cmd.arg("clone").arg("--depth").arg("1");
25
26 if let Some(ref git_ref) = self.git_ref {
27 clone_cmd.arg("--branch").arg(git_ref);
28 }
29
30 clone_cmd.arg(&self.repo).arg(clone_path);
31
32 let output = clone_cmd.output().await.into_diagnostic()?;
33
34 if !output.status.success() {
35 let stderr = String::from_utf8_lossy(&output.stderr);
36 return Err(miette!("Git clone failed: {}", stderr));
37 }
38
39 // Find lexicon files matching pattern
40 let mut lexicons = HashMap::new();
41
42 for entry in glob::glob(&format!("{}/{}", clone_path.display(), self.pattern))
43 .into_diagnostic()?
44 .filter_map(|e| e.ok())
45 {
46 if !entry.is_file() {
47 continue;
48 }
49
50 // Try to parse as lexicon
51 let content = tokio::fs::read_to_string(&entry).await.into_diagnostic()?;
52
53 match parse_from_index_or_lexicon_file(&content) {
54 Ok((nsid, doc)) => {
55 let doc = doc.into_static();
56 lexicons.insert(nsid, doc);
57 }
58 Err(_) => {
59 // Not a lexicon, skip
60 continue;
61 }
62 }
63 }
64
65 Ok(lexicons)
66 }
67}