Added init command
General code cleanup Fixed the Automatic task discovery, not discovering rask config tasks.
This commit is contained in:
parent
d8addbdb7f
commit
4fb6c7b497
|
@ -2,3 +2,4 @@ name: nested/folder
|
|||
|
||||
tasks:
|
||||
dev: echo 'Hello from nested/folder?'
|
||||
meme: echo 'Meme from nested/folder?'
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
|
@ -0,0 +1,51 @@
|
|||
use clap::Args;
|
||||
use crate::utils::file::{ConfigFile, parse_path_string, write_config_file};
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct Arguments {
|
||||
#[arg(long, default_value = ".", help = "Which directory to use as entry, defaults to the current directory")]
|
||||
entry: String,
|
||||
#[arg(help = "then name of the config file, defaults to the directory name", default_value = "")]
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub fn execute(arguments: &Arguments) -> Result<(), String> {
|
||||
let Arguments { entry, name } = arguments;
|
||||
|
||||
let mut path = parse_path_string(entry)?;
|
||||
if path.is_dir() {
|
||||
path.push("rask.yaml")
|
||||
}
|
||||
|
||||
if path.exists() {
|
||||
return Err(format!("Rask already initialised at {:?}", path));
|
||||
}
|
||||
|
||||
let mut config_name = name.clone(); // Use clone to avoid modifying the original input
|
||||
if config_name.is_empty() {
|
||||
config_name = path.parent()
|
||||
.unwrap()
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
}
|
||||
|
||||
let config_file: ConfigFile = ConfigFile {
|
||||
name: config_name,
|
||||
task_engine: Default::default(),
|
||||
directories: vec![],
|
||||
tasks: Default::default(),
|
||||
__file_path: Default::default(),
|
||||
__dir_path: Default::default(),
|
||||
};
|
||||
|
||||
write_config_file(path.clone(), config_file)?;
|
||||
|
||||
// NOTE: We could improve the init command by adding a reverse search for a parent rake file
|
||||
|
||||
println!("Rask initialised: {:?}", path);
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -40,8 +40,8 @@ fn get_config_tasks(configs: &Vec<Config>) -> Result<Vec<String>, String> {
|
|||
let mut tasks: Vec<String> = vec![];
|
||||
|
||||
for config in configs {
|
||||
for configTask in &config.tasks {
|
||||
let ConfigTask { key, .. } = configTask;
|
||||
for config_task in &config.tasks {
|
||||
let ConfigTask { key, .. } = config_task;
|
||||
if !tasks.contains(&key) {
|
||||
tasks.push(key.clone());
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod run;
|
||||
pub mod list;
|
||||
pub mod init;
|
|
@ -113,7 +113,7 @@ fn execute_task(task: &Task) -> Result<JoinHandle<bool>, String> {
|
|||
.arg(command)
|
||||
.current_dir(directory)
|
||||
.status()
|
||||
.expect("Failed to execute command");
|
||||
.map_err(|err| err.to_string());
|
||||
status.success()
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::process::exit;
|
|||
use clap::{Parser, Subcommand};
|
||||
use commands::run;
|
||||
use commands::list;
|
||||
use commands::init;
|
||||
|
||||
mod commands;
|
||||
mod utils;
|
||||
|
@ -10,6 +11,7 @@ mod utils;
|
|||
enum Command {
|
||||
Run(run::Arguments),
|
||||
List(list::Arguments),
|
||||
Init(init::Arguments),
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -25,6 +27,7 @@ fn main() {
|
|||
let result = match &arguments.command {
|
||||
Command::Run(arguments) => { run::execute(arguments) },
|
||||
Command::List(arguments) => { list::execute(arguments) },
|
||||
Command::Init(arguments) => { init::execute(arguments) },
|
||||
};
|
||||
|
||||
match result {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::fmt::Debug;
|
||||
use std::fs::canonicalize;
|
||||
use std::collections::HashMap;
|
||||
use globset::{Glob, GlobSetBuilder};
|
||||
use serde::Deserialize;
|
||||
|
@ -172,7 +171,7 @@ fn parse_config_file(config_file: ConfigFile) -> Result<Config, String> {
|
|||
TaskEngine::NPM => parse_package_json_tasks(&dir_path, TaskType::NPM)?,
|
||||
TaskEngine::YARN => parse_package_json_tasks(&dir_path, TaskType::YARN)?,
|
||||
TaskEngine::NONE => parse_config_tasks(config_file_tasks)?,
|
||||
TaskEngine::AUTO => parse_discovered_tasks(&dir_path)?,
|
||||
TaskEngine::AUTO => parse_discovered_tasks(&dir_path, config_file_tasks)?,
|
||||
};
|
||||
|
||||
let config: Config = Config { name, tasks, file_path, dir_path, directories };
|
||||
|
@ -181,12 +180,11 @@ fn parse_config_file(config_file: ConfigFile) -> Result<Config, String> {
|
|||
}
|
||||
|
||||
const PACKAGE_JSON_FILE: &str = "package.json";
|
||||
const NPM_LOCK_FILE: &str = "package.lock";
|
||||
const YARN_LOCK_FILE: &str = "yarn.lock";
|
||||
const COMPOSER_JSON_FILE: &str = "composer.json";
|
||||
|
||||
fn parse_discovered_tasks(dir_path: &PathBuf) -> Result<ConfigTasks, String> {
|
||||
let mut config_tasks: ConfigTasks = vec![];
|
||||
fn parse_discovered_tasks(dir_path: &PathBuf, config_file_tasks: ConfigFileTasks) -> Result<ConfigTasks, String> {
|
||||
let mut config_tasks: ConfigTasks = parse_config_tasks(config_file_tasks)?;
|
||||
|
||||
// Gathering facts
|
||||
let has_composer_json = dir_path.join(COMPOSER_JSON_FILE).exists();
|
||||
|
@ -200,7 +198,7 @@ fn parse_discovered_tasks(dir_path: &PathBuf) -> Result<ConfigTasks, String> {
|
|||
}
|
||||
|
||||
if has_package_json {
|
||||
let mut package_config_tasks: ConfigTasks;
|
||||
let package_config_tasks: ConfigTasks;
|
||||
|
||||
if has_yarn_lock {
|
||||
package_config_tasks = parse_package_json_tasks(dir_path, TaskType::YARN)?;
|
||||
|
@ -331,10 +329,7 @@ fn get_config_glob_pattern(root_path: &Path, glob_pattern: &String) -> PathBuf {
|
|||
}
|
||||
|
||||
pub fn resolve_config_path<P: AsRef<Path> + Debug + Clone + Copy>(path: P) -> Result<PathBuf, String> {
|
||||
let full_path = match canonicalize(path) {
|
||||
Ok(full_path) => full_path,
|
||||
Err(_) => return Err(format!("Target does not exists: {:?}", path.clone()))
|
||||
};
|
||||
let full_path = file::parse_path_string(path)?;
|
||||
|
||||
if full_path.is_dir() {
|
||||
let config_file = find_config_file(full_path)?;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::path::PathBuf;
|
||||
use std::fs::read_to_string;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::{canonicalize, read_to_string, write};
|
||||
use std::collections::HashMap;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub fn read_file_content (path: PathBuf) -> Result<String, String> {
|
||||
match read_to_string(path) {
|
||||
|
@ -10,10 +11,16 @@ pub fn read_file_content (path: PathBuf) -> Result<String, String> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write_file_content(file_path: &PathBuf, content: &str) -> Result<(), String> {
|
||||
write(file_path, content).map_err(|err| format!("Failed to write to file: {}", err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_json_file<T: for<'a> Deserialize<'a>>(file_path: &PathBuf) -> Result<T, String> {
|
||||
let content = read_file_content(file_path.clone())?;
|
||||
|
||||
let file_content: T = serde_json::from_str::<T>(&content).expect(format!("Failed to read the file: \"{:?}\"", file_path).as_str());
|
||||
let file_content: T = serde_json::from_str::<T>(&content).map_err(|err| err.to_string());
|
||||
|
||||
Ok(file_content)
|
||||
}
|
||||
|
@ -21,44 +28,57 @@ pub fn read_json_file<T: for<'a> Deserialize<'a>>(file_path: &PathBuf) -> Result
|
|||
fn read_yaml_file<T: for<'a> Deserialize<'a>>(file_path: &PathBuf) -> Result<T, String> {
|
||||
let content = read_file_content(file_path.clone())?;
|
||||
|
||||
let file_content: T = serde_yaml::from_str::<T>(&content).expect(format!("Failed to read the file: \"{:?}\"", file_path).as_str());
|
||||
let file_content: T = serde_yaml::from_str::<T>(&content).map_err(|err| err.to_string())?;
|
||||
|
||||
Ok(file_content)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
pub fn write_yaml_file<T: Serialize + Debug>(file_path: &PathBuf, data: &T) -> Result<(), String> {
|
||||
let yaml_content = serde_yaml::to_string(data).map_err(|err| err.to_string())?;
|
||||
|
||||
write_file_content(file_path, &yaml_content)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum TaskEngine {
|
||||
#[serde(rename = "composer")]
|
||||
COMPOSER,
|
||||
#[serde(rename = "npm")]
|
||||
NPM,
|
||||
#[serde(rename = "yarn")]
|
||||
YARN,
|
||||
#[serde(rename = "none")]
|
||||
NONE,
|
||||
#[serde(rename = "auto")]
|
||||
#[default]
|
||||
AUTO,
|
||||
}
|
||||
|
||||
pub type ConfigFileTasks = HashMap<String, String>;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct ConfigFile {
|
||||
pub(crate) name: String,
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "is_auto_task_engine")]
|
||||
pub(crate) task_engine: TaskEngine,
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub(crate) directories: Vec<String>,
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "ConfigFileTasks::is_empty")]
|
||||
pub(crate) tasks: ConfigFileTasks,
|
||||
// The following fields are not part of the yaml file.
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "skip_path")]
|
||||
pub(crate) __file_path: PathBuf,
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "skip_path")]
|
||||
pub(crate) __dir_path: PathBuf,
|
||||
}
|
||||
|
||||
fn is_auto_task_engine(value: &TaskEngine) -> bool {
|
||||
match value {
|
||||
TaskEngine::AUTO => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_path(path: &PathBuf) -> bool {
|
||||
path.to_str().map_or(true, |s| s.is_empty())
|
||||
}
|
||||
|
||||
pub fn read_config_file(config_file_path: PathBuf) -> Result<ConfigFile, String> {
|
||||
let mut config_file = read_yaml_file::<ConfigFile>(&config_file_path)?;
|
||||
|
||||
|
@ -68,3 +88,16 @@ pub fn read_config_file(config_file_path: PathBuf) -> Result<ConfigFile, String>
|
|||
Ok(config_file)
|
||||
}
|
||||
|
||||
pub fn write_config_file(config_file_path: PathBuf, config_file: ConfigFile) -> Result<(), String> {
|
||||
write_yaml_file::<ConfigFile>(&config_file_path, &config_file)
|
||||
}
|
||||
|
||||
pub fn parse_path_string<P: AsRef<Path> + Debug + Clone + Copy>(path: P) -> Result<PathBuf, String> {
|
||||
let full_path = match canonicalize(path) {
|
||||
Ok(full_path) => full_path,
|
||||
Err(_) => return Err(format!("Target does not exists: {:?}", path.clone()))
|
||||
};
|
||||
|
||||
Ok(full_path)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue