char/sorcery
static-files based git repo viewer
git clone https://git.t4t.associates/char/sorcery
f6e267c
main
1use std:: collections:: BTreeMap ; 2use std:: fs; 3use std:: path::{ Path , PathBuf }; 4 5use anyhow::{ Context , Result }; 6 7# [ derive ( Clone , Debug )] 8pub struct Repository { 9pub user : String , 10pub name : String , 11pub path : PathBuf , 12pub description : Option < String >, 13} 14 15/// Discover git repositories exactly two levels beneath `root`: 16/// `root/user/repo` or `root/user/repo.git`. 17pub fn discover ( root : & Path ) ->Result < Vec < Repository >> { 18let root = root 19. canonicalize () 20. with_context ( ||format! ( "canonicalizing repository root {}" , root. display ())) ?; 21let mut found =BTreeMap :: new (); 22 23for userin sorted_dirs ( & root) ?{ 24let Some ( user_name) = user. file_name (). and_then ( |n| n. to_str ()) else { 25continue ; 26}; 27if user_name. starts_with ( '.' ) { 28continue ; 29} 30for pathin sorted_dirs ( & user) ?{ 31let Some ( file_name) = path. file_name (). and_then ( |n| n. to_str ()) else { 32continue ; 33}; 34if file_name. starts_with ( '.' ) { 35continue ; 36} 37let name = file_name. strip_suffix ( ".git" ). unwrap_or ( file_name); 38let Ok ( repo) = gix:: open ( & path) else { 39continue ; 40}; 41if name. is_empty () { 42continue ; 43} 44let description =crate :: generate:: description ( & repo); 45 found. entry (( user_name. to_owned (), name. to_owned ())). or_insert ( Repository { 46user : user_name. to_owned (), 47name : name. to_owned (), 48 path, 49 description, 50}); 51} 52} 53 54Ok ( found. into_values (). collect ()) 55} 56 57fn sorted_dirs ( path : & Path ) ->Result < Vec < PathBuf >> { 58let mut dirs = fs:: read_dir ( path) 59. with_context ( ||format! ( "reading {}" , path. display ())) ? 60. filter_map ( |entry| entry. ok ()) 61. filter_map ( |entry| entry. file_type (). ok () ?. is_dir (). then_some ( entry. path ())) 62. collect ::< Vec < _ >>(); 63 dirs. sort (); 64Ok ( dirs) 65} 66