feat: cli base
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
This commit is contained in:
1130
Cargo.lock
generated
Normal file
1130
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
Normal file
21
Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "fujicli"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
description = "A CLI to manage Fujifilm devices, simulations, backups, and rendering"
|
||||
authors = [
|
||||
"Nikolaos Karaolidis <nick@karaolidis.com>",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
panic = 'abort'
|
||||
strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.48", features = ["derive", "wrap_help"] }
|
||||
libptp = "0.6.5"
|
||||
log = "0.4.28"
|
||||
log4rs = "1.4.0"
|
||||
rusb = "0.9.4"
|
19
src/cli/backup.rs
Normal file
19
src/cli/backup.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use super::common::file::{Input, Output};
|
||||
use clap::Subcommand;
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum BackupCmd {
|
||||
/// Export backup
|
||||
#[command(alias = "e")]
|
||||
Export {
|
||||
/// Output file (use '-' to write to stdout)
|
||||
output_file: Output,
|
||||
},
|
||||
|
||||
/// Import backup
|
||||
#[command(alias = "i")]
|
||||
Import {
|
||||
/// Input file (use '-' to read from stdin)
|
||||
input_file: Input,
|
||||
},
|
||||
}
|
35
src/cli/common/file.rs
Normal file
35
src/cli/common/file.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use std::{error::Error, path::PathBuf, str::FromStr};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Input {
|
||||
Path(PathBuf),
|
||||
Stdin,
|
||||
}
|
||||
|
||||
impl FromStr for Input {
|
||||
type Err = Box<dyn Error + Send + Sync>;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s == "-" {
|
||||
Ok(Input::Stdin)
|
||||
} else {
|
||||
Ok(Input::Path(PathBuf::from(s)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Output {
|
||||
Path(PathBuf),
|
||||
Stdout,
|
||||
}
|
||||
|
||||
impl FromStr for Output {
|
||||
type Err = Box<dyn Error + Send + Sync>;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s == "-" {
|
||||
Ok(Output::Stdout)
|
||||
} else {
|
||||
Ok(Output::Path(PathBuf::from(s)))
|
||||
}
|
||||
}
|
||||
}
|
28
src/cli/common/film.rs
Normal file
28
src/cli/common/film.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use std::error::Error;
|
||||
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SimulationSelector {
|
||||
Slot(u8),
|
||||
Name(String),
|
||||
}
|
||||
|
||||
impl std::str::FromStr for SimulationSelector {
|
||||
type Err = Box<dyn Error + Send + Sync>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Ok(slot) = s.parse::<u8>() {
|
||||
return Ok(SimulationSelector::Slot(slot));
|
||||
}
|
||||
|
||||
if s.is_empty() {
|
||||
Err("Simulation name cannot be empty".into())
|
||||
} else {
|
||||
Ok(SimulationSelector::Name(s.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct FilmSimulationOptions {}
|
2
src/cli/common/mod.rs
Normal file
2
src/cli/common/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod file;
|
||||
pub mod film;
|
12
src/cli/device.rs
Normal file
12
src/cli/device.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use clap::Subcommand;
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum DeviceCmd {
|
||||
/// List devices
|
||||
#[command(alias = "l")]
|
||||
List,
|
||||
|
||||
/// Dump device info
|
||||
#[command(alias = "i")]
|
||||
Info,
|
||||
}
|
55
src/cli/mod.rs
Normal file
55
src/cli/mod.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
mod backup;
|
||||
mod common;
|
||||
mod device;
|
||||
mod render;
|
||||
mod simulation;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
use backup::BackupCmd;
|
||||
use device::DeviceCmd;
|
||||
use render::RenderCmd;
|
||||
use simulation::SimulationCmd;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None, author)]
|
||||
pub struct Cli {
|
||||
/// Subcommands
|
||||
#[command(subcommand)]
|
||||
pub command: Commands,
|
||||
|
||||
/// Format output using json
|
||||
#[arg(long, short = 'j', global = true)]
|
||||
pub json: bool,
|
||||
|
||||
/// Only log warnings and errors
|
||||
#[arg(long, short = 'q', global = true, conflicts_with = "verbose")]
|
||||
pub quiet: bool,
|
||||
|
||||
/// Log extra debugging information
|
||||
#[arg(long, short = 'v', global = true, conflicts_with = "quiet")]
|
||||
pub verbose: bool,
|
||||
|
||||
/// Manually specify target device
|
||||
#[arg(long, short = 'd', global = true)]
|
||||
pub device: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Commands {
|
||||
/// Manage devices
|
||||
#[command(alias = "d", subcommand)]
|
||||
Device(DeviceCmd),
|
||||
|
||||
/// Manage film simulations
|
||||
#[command(alias = "s", subcommand)]
|
||||
Simulation(SimulationCmd),
|
||||
|
||||
/// Manage backups
|
||||
#[command(alias = "b", subcommand)]
|
||||
Backup(BackupCmd),
|
||||
|
||||
/// Render images
|
||||
#[command(alias = "r")]
|
||||
Render(RenderCmd),
|
||||
}
|
27
src/cli/render.rs
Normal file
27
src/cli/render.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::common::{
|
||||
file::{Input, Output},
|
||||
film::{FilmSimulationOptions, SimulationSelector},
|
||||
};
|
||||
use clap::Args;
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct RenderCmd {
|
||||
/// Simulation number or name
|
||||
#[arg(long, conflicts_with = "simulation_file")]
|
||||
simulation: Option<SimulationSelector>,
|
||||
|
||||
/// Path to exported simulation
|
||||
#[arg(long, conflicts_with = "simulation")]
|
||||
simulation_file: Option<PathBuf>,
|
||||
|
||||
#[command(flatten)]
|
||||
film_simulation_options: FilmSimulationOptions,
|
||||
|
||||
/// RAF input file (use '-' to read from stdin)
|
||||
input: Input,
|
||||
|
||||
/// Output file (use '-' to write to stdout)
|
||||
output: Output,
|
||||
}
|
49
src/cli/simulation.rs
Normal file
49
src/cli/simulation.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use super::common::{
|
||||
file::{Input, Output},
|
||||
film::{FilmSimulationOptions, SimulationSelector},
|
||||
};
|
||||
use clap::Subcommand;
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum SimulationCmd {
|
||||
/// List simulations
|
||||
#[command(alias = "l")]
|
||||
List,
|
||||
|
||||
/// Get simulation
|
||||
#[command(alias = "g")]
|
||||
Get {
|
||||
/// Simulation number or name
|
||||
simulation: SimulationSelector,
|
||||
},
|
||||
|
||||
/// Set simulation parameters
|
||||
#[command(alias = "s")]
|
||||
Set {
|
||||
/// Simulation number or name
|
||||
simulation: SimulationSelector,
|
||||
|
||||
#[command(flatten)]
|
||||
film_simulation_options: FilmSimulationOptions,
|
||||
},
|
||||
|
||||
/// Export simulation
|
||||
#[command(alias = "e")]
|
||||
Export {
|
||||
/// Simulation number or name
|
||||
simulation: SimulationSelector,
|
||||
|
||||
/// Output file (use '-' to write to stdout)
|
||||
output_file: Output,
|
||||
},
|
||||
|
||||
/// Import simulation
|
||||
#[command(alias = "i")]
|
||||
Import {
|
||||
/// Simulation number
|
||||
slot: u8,
|
||||
|
||||
/// Input file (use '-' to read from stdin)
|
||||
input_file: Input,
|
||||
},
|
||||
}
|
32
src/log.rs
Normal file
32
src/log.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use log::LevelFilter;
|
||||
use log4rs::{
|
||||
Config,
|
||||
append::console::{ConsoleAppender, Target},
|
||||
config::{Appender, Root},
|
||||
encode::pattern::PatternEncoder,
|
||||
};
|
||||
|
||||
pub fn init(quiet: bool, verbose: bool) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let level = if quiet {
|
||||
LevelFilter::Warn
|
||||
} else if verbose {
|
||||
LevelFilter::Debug
|
||||
} else {
|
||||
LevelFilter::Info
|
||||
};
|
||||
|
||||
let encoder = Box::new(PatternEncoder::new("{d} {h({l})} {M}::{L} - {m}{n}"));
|
||||
|
||||
let console = ConsoleAppender::builder()
|
||||
.encoder(encoder)
|
||||
.target(Target::Stderr)
|
||||
.build();
|
||||
|
||||
let config = Config::builder()
|
||||
.appender(Appender::builder().build("stderr", Box::new(console)))
|
||||
.build(Root::builder().appender("stderr").build(level))?;
|
||||
|
||||
log4rs::init_config(config)?;
|
||||
|
||||
Ok(())
|
||||
}
|
15
src/main.rs
Normal file
15
src/main.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use clap::Parser;
|
||||
mod cli;
|
||||
mod log;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cli = cli::Cli::parse();
|
||||
|
||||
log::init(cli.quiet, cli.verbose)?;
|
||||
|
||||
match cli.command {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user