use std::path::PathBuf;
use clap::{Args, Subcommand, ValueEnum};
use printable_shell_command::PrintableShellCommand;
use crate::common::{
debug::DebugPrintable,
ecosystem::Ecosystem,
package_manager::PackageManager,
template_file::{TemplateFile, TemplateFileArgs, TemplateFileCommand},
};
#[derive(Args, Debug)]
pub(crate) struct BoilerplateArgs {
#[command(subcommand)]
command: BoilerplateCommand,
}
#[derive(Debug, Subcommand)]
enum BoilerplateCommand {
CI(CIArgs),
AutoPublishGithubRelease(TemplateFileArgs),
Biome(TemplateFileArgs),
Tsconfig(TemplateFileArgs<TsconfigArgs>),
ReadmeCliHelp(TemplateFileArgs),
Bunfig(TemplateFileArgs),
RustToolchain(TemplateFileArgs),
}
#[derive(Debug, Clone, ValueEnum, Default)]
enum VCSForge {
#[default]
#[clap(name = "github")]
GitHub,
Codeberg,
}
#[derive(Args, Debug)]
pub(crate) struct CIArgs {
#[clap(long)]
forge: VCSForge,
#[command(flatten)]
template_file_args: TemplateFileArgs,
}
#[derive(Args, Clone, Debug)]
pub(crate) struct TsconfigArgs {
#[clap(long)]
no_dom: bool,
#[clap(long)]
module: Option<ESModule>,
}
#[derive(Debug, Clone, ValueEnum, Default)]
enum ESModule {
#[default]
ES2022,
ES2024,
}
fn ci_template(forge: &VCSForge) -> TemplateFile<'static> {
match forge {
VCSForge::GitHub => {
let bytes = include_bytes!("../templates/.github/workflows/CI.yaml");
TemplateFile {
relative_path: PathBuf::from("./.github/workflows/CI.yaml"),
bytes,
}
}
VCSForge::Codeberg => {
let bytes = include_bytes!("../templates/.woodpecker/CI.yaml");
TemplateFile {
relative_path: PathBuf::from("./.woodpecker/CI.yaml"),
bytes,
}
}
}
}
fn publish_github_release_template() -> TemplateFile<'static> {
let bytes = include_bytes!("../templates/.github/workflows/publish-github-release.yaml");
TemplateFile {
relative_path: PathBuf::from("./.github/workflows/publish-github-release.yaml"),
bytes,
}
}
fn biome_json_template() -> TemplateFile<'static> {
let bytes = include_bytes!("../templates/biome.json");
TemplateFile {
relative_path: PathBuf::from("./biome.json"),
bytes,
}
}
fn tsconfig_template(
template_file_command: TemplateFileCommand<TsconfigArgs>,
) -> TemplateFile<'static> {
let bytes: &[u8] = {
if let TemplateFileCommand::Add(template_file_create_args) = template_file_command {
match (
template_file_create_args.custom_args.no_dom,
template_file_create_args
.custom_args
.module
.unwrap_or_default(),
) {
(false, ESModule::ES2022) => include_bytes!("../templates/tsconfig.es2022.json"),
(false, ESModule::ES2024) => include_bytes!("../templates/tsconfig.es2024.json"),
(true, ESModule::ES2022) => {
include_bytes!("../templates/tsconfig.es2022.no-dom.json")
}
(true, ESModule::ES2024) => {
include_bytes!("../templates/tsconfig.es2024.no-dom.json")
}
}
} else {
include_bytes!("../templates/tsconfig.es2022.json")
}
};
TemplateFile {
relative_path: PathBuf::from("./tsconfig.json"),
bytes,
}
}
fn bunfig_template() -> TemplateFile<'static> {
let bytes = include_bytes!("../templates/bunfig.toml");
TemplateFile {
relative_path: PathBuf::from("./bunfig.toml"),
bytes,
}
}
fn readme_cli_help_template() -> TemplateFile<'static> {
let bytes = include_bytes!("../templates/.config/readme-cli-help.json");
TemplateFile {
relative_path: PathBuf::from("./.config/readme-cli-help.json"),
bytes,
}
}
fn rust_toolchain_template() -> TemplateFile<'static> {
let bytes = include_bytes!("../templates/rust-toolchain.toml");
TemplateFile {
relative_path: PathBuf::from("./rust-toolchain.toml"),
bytes,
}
}
fn add_biome(template_file_args: TemplateFileArgs) {
let (binary, args, biome_command_prefix) =
match PackageManager::auto_detect_preferred_package_manager_for_ecosystem(
Ecosystem::JavaScript,
) {
Some(PackageManager::Npm) => (
"npm",
[
"install",
"--save-dev",
"@biomejs/biome",
"@cubing/dev-config",
],
"bun x @biomejs/biome",
),
Some(PackageManager::Bun) => (
"bun",
[
"add",
"--development",
"@biomejs/biome",
"@cubing/dev-config",
],
"bun x @biomejs/biome",
),
Some(PackageManager::Yarn) => (
"yarn",
["add", "--dev", "@biomejs/biome", "@cubing/dev-config"],
"npx yarn exec @biomejs/biome",
),
Some(PackageManager::Pnpm) => (
"pnpm",
[
"install",
"--save-dev",
"@biomejs/biome",
"@cubing/dev-config",
],
"npx pnpm exec biome",
),
Some(PackageManager::Cargo) => panic!("unrechachable"),
None => {
panic!("No JS package detected.")
}
};
PrintableShellCommand::new(binary)
.arg_each(args)
.debug_print()
.spawn()
.expect("Could not add development dependency")
.wait()
.unwrap();
biome_json_template().handle_command(template_file_args);
println!(
"Use the following commands:
`package.json`:
\"lint\": \"{} check\"
\"format\": \"{} check --write\"
`Makefile` (make sure to convert ⇥ to tab indentation):
.PHONY: lint
lint:
⇥{} check
.PHONY: format
format:
⇥{} check --write
",
biome_command_prefix, biome_command_prefix, biome_command_prefix, biome_command_prefix,
)
}
fn add_tsconfig(template_file_args: TemplateFileArgs<TsconfigArgs>) {
let (binary, args) = match PackageManager::auto_detect_preferred_package_manager_for_ecosystem(
Ecosystem::JavaScript,
) {
Some(PackageManager::Npm) => ("npm", ["install", "--save-dev", "@cubing/dev-config"]),
Some(PackageManager::Bun) => ("bun", ["add", "--development", "@cubing/dev-config"]),
Some(PackageManager::Yarn) => ("yarn", ["add", "--dev", "@cubing/dev-config"]),
Some(PackageManager::Pnpm) => ("pnpm", ["install", "--save-dev", "@cubing/dev-config"]),
Some(PackageManager::Cargo) => panic!("unrechachable"),
None => {
panic!("No JS package detected.")
}
};
PrintableShellCommand::new(binary)
.arg_each(args)
.spawn()
.expect("Could not add development dependency")
.wait()
.unwrap();
tsconfig_template(template_file_args.command.clone()).handle_command(template_file_args);
}
fn add_bunfig(template_file_args: TemplateFileArgs) {
bunfig_template().handle_command(template_file_args);
}
fn add_readme_cli_help(template_file_args: TemplateFileArgs) {
readme_cli_help_template().handle_command(template_file_args);
}
fn add_rust_toolchain(template_file_args: TemplateFileArgs) {
rust_toolchain_template().handle_command(template_file_args);
println!(
"Use the following commands:
`Makefile` (make sure to convert ⇥ to tab indentation):
.PHONY: lint-rust
lint-rust:
⇥cargo clippy -- --deny warnings
⇥cargo fmt --check
.PHONY: format-rust
format-rust:
⇥cargo clippy --fix --allow-no-vcs
⇥cargo fmt
"
)
}
pub(crate) fn boilerplate(boilerplate_args: BoilerplateArgs) {
match boilerplate_args.command {
BoilerplateCommand::CI(ci_args) => {
ci_template(&ci_args.forge).handle_command(ci_args.template_file_args);
}
BoilerplateCommand::AutoPublishGithubRelease(template_file_args) => {
publish_github_release_template().handle_command(template_file_args);
}
BoilerplateCommand::Biome(template_file_args) => add_biome(template_file_args),
BoilerplateCommand::Tsconfig(template_file_args) => add_tsconfig(template_file_args),
BoilerplateCommand::Bunfig(template_file_args) => add_bunfig(template_file_args),
BoilerplateCommand::ReadmeCliHelp(template_file_args) => {
add_readme_cli_help(template_file_args)
}
BoilerplateCommand::RustToolchain(template_file_args) => {
add_rust_toolchain(template_file_args)
}
};
}