noctua/core/examples/cli.rs

324 lines
8.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: GPL-3.0-or-later
// examples/cli.rs
//
// CLI test harness for the noctua_core document foundation.
use clap::{ArgGroup, Parser};
use noctua_core::document::command::SessionCommand;
use noctua_core::document::manager::{DocumentManager, DocumentState};
use noctua_core::document::session::CollectionKind;
use std::path::PathBuf;
// ── ANSI helpers ──
const RESET: &str = "\x1b[0m";
const BOLD: &str = "\x1b[1m";
const DIM: &str = "\x1b[2m";
const GREEN: &str = "\x1b[32m";
const YELLOW: &str = "\x1b[33m";
const RED: &str = "\x1b[31m";
const CYAN: &str = "\x1b[36m";
const MAGENTA: &str = "\x1b[35m";
fn heading(text: &str) {
println!("\n{BOLD}{CYAN}── {text} ──{RESET}");
}
fn step(text: &str) {
println!(" {DIM}>{RESET} {text}");
}
fn ok(text: &str) {
println!(" {GREEN}Ok{RESET} {text}");
}
fn err(text: &str) {
eprintln!(" {RED}X{RESET} {text}");
}
fn info(text: &str) {
println!(" {YELLOW}i{RESET} {text}");
}
fn print_session(manager: &DocumentManager, session_name: &str) {
if let Some(session) = manager.session(session_name) {
let kind_label = match session.kind {
CollectionKind::DirectoryBrowser => "DirectoryBrowser",
CollectionKind::DocumentCollection => "DocumentCollection",
};
println!(
" {MAGENTA}Collection: {BOLD}{}{RESET} {DIM}({kind_label}, {} items, index: {:?}){RESET}",
session.name,
session.items.len(),
session.current_index
);
for (i, item) in session.items.iter().enumerate() {
let marker = if session.current_index == Some(i) {
format!("{GREEN}=>{RESET}")
} else {
format!("{DIM} {RESET}")
};
let name = item
.path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("?");
println!(
" {marker} {DIM}[{i}]{RESET} {name} {DIM}(page {}){RESET}",
item.page_index
);
}
} else {
err(&format!("Session '{session_name}' not found"));
}
}
fn exec(manager: &mut DocumentManager, label: &str, command: SessionCommand) {
step(label);
manager.handle(command);
if let DocumentState::Error(e) = &manager.state {
err(&format!("{e}"));
manager.state = DocumentState::Empty;
}
}
fn print_nav_index(manager: &DocumentManager, label: &str) {
if let Some(session) = manager.active_session() {
ok(&format!("{label:<28} → index {:?}", session.current_index));
}
}
#[derive(Parser, Debug)]
#[command(
name = "noctua",
about = "A document viewer for the COSMIC desktop",
group(
ArgGroup::new("source")
.required(true)
.args(["open_document", "open_directory", "open_session"])
)
)]
struct Opt {
/// Open a document (raster, vector, portable)
#[arg(long, value_name = "PATH")]
open_document: Option<PathBuf>,
/// Open a directory
#[arg(long, value_name = "PATH")]
open_directory: Option<PathBuf>,
/// Open a session file (.ron)
#[arg(long, value_name = "PATH")]
open_session: Option<PathBuf>,
}
fn main() {
env_logger::init();
let cli = Opt::parse();
let mut mgr = DocumentManager::new();
// ── Open source ──
heading("Opening source");
if let Some(path) = cli.open_session {
exec(&mut mgr, "Open session file", SessionCommand::Open { path });
} else if let Some(path) = cli.open_document {
let dir = path
.parent()
.map(|p| p.to_path_buf())
.unwrap_or_else(|| PathBuf::from("."));
let session_name = dir
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("Browse")
.to_string();
exec(
&mut mgr,
&format!("New session '{session_name}'"),
SessionCommand::New {
name: session_name.clone(),
kind: CollectionKind::DirectoryBrowser,
},
);
exec(
&mut mgr,
&format!("Select '{session_name}'"),
SessionCommand::Select {
name: session_name.clone(),
},
);
exec(
&mut mgr,
&format!("Scan directory {}", dir.display()),
SessionCommand::AddDirectory { dir },
);
exec(
&mut mgr,
&format!("Add document {}", path.display()),
SessionCommand::AddDocument { path },
);
} else if let Some(path) = cli.open_directory {
let session_name = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("Browse")
.to_string();
exec(
&mut mgr,
&format!("New session '{session_name}'"),
SessionCommand::New {
name: session_name.clone(),
kind: CollectionKind::DirectoryBrowser,
},
);
exec(
&mut mgr,
&format!("Select '{session_name}'"),
SessionCommand::Select {
name: session_name.clone(),
},
);
exec(
&mut mgr,
&format!("Scan directory {}", path.display()),
SessionCommand::AddDirectory { dir: path },
);
}
// ── State summary ──
heading("State after open");
match &mgr.state {
DocumentState::Empty => info("No document loaded"),
DocumentState::Loading => info("Loading…"),
DocumentState::Error(e) => err(&format!("{e}")),
DocumentState::Loaded(_) => ok("Document loaded"),
}
let active_name = match mgr.active_session_name.clone() {
Some(n) => n,
None => {
info("No active session nothing more to test.");
return;
}
};
print_session(&mgr, &active_name);
// ── Navigation ──
heading("Navigation");
exec(&mut mgr, "NextDocument", SessionCommand::NextDocument);
print_nav_index(&mgr, "After NextDocument");
exec(
&mut mgr,
"PreviousDocument",
SessionCommand::PreviousDocument,
);
print_nav_index(&mgr, "After PreviousDocument");
exec(
&mut mgr,
"SelectDocument { index: 0 }",
SessionCommand::SelectDocument { index: 0 },
);
print_nav_index(&mgr, "After SelectDocument(0)");
// ── Collection operations ──
heading("Collection operations");
exec(
&mut mgr,
"New session 'Favorites'",
SessionCommand::New {
name: "Favorites".to_string(),
kind: CollectionKind::DocumentCollection,
},
);
exec(
&mut mgr,
"New session 'Workspace'",
SessionCommand::New {
name: "Workspace".to_string(),
kind: CollectionKind::DocumentCollection,
},
);
// Copy first item from browser → Favorites
heading("Copy item to Favorites");
step("Before:");
print_session(&mgr, "Favorites");
exec(
&mut mgr,
&format!("Copy [0] from '{active_name}' → 'Favorites'"),
SessionCommand::CopyDocument {
source: active_name.clone(),
target: "Favorites".to_string(),
source_index: 0,
target_index: 0,
},
);
step("After:");
print_session(&mgr, "Favorites");
// Remove from DirectoryBrowser (should fail)
heading("Remove from DirectoryBrowser (expect error)");
exec(
&mut mgr,
&format!("Select '{active_name}'"),
SessionCommand::Select {
name: active_name.clone(),
},
);
exec(
&mut mgr,
"RemoveDocument { index: 0 }",
SessionCommand::RemoveDocument { index: 0 },
);
// Move: Favorites → Workspace (copy + delete)
heading("Move: Favorites → Workspace");
step("Before:");
print_session(&mgr, "Favorites");
print_session(&mgr, "Workspace");
exec(
&mut mgr,
"Copy [0] from 'Favorites' → 'Workspace'",
SessionCommand::CopyDocument {
source: "Favorites".to_string(),
target: "Workspace".to_string(),
source_index: 0,
target_index: 0,
},
);
exec(
&mut mgr,
"Select 'Favorites'",
SessionCommand::Select {
name: "Favorites".to_string(),
},
);
exec(
&mut mgr,
"RemoveDocument { index: 0 }",
SessionCommand::RemoveDocument { index: 0 },
);
step("After:");
print_session(&mgr, "Favorites");
print_session(&mgr, "Workspace");
heading("Done");
}