pub mod constants {
pub const CONFIG_FILE: &str = "wachit.json";
}
pub mod types {
use std::{
fs::{self, File},
io::{ErrorKind, Read},
};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use crate::processor;
#[derive(Serialize, Deserialize, Debug)]
pub enum ConfigValues {
String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum PackageManager {
NPM,
PNPM,
YARN,
NIL,
}
impl Default for PackageManager {
fn default() -> Self {
PackageManager::NPM
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Config {
#[serde[default = "set_default_scripts"]]
pub scripts: Map<String, Value>,
#[serde(default = "set_default_pkg_manager")]
pub pkg_manager: PackageManager,
#[serde(default = "set_default_target")]
pub target: String,
#[serde(default = "set_default_args")]
pub args: Vec<String>,
#[serde(default = "set_default_inspect")]
pub inspect: bool,
#[serde(default = "set_default_target_index")]
pub target_index: usize,
}
impl Config {
pub fn create_default_config() -> Config {
Config {
scripts: set_default_scripts(),
args: set_default_args(),
inspect: set_default_inspect(),
pkg_manager: set_default_pkg_manager(),
target: set_default_target(),
target_index: set_default_target_index(),
}
}
pub fn new(project_dir: &str, args: Vec<String>) -> Config {
let file_conf_result = processor::load_file_config();
let mut is_config_file: bool = false;
let mut new_config: Config = match file_conf_result {
Some(_conf) => {
is_config_file = true;
_conf
}
_ => Config::create_default_config(),
};
let file_result = File::open(project_dir);
match file_result {
Ok(mut file) => {
let mut pkg_json_file = String::new();
match file.read_to_string(&mut pkg_json_file) {
Ok(_) => {
let conf: Config = serde_json::from_str(&pkg_json_file).unwrap();
new_config.scripts = conf.scripts;
}
Err(err) => {
panic!("Error reading from package.json = {:?}", err);
}
}
}
Err(err) => match err.kind() {
ErrorKind::NotFound => {
println!("Missing file: package.json");
}
ErrorKind::PermissionDenied => {
println!("The user does not have the permission to access package.json");
}
_ => {
println!("Error opening package.json:{:?}", err);
}
},
};
if is_config_file {
return new_config;
}
if args.len() < 2 {
panic!("Format: wachit [wachit options] [target or command name]")
}
new_config.args = args;
let pkg_mngr_options = vec!["pnpm-lock.yaml", "package-lock.json", "yarn.lock"];
let mut pkg_manager = PackageManager::NPM;
for (idx, opt) in pkg_mngr_options.iter().enumerate() {
match fs::exists(opt) {
Ok(true) => match idx {
0 => {
pkg_manager = PackageManager::PNPM;
break;
}
1 => {
pkg_manager = PackageManager::NPM;
break;
}
2 => {
pkg_manager = PackageManager::YARN;
break;
}
_ => {
pkg_manager = PackageManager::NIL;
break;
}
},
_ => {
continue;
}
}
}
new_config.pkg_manager = pkg_manager;
let mut target_index = 1;
let mut inspect = false;
if new_config.args[1].eq("--inspect") {
target_index = 2;
inspect = true;
}
new_config.inspect = inspect;
new_config.target_index = target_index;
let target = File::open(&new_config.args[new_config.target_index]);
let target_file: String = match target {
Ok(_) => String::from(&new_config.args[target_index]),
Err(_) => String::new(),
};
new_config.target = target_file;
new_config
}
pub fn get_command(&self) -> String {
if !self.target.contains(".") && self.inspect == true {
panic!("Missing: Node inspect file path");
} else if self.target.contains(".") {
let mut cmd = String::from("node ");
if self.inspect == true {
cmd.push_str("--inspect ");
}
cmd.push_str(&self.target);
cmd
} else {
let command: String = match self.scripts.get(&self.target).and_then(|v| v.as_str())
{
Some(cmd) => String::from(cmd),
_ => {
let mut cmd_str = match self.pkg_manager {
PackageManager::NPM => String::from("npm run "),
PackageManager::PNPM => String::from("pnpm run "),
PackageManager::YARN => String::from("yarn "),
PackageManager::NIL => String::from("node "),
};
cmd_str.push_str(&self.target);
cmd_str
}
};
return command;
}
}
pub fn get_arguments(&self) -> &Vec<String> {
&self.args
}
}
fn set_default_pkg_manager() -> PackageManager {
PackageManager::NPM
}
fn set_default_scripts() -> Map<String, Value> {
Map::new()
}
fn set_default_target() -> String {
String::new()
}
fn set_default_args() -> Vec<String> {
Vec::new()
}
fn set_default_inspect() -> bool {
false
}
fn set_default_target_index() -> usize {
1
}
}
pub mod processor {
use crate::constants::CONFIG_FILE;
use std::{
fs::File,
io::Read,
process::{Child, Command, Stdio},
};
use crate::types::Config;
pub fn load_file_config() -> Option<Config> {
let conf_file = File::open(&CONFIG_FILE);
match conf_file {
Ok(mut config_file) => {
let mut config_string: String = String::new();
match config_file.read_to_string(&mut config_string) {
Ok(_) => {
let conf: Config = serde_json::from_str(&config_string).unwrap();
Some(conf)
}
_ => None,
}
}
_ => None,
}
}
pub fn run_command(config: &Config) -> Option<Child> {
let cmd_v: Vec<String> = config
.get_command()
.split(" ")
.map(|str| str.trim().to_string())
.collect();
println!("Starting wachit process...");
let base_cmd = &cmd_v[0];
let args = &cmd_v[1..];
let mut command = Command::new(base_cmd);
command.stdout(Stdio::piped());
command.args(args);
if config.target.len() > 0 {
command.args([&config.target]);
}
println!("{:?}", config.get_command());
match command.spawn() {
Ok(child) => Some(child),
Err(err) => {
println!("{:?}", err);
None
}
}
}
pub fn should_ignore_path(path: &String) -> bool {
if path.contains("wachit.json")
|| path.contains("build")
|| path.contains("node_modules")
|| path.ends_with(".env")
|| path.ends_with(".gitignore")
|| path.ends_with(".next")
|| path.ends_with(".git")
|| path.contains("target")
|| path.contains(".vscode")
|| path.contains("dist")
|| (!(path.ends_with(".ts"))
&& !(path.ends_with(".js"))
&& !(path.ends_with(".tsx"))
&& !(path.ends_with(".jsx"))
&& !(path.ends_with(".json")))
{
return true;
}
return false;
}
}
#[cfg(test)]
mod tests {
use crate::types::{Config, PackageManager};
use std::fs;
#[test]
fn test_read_pkg_json() {
let _: Config = Config::new("./test_data/package.json", vec![]);
assert!(true);
}
#[test]
fn test_read_pkg_json_scripts() {
let conf: Config = Config::new("./test_data/package.json", vec![]);
assert_eq!(conf.scripts["build"].as_str(), Some("npm run build"));
assert_eq!(conf.scripts["dev"].as_str(), Some("npm run dev"));
assert_eq!(conf.scripts["start"].as_str(), Some("npm run start"));
}
#[test]
fn test_pkg_managers() {
let conf: Config = Config::new("./test_data/package.json", vec![]);
let pkg_mngr_options = vec!["pnpm-lock.yaml", "package-lock.json", "yarn.lock"];
for (idx, opt) in pkg_mngr_options.iter().enumerate() {
match fs::exists(opt) {
Ok(true) => match idx {
0 => {
assert_eq!(conf.pkg_manager, PackageManager::PNPM);
break;
}
1 => {
assert_eq!(conf.pkg_manager, PackageManager::NPM);
break;
}
2 => {
assert_eq!(conf.pkg_manager, PackageManager::YARN);
break;
}
_ => {
assert_eq!(conf.pkg_manager, PackageManager::NPM);
break;
}
},
_ => {
continue;
}
}
}
}
#[test]
fn test_script_command() {
let args = Vec::from([String::from(""), String::from("dev")]);
let conf: Config = Config::new("./test_data/package.json", args);
let cmd = conf.get_command();
assert_eq!(cmd, String::from("npm run dev"))
}
}