use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
mod cli_impl;
mod decrypt;
mod generate;
mod query;
mod validate;
pub use cli_impl::run_cli;
#[derive(Parser, Debug)]
#[command(name = "inklog")]
#[command(author = "Kirky.X")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(about = "inklog - Enterprise-grade Rust logging infrastructure CLI", long_about = None)]
struct Cli {
#[arg(long, global = true)]
#[arg(help = "Emit machine-readable JSON output")]
json: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
#[command(name = "decrypt")]
#[command(about = "Decrypt encrypted log files")]
Decrypt {
#[arg(short, long)]
#[arg(help = "Input encrypted file or directory")]
input: PathBuf,
#[arg(short, long)]
#[arg(help = "Output file or directory")]
output: Option<PathBuf>,
#[arg(short, long, env = "INKLOG_DECRYPT_KEY")]
#[arg(help = "Environment variable name containing the decryption key")]
#[arg(value_parser = clap::builder::NonEmptyStringValueParser::new())]
key_env: String,
#[arg(long)]
#[arg(help = "Recursive decrypt directories")]
recursive: bool,
#[arg(long)]
#[arg(help = "Enable batch mode for multiple files")]
batch: bool,
},
#[command(name = "generate")]
#[command(about = "Generate inklog configuration files")]
Generate {
#[arg(short, long)]
#[arg(help = "Output directory or file path")]
output: Option<PathBuf>,
#[arg(short, long)]
#[arg(help = "Config type: minimal, full, database, file")]
#[arg(default_value = "full")]
config_type: ConfigType,
#[arg(long)]
#[arg(help = "Generate environment variable example file")]
env_example: bool,
},
#[command(name = "validate")]
#[command(about = "Validate inklog configuration files (default: inklog_config.toml)")]
Validate {
#[arg(short, long)]
#[arg(help = "Path to configuration file")]
config: Option<PathBuf>,
#[arg(long)]
#[arg(help = "Check system prerequisites instead of config file")]
prerequisites: bool,
},
#[command(name = "query")]
#[command(
about = "Search local log files by time range/level/keyword (unpacks encrypted/compressed archives)"
)]
Query {
#[arg(long = "path")]
#[arg(help = "Log file or directory to search (recursive; repeatable)")]
path: Vec<PathBuf>,
#[arg(long)]
#[arg(help = "Only records at/after this time (RFC3339, e.g. 2026-09-11T00:00:00Z)")]
since: Option<String>,
#[arg(long)]
#[arg(help = "Only records at/before this time (RFC3339)")]
until: Option<String>,
#[arg(long)]
#[arg(help = "Minimum level: trace/debug/info/warn/error/fatal")]
level: Option<String>,
#[arg(long)]
#[arg(help = "Substring filter applied to message and target")]
grep: Option<String>,
#[arg(long, default_value = "1000")]
#[arg(help = "Maximum number of records to return (0 = unlimited)")]
limit: usize,
#[arg(long, env = "INKLOG_DECRYPT_KEY")]
#[arg(help = "Environment variable name holding the decryption key for .enc files")]
#[arg(value_parser = clap::builder::NonEmptyStringValueParser::new())]
key_env: Option<String>,
},
}
#[derive(Debug, Clone, ValueEnum)]
pub enum ConfigType {
Minimal,
Full,
Database,
File,
}
impl std::fmt::Display for ConfigType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ConfigType::Minimal => write!(f, "minimal"),
ConfigType::Full => write!(f, "full"),
ConfigType::Database => write!(f, "database"),
ConfigType::File => write!(f, "file"),
}
}
}
fn main() {
let code = run_cli();
if code != 0 {
std::process::exit(code);
}
}
#[cfg(test)]
#[ctor::ctor(unsafe)]
fn init_test_locale() {
unsafe {
std::env::set_var("INKLOG_LOCALE", "en");
}
}