git_download/
lib.rs

1use cmd_lib::run_cmd;
2use std::path::{Path, PathBuf};
3
4/// Create a downloader whose target is the repository.
5/// You can use any form of url which is allowed in `git remote add`.
6/// e.g. git@github.com:akiradeveloper/git-download
7pub fn repo(path: impl Into<String>) -> Downloader {
8    Downloader::new(path)
9}
10
11struct CopyRequest {
12    from: PathBuf,
13    to: PathBuf,
14}
15pub struct Downloader {
16    repo_path: String,
17    branch_name: String,
18    out_dir: PathBuf,
19    copy_requests: Vec<CopyRequest>,
20}
21impl Downloader {
22    fn new(repo: impl Into<String>) -> Self {
23        let cur_dir = std::env::current_dir().unwrap();
24        Self {
25            repo_path: repo.into(),
26            branch_name: "master".to_owned(),
27            out_dir: cur_dir,
28            copy_requests: vec![],
29        }
30    }
31    /// Change the output directory. The default is the current dir.
32    pub fn out_dir(mut self, path: impl AsRef<Path>) -> Self {
33        self.out_dir = path.as_ref().to_owned();
34        self
35    }
36    /// Change the branch name. The default is "master".
37    pub fn branch_name(mut self, name: impl Into<String>) -> Self {
38        self.branch_name = name.into();
39        self
40    }
41    /// Add a file to copy from remote to local.
42    /// The path `src` is a relative from the repository root and the
43    /// path `dst` is a relative from `out_dir`.
44    pub fn add_file(mut self, src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Self {
45        let from = src.as_ref().to_owned();
46        let to = dst.as_ref().to_owned();
47        let req = CopyRequest { from, to };
48        self.copy_requests.push(req);
49        self
50    }
51    /// Execute downloading.
52    pub fn exec(self) -> anyhow::Result<()> {
53        let old_pwd = std::env::current_dir()?;
54
55        let dir = tempfile::tempdir()?;
56        let dir_path = dir.path();
57        std::env::set_current_dir(dir_path)?;
58
59        let repo = &self.repo_path;
60        run_cmd! {
61            git init .;
62            git config core.sparsecheckout true;
63            git remote add origin $repo;
64        }?;
65
66        for req in &self.copy_requests {
67            let from = &req.from;
68            run_cmd! {
69                echo $from >> .git/info/sparse-checkout;
70            }?;
71        }
72
73        let branch_name = &self.branch_name;
74        run_cmd! {
75            git pull origin $branch_name;
76        }?;
77
78        for req in &self.copy_requests {
79            let from = &req.from;
80            let to = &req.to;
81            let to = self.out_dir.join(to);
82            let to_dir = to.parent().unwrap();
83            if !to_dir.exists() {
84                std::fs::create_dir_all(to_dir)?;
85            }
86            run_cmd! {
87                mv $from $to;
88            }?;
89        }
90
91        std::env::set_current_dir(old_pwd)?;
92        Ok(())
93    }
94}