1use clap::Parser;
4use git2::Repository;
5use regex::Regex;
6use std::fs;
7use std::fs::remove_dir_all;
8use std::path::Path;
9use walkdir::{DirEntry, WalkDir};
10use zino_core::error::Error;
11
12mod deploy;
13mod init;
14mod new;
15mod serve;
16
17#[derive(Parser)]
19#[clap(name = "zino", version)]
20pub struct Cli {
21 #[clap(global = true, long)]
23 bin: Option<String>,
24 #[clap(subcommand)]
26 action: Subcommands,
27 #[clap(long)]
29 verbose: bool,
30}
31
32impl Cli {
33 #[inline]
35 pub fn action(self) -> Subcommands {
36 self.action
37 }
38}
39
40#[derive(Parser)]
42pub enum Subcommands {
43 Init(init::Init),
45 New(new::New),
47 Serve(serve::Serve),
49 Deploy(deploy::Deploy),
51}
52
53pub(crate) static TEMPORARY_TEMPLATE_PATH: &str = "./temporary_zino_template";
55
56pub(crate) static DEFAULT_TEMPLATE_URL: &str =
58 "https://github.com/zino-rs/zino-template-default.git";
59
60pub(crate) fn clone_and_process_template(
62 template_url: &str,
63 target_path_prefix: &str,
64 project_name: &str,
65) -> Result<(), Error> {
66 Repository::clone(template_url, TEMPORARY_TEMPLATE_PATH)?;
67
68 for entry in WalkDir::new(TEMPORARY_TEMPLATE_PATH)
69 .into_iter()
70 .filter_entry(|e| !is_ignored(e))
71 {
72 let entry = entry?;
73 if entry.file_type().is_file() {
74 let template_file_path = entry.path();
75 let target_path = format!(
76 ".{}/{}",
77 target_path_prefix,
78 template_file_path
79 .strip_prefix(TEMPORARY_TEMPLATE_PATH)?
80 .to_str()
81 .ok_or_else(|| Error::new(
82 "fail to convert the template file path to string"
83 ))?
84 );
85 fs::create_dir_all(Path::new(&target_path).parent().unwrap())?;
86
87 let content =
88 fs::read_to_string(template_file_path)?.replace("{project-name}", project_name);
89 fs::write(&target_path, content)?;
90 }
91 }
92
93 Ok(())
94}
95
96fn is_ignored(entry: &DirEntry) -> bool {
98 entry.file_name().to_str().map_or(false, |s| {
99 s.starts_with('.') || s == "LICENSE" || s == "README.md"
100 })
101}
102
103fn clean_template_dir(path: &str) {
105 let _ = remove_dir_all(path);
106}
107
108pub(crate) fn check_package_name_validation(name: &str) -> Result<(), Error> {
110 Regex::new(r"^[a-zA-Z][a-zA-Z0-9_-]*$")
111 .map_err(|e| Error::new(e.to_string()))?
112 .is_match(name)
113 .then_some(())
114 .ok_or_else(|| Error::new(format!("invalid package name: `{}`", name)))
115}