dinopkg/crates/dinopkg-cli/src/command/init.rs
2024-07-15 20:27:59 +01:00

131 lines
4.5 KiB
Rust

use std::env;
use camino::Utf8PathBuf;
use color_eyre::eyre::eyre;
use color_eyre::Result;
use dialoguer::{theme::ColorfulTheme, Confirm, Input};
use dinopkg_package_json::PackageJson;
use gix_config::File as GitConfigFile;
use maplit::hashmap;
use owo_colors::OwoColorize;
use syntect::easy::HighlightLines;
use syntect::highlighting::{Style, ThemeSet};
use syntect::parsing::SyntaxSet;
use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
use tokio::fs;
pub async fn init() -> Result<()> {
// FIXME: one instance should be loaded at start
let ps = SyntaxSet::load_defaults_newlines();
let ts = ThemeSet::load_defaults();
let syntax = ps
.find_syntax_by_extension("json")
.ok_or(eyre!("failed to load json syntax"))?;
let mut h = HighlightLines::new(syntax, &ts.themes["base16-eighties.dark"]);
// Get some project/env specific info to make the defaults more relevant
let current_dir = Utf8PathBuf::try_from(env::current_dir()?)?;
let current_dir_name = current_dir.file_name().unwrap_or("package");
// FIXME: this blocks the event loop
let git_config_file = GitConfigFile::from_git_dir(current_dir.join(".git").into());
let git_repo_url = git_config_file
.map(|config| {
config
.section("remote", Some("origin".into()))
.ok()
.and_then(|remote_section| {
remote_section
.body()
.value("url")
.map(|url| url.to_string())
})
})
.ok()
.flatten()
.map(|url| url.replace("git@github.com", "https://github.com/"));
// Now, onto the questions!
let package_name: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Package name")
.default(current_dir_name.into())
.interact_text()?;
let version: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Package version")
.default("1.0.0".into())
.interact_text()?;
let private = Confirm::with_theme(&ColorfulTheme::default())
.with_prompt("Is this a private package?")
.default(true)
.interact()?;
let description: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Description")
.allow_empty(true)
.interact_text()?;
let entry_point: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Entry point")
.default("index.js".into())
.interact_text()?;
let test_command: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Test command")
.default("echo \"Error: no test specified\" && exit 1".into())
.interact_text()?;
let git_repository: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Git repository")
.default(git_repo_url.unwrap_or_default())
.interact_text()?;
let author: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Author name")
.allow_empty(true)
.interact_text()?;
let license: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("License")
.default("MIT".into())
.interact_text()?;
let package_json = PackageJson {
name: package_name,
version,
author: Some(author),
repository: Some(git_repository),
license: Some(license),
description: Some(description),
private,
main: Some(entry_point),
scripts: Some(hashmap! {
"test".into() => test_command,
}),
dependencies: None,
dev_dependencies: None,
};
println!(
"\nI will write the following output to {}:",
"`package.json`".purple()
);
let output = serde_json::to_string_pretty(&package_json)?;
for line in LinesWithEndings::from(&output) {
let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap();
let escaped = as_24_bit_terminal_escaped(&ranges[..], false);
print!("{}", escaped);
}
println!("\x1b[0m");
let yes = Confirm::with_theme(&ColorfulTheme::default())
.with_prompt("Is this okay?")
.default(true)
.interact()?;
if yes {
fs::write("package.json", output).await?;
println!(
"Successfully wrote new {} for {}!",
"`package.json`".purple(),
package_json.name.purple()
);
} else {
println!("{}", "Cancelled.".bold().red())
}
Ok(())
}