proplate_core/template/
resolver.rs

1use std::env::current_dir;
2use std::fs;
3use std::path::{Path, PathBuf};
4
5use proplate_tui::logger;
6use uuid::Uuid;
7
8use proplate_errors::{ProplateError, ProplateErrorKind, ProplateResult, TemplateErrorKind};
9use proplate_integration::git;
10
11use crate::join_path;
12
13use crate::{fs as pfs, template::Template};
14
15/// Attemps to find a template at the given location
16/// It can be either a local path or a github repo url
17pub fn clone_template(location: &str, dest: &str) -> ProplateResult<Template> {
18  if !is_valid_location(location) {
19    return Err(
20      ProplateError::create(ProplateErrorKind::Template {
21        kind: TemplateErrorKind::NotFound { is_remote: false },
22        location: location.into(),
23      })
24      .with_ctx("template:clone")
25      .with_cause("The location is neither a local nor a git repo"),
26    );
27  }
28  match is_remote_uri(location) {
29    true => clone_remote_template(location),
30    false => clone_local_template(location, dest),
31  }
32}
33
34fn clone_local_template(location: &str, dest: &str) -> ProplateResult<Template> {
35  // make unique id
36  let path = join_path!(".temp", format!("{}-{}", dest, Uuid::new_v4()));
37  let from = Path::new(location);
38
39  println!(
40    "{}",
41    logger::step(&format!("Cloning local template {}...", location))
42  );
43
44  pfs::copy_fdir(from, &path, None).map_err(|e| {
45    ProplateError::create(ProplateErrorKind::Fs {
46      concerned_paths: vec![from.display().to_string(), path.display().to_string()],
47      operation: "copy_fdir".into(),
48    })
49    .with_ctx("template:local:clone")
50    .with_cause(&e.to_string())
51  })?;
52
53  template_with_filebase(path.into(), location, location.into())
54}
55
56fn clone_remote_template(uri: &str) -> ProplateResult<Template> {
57  let tail = uri.strip_prefix("https://github.com/").unwrap();
58
59  // make unique id
60  let id = tail.split("/").collect::<Vec<_>>().join("-");
61  let dest = join_path!(".temp", format!("{}-{}", id, Uuid::new_v4()));
62
63  println!(
64    "{}",
65    logger::step(&format!("Cloning template from git repo {}...", uri))
66  );
67
68  // TODO: shouldn't be done here
69  git::exec_cmd(
70    ["clone", uri, dest.to_str().unwrap()],
71    &current_dir().unwrap(),
72  )
73  .map_err(|_| {
74    ProplateError::create(ProplateErrorKind::Template {
75      kind: TemplateErrorKind::NotFound { is_remote: true },
76      location: uri.into(),
77    })
78    .with_ctx("template:remote:clone")
79    .with_cause("git clone failed")
80  })?;
81
82  template_with_filebase(dest, &id, uri.to_string())
83}
84
85// TODO: move to Template struct
86/// Create a template representation based on the provided meta
87fn template_with_filebase(path: PathBuf, id: &str, source: String) -> ProplateResult<Template> {
88  let file_list = fs::read_dir(&path)
89    .map_err(|e| {
90      ProplateError::create(ProplateErrorKind::Fs {
91        concerned_paths: vec![path.display().to_string()],
92        operation: "read_dir".into(),
93      })
94      .with_ctx("template:create")
95      .with_cause(&e.to_string())
96    })?
97    .into_iter()
98    .filter_map(|e| match e {
99      Ok(entry) => entry.file_name().to_str().map(|s| s.to_string()).or(None),
100      _ => None,
101    })
102    .collect::<Vec<_>>();
103  Ok(Template::build(id.to_string(), path, file_list, source))
104}
105
106fn is_remote_uri(uri: &str) -> bool {
107  uri.starts_with("https://github.com/")
108}
109
110fn is_local_dir(uri: &str) -> bool {
111  let path = PathBuf::from(uri);
112  path.exists() && path.is_dir()
113}
114
115fn is_valid_location(location: &str) -> bool {
116  is_remote_uri(location) || is_local_dir(location)
117}