Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
chrislearn committed Jan 16, 2025
1 parent 7e5999a commit 6914f33
Show file tree
Hide file tree
Showing 69 changed files with 214 additions and 304 deletions.
26 changes: 26 additions & 0 deletions src/git.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::fs::File;
use std::io::Write;
use std::path::Path;

use anyhow::Result;

use crate::utils;

pub fn init_repository(dir: &Path) -> Result<()> {
if !dir.join(".git").exists() {
// Temporary fix to work around bug in libgit2 when creating a
// directory in the root of a posix filesystem.
// See: https://github.com/libgit2/libgit2/issues/5130
utils::create_dir_all(dir)?;
git2::Repository::init(dir)?;
write_ignore_file(dir)?;
}
Ok(())
}

pub fn write_ignore_file(project_path: &Path) -> Result<()> {
let fp_ignore = project_path.join(".gitignore");
let mut fp_ignore_file = File::create(fp_ignore)?;
fp_ignore_file.write_all(b"/target\n/migration/target")?;
Ok(())
}
17 changes: 12 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ use clap::Parser;
mod test;
mod utils;
use i18n::set_locale;
use utils::check_for_updates;
mod i18n;
mod git;
mod templates;
mod project;
mod updater;
mod printer;
mod namer;

rust_i18n::i18n!("locales", fallback = "en");
#[derive(Parser, Debug)]
Expand All @@ -28,17 +33,19 @@ pub struct NewCmd {
}
#[tokio::main]
async fn main() -> Result<()> {
utils::print_logo();
printer::print_logo();
let opts: Opts = Opts::parse();
match opts.subcmd {
SubCommand::New(new_cmd) => {
set_locale(&new_cmd.lang);
check_for_updates().await;
match utils::create_project(&new_cmd) {
updater::check_for_updates().await;
match project::create(&new_cmd) {
Ok(_) => (),
Err(e) => utils::error(e.to_string()),
Err(e) => printer::error(e.to_string()),
};
}
}
Ok(())
}


1 change: 1 addition & 0 deletions src/utils/restricted_names.rs → src/namer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::bail;
use anyhow::Result;


/// Returns `true` if the name contains non-ASCII characters.
pub fn is_non_ascii_name(name: &str) -> bool {
name.chars().any(|ch| ch > '\x7f')
Expand Down
File renamed without changes.
82 changes: 82 additions & 0 deletions src/project.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::ffi::{OsStr, OsString};
use std::path::Path;
use std::{env, slice};

use anyhow::{Context, Result};
use rust_i18n::t;

use crate::printer::{self, success, warning};
use crate::namer;
use crate::NewCmd;

pub fn create(new_cmd: &NewCmd) -> Result<()> {
check_name(&new_cmd.project_name)?;
let project_name = &new_cmd.project_name;
let project_path = Path::new(project_name);
if project_path.exists() {
anyhow::bail!(t!(
"error_project_path_exist",
path = project_path.to_string_lossy()
))
}

check_path(project_path)?;
crate::templates::classic::generate(new_cmd)?;
after_print_info(project_name);
Ok(())
}

fn after_print_info(project_name: &String) {
println!(); // a new line
success(t!("create_info", project_name = project_name).replace(r"\n", "\n"));
success(t!("create_success").replace(r"\n", "\n"));
success(t!("rust_version_tip"));
println!(); // a new line
}

fn check_name(name: &str) -> Result<()> {
namer::validate_package_name(name, "package name")?;

if namer::is_keyword(name) {
anyhow::bail!(t!("error_is_keyword", name = name));
}
if namer::is_conflicting_artifact_name(name) {
warning(t!("error_is_conflicting_artifact_name", name = name).replace(r"\n", "\n"));
}
if name == "test" {
anyhow::bail!(t!("error_equal_test").replace(r"\n", "\n"))
}
if ["core", "std", "alloc", "proc_macro", "proc-macro"].contains(&name) {
warning(t!("error_part_of_standard_library", name = name,).replace(r"\n", "\n"));
}
if namer::is_windows_reserved(name) {
if cfg!(windows) {
anyhow::bail!(t!("error_is_windows_reserved", name = name),);
} else {
warning(t!("warning_is_windows_reserved", name = name).replace(r"\n", "\n"));
}
}
if namer::is_non_ascii_name(name) {
warning(t!("warning_is_non_ascii_name", name = name).replace(r"\n", "\n"));
}
Ok(())
}
fn check_path(path: &Path) -> Result<()> {
// warn if the path contains characters that will break `env::join_paths`
if join_paths(slice::from_ref(&OsStr::new(path)), "").is_err() {
let path = path.to_string_lossy();
printer::warning(t!("warning_invalid_path", path = path));
}
Ok(())
}

fn join_paths<T: AsRef<OsStr>>(paths: &[T], env: &str) -> Result<OsString> {
env::join_paths(paths.iter()).with_context(|| {
let mut message = t!("erroe_join_paths", env = env).replace(r"\n", "\n");
for path in paths {
use std::fmt::Write;
write!(&mut message, "\n {:?}", Path::new(path)).unwrap();
}
message
})
}
126 changes: 13 additions & 113 deletions src/utils/create_project.rs → src/templates/classic/mod.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,43 @@
use std::ffi::{OsStr, OsString};
use std::ffi::OsStr;
use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
use std::{env, slice};

use anyhow::{Context, Result};
use anyhow::Result;
use liquid::model::Object;
use print_util::success;
use rust_i18n::t;

use super::get_selection::{get_user_selected, UserSelected};
use super::{print_util, restricted_names, warning};
use crate::NewCmd;
use crate::printer::warning;
use crate::{git, NewCmd};

mod selection;
use selection::Selected;

#[derive(rust_embed::RustEmbed)]
#[folder = "./template"]
#[folder = "./templates/classic"]
struct Template;

pub fn create_project(new_cmd: &NewCmd) -> Result<()> {
check_name(&new_cmd.project_name)?;
let project_name = &new_cmd.project_name;
let project_path = Path::new(project_name);
if project_path.exists() {
anyhow::bail!(t!(
"error_project_path_exist",
path = project_path.to_string_lossy()
))
}

check_path(project_path)?;
let Some(config) = get_user_selected()? else {
pub fn generate(new_cmd: &NewCmd) -> Result<()> {
let Some(config) = selection::get_selected()? else {
anyhow::bail!("cli quit!")
};
match init_git(project_path) {
let project_path = Path::new(&new_cmd.project_name);
match git::init_repository(project_path) {
Ok(_) => {}
Err(e) => {
warning(t!("warning_init_git", error = e).replace(r"\n", "\n"));
}
}

create_files(project_path, config, new_cmd)?;

after_print_info(project_name);
Ok(())
}

fn after_print_info(project_name: &String) {
println!(); // a new line
success(t!("create_info", project_name = project_name).replace(r"\n", "\n"));
success(t!("create_success").replace(r"\n", "\n"));
success(t!("rust_version_tip"));
println!(); // a new line
}

pub fn create_files(
project_path: &Path,
user_selected: UserSelected,
new_cmd: &NewCmd,
) -> Result<()> {
let code_gen = user_selected.code_gen.to_string();
fn create_files(project_path: &Path, user_selected: Selected, new_cmd: &NewCmd) -> Result<()> {
let db_lib = user_selected.db_lib.to_string();
let db_type = user_selected.db_type.to_string();
let data = liquid::object!({
"project_name": new_cmd.project_name,
"code_gen":code_gen,
"db_type":db_type,
"db_lib":db_lib,
"main_log_message":t!("main_log_message"),
Expand Down Expand Up @@ -177,77 +151,3 @@ fn write_file(tmpl: &[u8], file_path: &Path, data: &Object) -> Result<()> {
}
Ok(())
}

fn check_name(name: &str) -> Result<()> {
restricted_names::validate_package_name(name, "package name")?;

if restricted_names::is_keyword(name) {
anyhow::bail!(t!("error_is_keyword", name = name));
}
if restricted_names::is_conflicting_artifact_name(name) {
warning(t!("error_is_conflicting_artifact_name", name = name).replace(r"\n", "\n"));
}
if name == "test" {
anyhow::bail!(t!("error_equal_test").replace(r"\n", "\n"))
}
if ["core", "std", "alloc", "proc_macro", "proc-macro"].contains(&name) {
warning(t!("error_part_of_standard_library", name = name,).replace(r"\n", "\n"));
}
if restricted_names::is_windows_reserved(name) {
if cfg!(windows) {
anyhow::bail!(t!("error_is_windows_reserved", name = name),);
} else {
warning(t!("warning_is_windows_reserved", name = name).replace(r"\n", "\n"));
}
}
if restricted_names::is_non_ascii_name(name) {
warning(t!("warning_is_non_ascii_name", name = name).replace(r"\n", "\n"));
}
Ok(())
}
fn check_path(path: &Path) -> Result<()> {
// warn if the path contains characters that will break `env::join_paths`
if join_paths(slice::from_ref(&OsStr::new(path)), "").is_err() {
let path = path.to_string_lossy();
print_util::warning(t!("warning_invalid_path", path = path));
}
Ok(())
}

pub fn join_paths<T: AsRef<OsStr>>(paths: &[T], env: &str) -> Result<OsString> {
env::join_paths(paths.iter()).with_context(|| {
let mut message = t!("erroe_join_paths", env = env).replace(r"\n", "\n");
for path in paths {
use std::fmt::Write;
write!(&mut message, "\n {:?}", Path::new(path)).unwrap();
}
message
})
}

pub fn init_git(project_path: &Path) -> Result<()> {
if !project_path.join(".git").exists() {
// Temporary fix to work around bug in libgit2 when creating a
// directory in the root of a posix filesystem.
// See: https://github.com/libgit2/libgit2/issues/5130
create_dir_all(project_path)?;
git2::Repository::init(project_path)?;
write_ignore_file(project_path)?;
}
Ok(())
}

fn write_ignore_file(project_path: &Path) -> Result<()> {
let fp_ignore = project_path.join(".gitignore");
let mut fp_ignore_file = File::create(fp_ignore)?;
fp_ignore_file.write_all(b"/target\n/migration/target")?;
Ok(())
}

/// Equivalent to [`create_dir_all`] with better error messages.
pub fn create_dir_all(p: impl AsRef<Path>) -> Result<()> {
let p = p.as_ref();
fs::create_dir_all(p)
.with_context(|| format!("failed to create directory `{}`", p.display()))?;
Ok(())
}
Loading

0 comments on commit 6914f33

Please sign in to comment.