use anyhow::Result;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
use doxx::ExportFormat;
mod document;
mod export;
pub mod image_extractor;
pub mod terminal_image;
mod ui;
#[derive(Parser)]
#[command(
name = "doxx",
version,
about = "Terminal document viewer for .docx files",
long_about = "Beautiful .docx viewing in your terminal"
)]
struct Cli {
#[arg(value_name = "FILE")]
file: Option<PathBuf>,
#[arg(short, long)]
outline: bool,
#[arg(short, long)]
page: Option<usize>,
#[arg(short, long)]
search: Option<String>,
#[arg(long, value_enum)]
export: Option<ExportFormat>,
#[arg(long)]
force_ui: bool,
#[arg(long)]
color: bool,
#[arg(long)]
images: bool,
#[arg(long)]
no_images: bool,
#[arg(long)]
extract_images: Option<PathBuf>,
#[arg(long, value_name = "COLS")]
image_width: Option<u32>,
#[arg(long, value_name = "ROWS")]
image_height: Option<u32>,
#[arg(long, value_name = "SCALE")]
image_scale: Option<f32>,
#[arg(long)]
debug_terminal: bool,
#[command(subcommand)]
config: Option<ConfigCommands>,
}
#[derive(Subcommand)]
enum ConfigCommands {
Set { key: String, value: String },
Get { key: String },
Init,
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
if cli.debug_terminal {
use terminal_image::TerminalImageRenderer;
let renderer = TerminalImageRenderer::new();
renderer.print_capabilities();
return Ok(());
}
match &cli.config {
Some(ConfigCommands::Init) => {
println!("Initializing doxx configuration...");
return Ok(());
}
Some(ConfigCommands::Set { key, value }) => {
println!("Setting {key} = {value}");
return Ok(());
}
Some(ConfigCommands::Get { key }) => {
println!("Getting {key}");
return Ok(());
}
None => {}
}
let file_path = cli
.file
.clone()
.ok_or_else(|| anyhow::anyhow!("Please provide a document file to view"))?;
if !file_path.exists() {
anyhow::bail!("File not found: {}", file_path.display());
}
let image_options = document::ImageOptions {
enabled: cli.images,
max_width: cli.image_width,
max_height: cli.image_height,
scale: cli.image_scale,
};
let document = document::load_document(&file_path, image_options).await?;
if let Some(extract_dir) = &cli.extract_images {
use image_extractor::ImageExtractor;
let mut extractor = ImageExtractor::new()?;
extractor.extract_images_from_docx(&file_path)?;
std::fs::create_dir_all(extract_dir)?;
for (rel_id, temp_path) in extractor.list_images() {
let target_path = extract_dir.join(rel_id);
std::fs::copy(temp_path, &target_path)?;
println!("Extracted: {}", target_path.display());
}
println!(
"Successfully extracted {} images to {}",
extractor.list_images().len(),
extract_dir.display()
);
return Ok(());
}
if let Some(export_format) = &cli.export {
export::export_document(&document, export_format)?;
return Ok(());
}
ui::run_viewer(document, &cli).await?;
Ok(())
}