typst local package (tlp) manager
1#[cfg(feature = "git2")]
2use std::error::Error;
3use std::path::Path;
4#[cfg(not(feature = "git2"))]
5use std::process::Command;
6
7#[allow(unused)]
8use snafu::{OptionExt as _, ResultExt as _, Whatever};
9
10#[cfg(feature = "git2")]
11type GitError = Box<dyn Error>;
12#[cfg(not(feature = "git2"))]
13type GitError = Whatever;
14
15pub fn init(at: &Path, _git: &Path) -> Result<(), GitError> {
16 #[cfg(feature = "git2")]
17 git2::Repository::init_opts(
18 at,
19 git2::RepositoryInitOptions::new()
20 .no_reinit(true)
21 .mkdir(false)
22 .mkpath(false)
23 .external_template(false),
24 )?;
25
26 #[cfg(not(feature = "git2"))]
27 run(at, _git, ["init", "-q"], []).whatever_context("failed to run `git init`")?;
28
29 Ok(())
30}
31
32pub fn commit(at: &Path, files: &str, msg: &str, _git: &Path, _root: bool) -> Result<(), Whatever> {
33 #[cfg(feature = "git2")]
34 {
35 let repo = git2::Repository::open(at).whatever_context("failed to access repository")?;
36 let mut ind = repo.index().whatever_context("failed to get index")?;
37 ind.add_all([files].iter(), git2::IndexAddOption::DEFAULT, None)
38 .whatever_context("failed to add files to index")?;
39 ind.write().whatever_context("failed to write index")?;
40 let oid = ind
41 .write_tree()
42 .whatever_context("failed to write index tree")?;
43 let tree = repo.find_tree(oid).whatever_context("failed to get tree")?;
44 let sig = repo
45 .signature()
46 .whatever_context("failed to obtain signature")?;
47 let parent = (!_root)
48 .then(|| {
49 repo.head()
50 .whatever_context("failed to get HEAD")?
51 .peel_to_commit()
52 .whatever_context("failed to get HEAD commit")
53 })
54 .transpose()?;
55 repo.commit(
56 Some("HEAD"),
57 &sig,
58 &sig,
59 msg,
60 &tree,
61 parent.as_ref().as_slice(),
62 )
63 .whatever_context("failed to create commit")?;
64 }
65
66 #[cfg(not(feature = "git2"))]
67 {
68 run(at, _git, ["add", files], []).whatever_context("failed to run `git add *`")?;
69 run(at, _git, ["commit", "-qm", msg], []).whatever_context("failed to run `git commit`")?;
70 }
71
72 Ok(())
73}
74
75pub fn tag_and_worktree(
76 source: &Path,
77 dest: &Path,
78 tag: &str,
79 _git: &Path,
80) -> Result<(), Whatever> {
81 #[cfg(feature = "git2")]
82 {
83 let repo =
84 git2::Repository::open(source).whatever_context("failed to access repository")?;
85 let head = repo
86 .head()
87 .whatever_context("failed to get HEAD")?
88 .peel(git2::ObjectType::Any)
89 .whatever_context("failed to get HEAD object")?;
90 repo.tag_lightweight(&format!("v{tag}"), &head, false)
91 .whatever_context("failed to create tag")?;
92
93 repo.worktree(tag, &dest.join(tag), None)
94 .whatever_context("failed to create worktree")?;
95 }
96
97 #[cfg(not(feature = "git2"))]
98 {
99 run(source, _git, ["tag", &format!("v{tag}")], [])
100 .whatever_context("failed to run `git tag`")?;
101 run(
102 source,
103 _git,
104 [
105 "worktree",
106 "add",
107 "-q",
108 &format!("{}", dest.join(tag).display()),
109 &format!("v{tag}"),
110 ],
111 [],
112 )
113 .whatever_context("failed to run `git worktree add`")?;
114 }
115
116 Ok(())
117}
118
119#[cfg(not(feature = "git2"))]
120fn run<const N: usize, const M: usize>(
121 at: &Path,
122 git: &Path,
123 args: [&str; N],
124 other_args: [&Path; M],
125) -> Result<(), Whatever> {
126 Command::new(git)
127 .args(args)
128 .args(other_args)
129 .current_dir(at)
130 .status()
131 .whatever_context("couldn't execute command")?
132 .success()
133 .then_some(())
134 .whatever_context("command failed")?;
135 Ok(())
136}