char/sorcery
static-files based git repo viewer
git clone https://git.t4t.associates/char/sorcery
42f80d8
main
1use std:: fs; 2use std:: ops:: Deref ; 3use std:: path::{ Path , PathBuf }; 4use std:: process:: Command ; 5use std:: sync::{ Arc , OnceLock }; 6use std:: time::{ SystemTime , UNIX_EPOCH }; 7 8use anyhow::{ Result , ensure}; 9 10use crate :: highlight:: Cache ; 11 12/// A fresh directory under the system temp dir, self-removes on drop. 13pub struct TempDir ( PathBuf ); 14 15impl TempDir { 16pub fn new ( label : & str ) ->Self { 17let nanos =SystemTime :: now () 18. duration_since ( UNIX_EPOCH ) 19. expect ( "clock before epoch" ) 20. as_nanos (); 21let path = std:: env:: temp_dir (). join ( format! ( 22"sorcery-{label}-{}-{nanos}" , 23 std:: process:: id (), 24)); 25 fs:: create_dir_all ( & path). expect ( "creating temp dir" ); 26TempDir ( path) 27} 28} 29 30impl Deref for TempDir { 31type Target =Path ; 32 33fn deref ( & self ) ->& Path { 34& self . 0 35} 36} 37 38impl Drop for TempDir { 39fn drop ( & mut self ) { 40let _ = fs:: remove_dir_all ( & self . 0 ); 41} 42} 43 44/// Runs git in `repo` isolated from the developer's own config (signing, 45/// hooks, templates) and returns trimmed stdout. 46pub fn git ( repo : & Path , args : & [ & str ]) ->Result < String > { 47let output =Command :: new ( "git" ) 48. current_dir ( repo) 49. args ( args) 50. env ( "GIT_CONFIG_GLOBAL" , "/dev/null" ) 51. env ( "GIT_CONFIG_NOSYSTEM" , "1" ) 52. env ( "GIT_AUTHOR_NAME" , "test" ) 53. env ( "GIT_AUTHOR_EMAIL" , "test@example" ) 54. env ( "GIT_COMMITTER_NAME" , "test" ) 55. env ( "GIT_COMMITTER_EMAIL" , "test@example" ) 56. output () ?; 57ensure! ( 58 output. status . success (), 59"git {}: {}" , 60 args. join ( " " ), 61String :: from_utf8_lossy ( & output. stderr ), 62); 63Ok ( String :: from_utf8 ( output. stdout ) ?. trim (). to_owned ()) 64} 65 66pub fn init_repo ( repo : & Path ) ->Result <()> { 67 fs:: create_dir_all ( repo) ?; 68git ( repo, & [ "init" , "-q" , "-b" , "main" ]) ?; 69Ok (()) 70} 71 72pub fn init_sha256_repo ( repo : & Path ) ->Result <()> { 73 fs:: create_dir_all ( repo) ?; 74git ( 75 repo, 76& [ "init" , "-q" , "-b" , "main" , "--object-format=sha256" ], 77) ?; 78Ok (()) 79} 80 81/// Stages everything and commits it, returning the new commit's hex id. 82pub fn commit ( repo : & Path , message : & str ) ->Result < String > { 83git ( repo, & [ "add" , "-A" ]) ?; 84git ( repo, & [ "commit" , "-qm" , message]) ?; 85git ( repo, & [ "rev-parse" , "HEAD" ]) 86} 87 88/// `Cache::new` claims a process-wide slot, so all tests share one. 89pub fn grammar_cache () ->Arc < Cache > { 90static CACHE : OnceLock < Arc < Cache >> =OnceLock :: new (); 91CACHE 92. get_or_init ( ||Arc :: new ( Cache :: new ( std:: env:: temp_dir (). join ( "sorcery-test-grammars" )))) 93. clone () 94}