Skip to content

Commit

Permalink
Merge pull request #36 from SARDONYX-sard/feature/implement-cli-subcmd
Browse files Browse the repository at this point in the history
feat(cli): implement cli sub commands
  • Loading branch information
SARDONYX-sard authored Jan 16, 2024
2 parents e4262f4 + 026c438 commit 9e2a0a3
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 92 deletions.
4 changes: 3 additions & 1 deletion dar2oar_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ path = "./src/main.rs"

[dependencies]
anyhow = { version = "1.0.75", features = ["backtrace"] }
clap = { version = "4.4.4", features = ["derive"] } # For CLI
# NOTE: clap uses v3.2.23, the last successfully built version, because color mode was erased in v4
clap = { version = "3.2.23", features = ["derive"] } # For CLI
dar2oar_core = { path = "../dar2oar_core" }
tokio = { version = "1.33.0", features = [
"fs",
Expand All @@ -25,6 +26,7 @@ tokio = { version = "1.33.0", features = [
"macros",
] }
tracing = "0.1.40" # Logger
tracing-appender = "0.2.2"
tracing-subscriber = "0.3.17"

[dev-dependencies]
Expand Down
59 changes: 59 additions & 0 deletions dar2oar_cli/src/cli/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::convert::Convert;
use clap::Args;

#[derive(Debug, clap::Parser)]
#[clap(version, about)]
pub(crate) enum Commands {
/// Convert DAR to OAR
#[clap(arg_required_else_help = true)]
Convert(Convert),

#[clap(arg_required_else_help = true)]
/// Unhide all files in the `DynamicAnimationReplacer` directory
/// by removing the `mohidden` extension
UnhideDar(UnhideDarOption),

#[clap(arg_required_else_help = true)]
/// Find and delete `OpenAnimationReplacer` directory
RemoveOar(RemoveOarOption),
}

#[derive(Debug, Args)]
pub(super) struct UnhideDarOption {
#[clap(value_parser)]
/// DAR directory containing files with ".mohidden" extension
pub dar_dir: String,

// ---logger
#[clap(long)]
/// Log output to stdout as well
pub stdout: bool,
#[clap(long, default_value = "error")]
/// Log level
///
/// trace | debug | info | warn | error
pub log_level: String,
#[clap(long, default_value = "./convert.log")]
/// Output path of log file
pub log_path: String,
}

#[derive(Debug, Args)]
pub(super) struct RemoveOarOption {
#[clap(value_parser)]
/// Path containing the "OpenAnimationReplacer" directory
pub target_path: String,

// ---logger
#[clap(long)]
/// Log output to stdout as well
pub stdout: bool,
#[clap(long, default_value = "error")]
/// Log level
///
/// trace | debug | info | warn | error
pub log_level: String,
#[clap(long, default_value = "./convert.log")]
/// Output path of log file
pub log_path: String,
}
45 changes: 45 additions & 0 deletions dar2oar_cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
mod commands;

use crate::cli::commands::Commands;
use crate::convert::dar2oar;
use crate::init_tracing;
use dar2oar_core::{remove_oar, unhide_dar, Closure};
use std::str::FromStr;
use tracing::Level;

/// Converter CLI version
#[derive(Debug, clap::Parser)]
#[clap(name = "dar2oar", about)]
pub(crate) struct Cli {
#[clap(subcommand)]
command: Commands,
}

macro_rules! init_logger {
($args:ident) => {
init_tracing(
&$args.log_path,
Level::from_str(&$args.log_level).unwrap_or(Level::ERROR),
$args.stdout,
)?;
};
}

pub(crate) async fn run_cli(args: Cli) -> anyhow::Result<()> {
match args.command {
Commands::Convert(args) => {
init_logger!(args);
dar2oar(args).await?;
}
Commands::UnhideDar(args) => {
init_logger!(args);
unhide_dar(args.dar_dir, Closure::default).await?;
}
Commands::RemoveOar(args) => {
init_logger!(args);
remove_oar(args.target_path, Closure::default).await?;
}
}

Ok(())
}
70 changes: 70 additions & 0 deletions dar2oar_cli/src/convert.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use dar2oar_core::{convert_dar_to_oar, get_mapping_table, Closure, ConvertOptions};

pub(crate) async fn dar2oar(args: Convert) -> anyhow::Result<()> {
let config = ConvertOptions {
dar_dir: args.src,
oar_dir: args.dist,
mod_name: args.name,
author: args.author,
section_table: get_mapping_table(args.mapping_file).await,
section_1person_table: get_mapping_table(args.mapping_1person_file).await,
run_parallel: args.run_parallel,
hide_dar: args.hide_dar,
};
match convert_dar_to_oar(config, Closure::default).await {
Ok(()) => Ok(()),
Err(err) => {
tracing::error!("{}", err);
anyhow::bail!("{}", err)
}
}
}

#[derive(Debug, clap::Args)]
pub(crate) struct Convert {
#[clap(value_parser)]
/// DAR source dir path
src: String,
#[clap(long)]
/// OAR destination dir path(If not, it is inferred from DAR path)
dist: Option<String>,
#[clap(long)]
/// Mod name in config.json & directory name(If not, it is inferred from DAR path)
name: Option<String>,
#[clap(long)]
/// Mod author in config.json
author: Option<String>,
#[clap(long)]
/// Path to section name table
///
/// - See more details
/// https://github.com/SARDONYX-sard/dar-to-oar/wiki#what-is-the-mapping-file
mapping_file: Option<String>,
#[clap(long)]
/// Path to section name table(For _1st_person)
mapping_1person_file: Option<String>,
#[clap(long)]
/// Use multi thread
///
/// [Note]
/// More than twice the processing speed can be expected,
/// but the concurrent processing results in thread termination timings being out of order,
/// so log writes will be out of order as well, greatly reducing readability of the logs.
run_parallel: bool,
#[clap(long)]
/// After conversion, add ".mohidden" to all DAR files to hide them(For MO2 user)
hide_dar: bool,

// ---logger
#[clap(long)]
/// Log output to stdout as well
pub stdout: bool,
#[clap(long, default_value = "error")]
/// Log level
///
/// trace | debug | info | warn | error
pub log_level: String,
#[clap(long, default_value = "./convert.log")]
/// Output path of log file
pub log_path: String,
}
37 changes: 37 additions & 0 deletions dar2oar_cli/src/logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::fs::File;
use std::path::Path;
use tracing::level_filters::LevelFilter;

/// Init logger.
pub(crate) fn init_tracing(
log_path: impl AsRef<Path>,
filter: impl Into<LevelFilter>,
with_stdout: bool,
) -> anyhow::Result<()> {
use tracing_subscriber::{fmt, layer::SubscriberExt};
let log_path = log_path.as_ref();
if let Some(log_path) = log_path.parent() {
std::fs::create_dir_all(log_path)?;
};

match with_stdout {
true => tracing::subscriber::set_global_default(
fmt::Subscriber::builder()
.with_max_level(filter)
.finish()
.with(
fmt::Layer::default()
.with_writer(File::create(log_path)?)
.with_line_number(true)
.with_ansi(false),
),
)?,
false => tracing_subscriber::fmt()
.with_ansi(false)
.with_writer(File::create(log_path)?)
.with_max_level(filter)
.init(),
}

Ok(())
}
68 changes: 8 additions & 60 deletions dar2oar_cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,17 @@
use clap::{arg, Parser};
use dar2oar_core::{convert_dar_to_oar, get_mapping_table, Closure, ConvertOptions};
use std::fs::File;
use std::str::FromStr;
use tokio::time::Instant;
use tracing::Level;
mod cli;
mod convert;
mod logger;

/// dar2oar --src "DAR path" --dist "OAR path"
#[derive(Debug, Parser)]
#[command(version, about)]
pub struct Args {
/// DAR source dir path
#[arg(long)]
src: String,
/// OAR destination dir path(If not, it is inferred from src)
#[arg(long)]
dist: Option<String>,
/// mod name in config.json & directory name(If not, it is inferred from src)
#[arg(long)]
name: Option<String>,
/// mod author in config.json
#[arg(long)]
author: Option<String>,
/// path to section name table
#[arg(long)]
mapping_file: Option<String>,
/// path to section name table(For _1st_person)
#[arg(long)]
mapping_1person_file: Option<String>,
/// log_level trace | debug | info | warn | error
#[arg(long, default_value = "error")]
log_level: String,
/// Output path of log file
#[arg(long, default_value = "./convert.log")]
log_path: String,
/// use multi thread(More than twice the processing speed can be expected, but the logs are difficult to read and may fail due to unexpected bugs.)
#[arg(long)]
run_parallel: bool,
#[arg(long)]
/// After converting to OAR, add mohidden to the DAR directory before conversion to treat it as a hidden directory. (for MO2 users)
/// NOTE: It appears to work on the MO2 Tree view, but it is doubtful that it works in the author's actual experience.
hide_dar: bool,
}
use crate::cli::{run_cli, Cli};
use crate::logger::init_tracing;
use clap::Parser;
use tokio::time::Instant;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let start = Instant::now();
let args = Args::parse();
tracing_subscriber::fmt()
.with_ansi(false)
.with_writer(File::create(&args.log_path)?)
.with_max_level(Level::from_str(&args.log_level).unwrap_or(Level::ERROR))
.init();

let config = ConvertOptions {
dar_dir: args.src,
oar_dir: args.dist,
mod_name: args.name,
author: args.author,
section_table: get_mapping_table(args.mapping_file).await,
section_1person_table: get_mapping_table(args.mapping_1person_file).await,
run_parallel: args.run_parallel,
hide_dar: args.hide_dar,
};

match convert_dar_to_oar(config, Closure::default).await {
match run_cli(Cli::parse()).await {
Ok(()) => {
let elapsed = start.elapsed();
tracing::info!(
Expand Down
10 changes: 3 additions & 7 deletions dar2oar_core/src/fs/converter/support_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use tokio::fs;

/// # Returns
/// Report which dirs have been shown
///
/// # NOTE
/// It is currently used only in GUI, but is implemented in Core as an API.
/// A parallel search will find the `DynamicAnimationReplacer` directory in the path passed as the argument
/// and remove only the `mohidden` extension names from the files in that directory.
pub async fn unhide_dar(
dar_dir: impl AsRef<Path>,
mut progress_fn: impl FnMut(usize),
Expand Down Expand Up @@ -58,8 +55,7 @@ pub async fn unhide_dar(
}
}

/// # NOTE
/// It is currently used only in GUI, but is implemented in Core as an API.
/// A parallel search will find and remove the `OpenAnimationReplacer` directory from the path passed as the argument.
pub async fn remove_oar(
search_dir: impl AsRef<Path>,
mut progress_fn: impl FnMut(usize),
Expand Down
Loading

0 comments on commit 9e2a0a3

Please sign in to comment.