- funktionierende Version
This commit is contained in:
Adam Skotarczak 2025-05-17 00:38:45 +02:00
parent f13829f14d
commit 1210da7601
Signed by: realAscot
GPG Key ID: 4CB9B8D93A96A538
8 changed files with 142 additions and 17 deletions

16
.gitignore vendored
View File

@ -1,7 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
debug/**
target/**
# These are backup files generated by rustfmt
**/*.rs.bk
@ -9,12 +9,8 @@ target/
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Build - Test:
*.txt
*.txt
# Releases (realAscot)
releases/**

32
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,32 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "cargo",
"command": "check",
"problemMatcher": [
"$rustc"
],
"group": "build",
"label": "rust: cargo check"
},
{
"type": "cargo",
"command": "clean",
"problemMatcher": [
"$rustc"
],
"group": "clean",
"label": "rust: cargo clean"
},
{
"type": "cargo",
"command": "build",
"problemMatcher": [
"$rustc"
],
"group": "build",
"label": "rust: cargo build"
}
]
}

View File

@ -10,6 +10,14 @@ use std::path::PathBuf;
about = "Generiert eine ASCII-Baumstruktur eines Verzeichnisses."
)]
pub struct CliArgs {
/// Aktiviere den Debug-Modus
#[clap(short = 'D', long = "debug", global = true, action)]
pub debug: bool,
/// Aktiviere den Silent-Mode für Verwendung in Batch und Skripten
#[clap(short = 'q', long, global = true, action)]
pub quiet: bool,
/// Stammverzeichnis (default: aktuelles Verzeichnis)
#[arg(default_value = ".")]
pub root_path: PathBuf,

3
src/lib.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod config;
pub mod app;
pub mod utils;

View File

@ -6,11 +6,16 @@ use config::args::CliArgs;
use config::loader::load_config_from_home;
use clap::Parser;
use std::fs;
use std::time::Instant;
use treescanner::utils::ascii_spinner::start_spinner;
fn main() {
let args = CliArgs::parse();
let file_config = load_config_from_home().unwrap_or_default();
let start_time = Instant::now();
if !args.root_path.is_dir() {
eprintln!("Fehler: '{}' ist kein gültiges Verzeichnis.", args.root_path.display());
std::process::exit(1);
@ -41,8 +46,21 @@ fn main() {
};
let mut builder = TreeBuilder::new(config);
// Spinner starten, wenn nicht quiet
let spinner = if !args.quiet {
Some(start_spinner(2))
} else {
None
};
let output = builder.build_tree();
if let Some(stop) = spinner {
let _ = stop.send(());
}
println!(); // saubere Zeile nach Spinner
let viewonly = args.viewonly || file_config.viewonly.unwrap_or(false);
let output_path = args.output.clone().or_else(|| file_config.output.map(Into::into)).unwrap_or_else(|| "tree.txt".into());
@ -51,13 +69,16 @@ fn main() {
eprintln!("Fehler beim Schreiben der Datei: {e}");
std::process::exit(1);
}
let (folders, files) = builder.stats();
println!(
"Erfasst wurden {} Ordner und {} Dateien. Ergebnis in {} gespeichert.",
folders,
files,
output_path.display()
);
if !args.quiet {
let (folders, files) = builder.stats();
println!(
"Erfasst wurden {} Ordner und {} Dateien. Ergebnis in {} gespeichert.",
folders,
files,
output_path.display()
);
println!("⏱️ Gesamtlaufzeit: {:.2?}", start_time.elapsed());
}
} else {
println!("{}", output);
}

View File

@ -0,0 +1,35 @@
use std::sync::mpsc::{self, Sender};
use std::thread;
use std::time::Duration;
/// Startet einen minimalistischen ASCII-Spinner in einem Hintergrundthread.
///
/// Gibt alle X Millisekunden einen neuen Frame auf stdout aus (mit `\r`).
/// Die Frequenz wird über `ticks_per_second` gesteuert.
///
/// # Beispiel
/// ```
/// let stop = ascii_spinner::start_spinner(8); // 8 Ticks pro Sekunde
/// // ... lange Operation ...
/// let _ = stop.send(());
/// ```
pub fn start_spinner(ticks_per_second: u64) -> Sender<()> {
let (tx, rx) = mpsc::channel();
let frames = vec!["", "", "", "", "", "", "", "", "", ""];
let clamped = ticks_per_second.clamp(1, 20);
let interval = Duration::from_millis(1000 / clamped);
thread::spawn(move || {
let mut idx = 0;
while rx.try_recv().is_err() {
print!("\r[{}] läuft ...", frames[idx % frames.len()]);
let _ = std::io::Write::flush(&mut std::io::stdout());
idx += 1;
thread::sleep(interval);
}
print!("\r \r"); // Spinner löschen
let _ = std::io::Write::flush(&mut std::io::stdout());
});
tx
}

View File

@ -1 +1,2 @@
pub mod logger;
pub mod ascii_spinner;

29
tests/config_tests.rs Normal file
View File

@ -0,0 +1,29 @@
use treescanner::config::loader::{load_config_from_home, ConfigFile};
use std::fs;
use std::path::PathBuf;
fn write_temp_config(content: &str) -> PathBuf {
let path = dirs::home_dir().unwrap().join(".treescanner.conf");
fs::write(&path, content).expect("Konnte Testdatei nicht schreiben");
path
}
#[test]
fn test_config_wird_korrekt_geladen() {
let config_text = r#"
max_depth = 2
max_files_per_dir = 5
ignore = [".git", "target"]
viewonly = true
"#;
let conf_path = write_temp_config(config_text);
let loaded: ConfigFile = load_config_from_home().expect("Konfig konnte nicht geladen werden");
assert_eq!(loaded.max_depth, Some(2));
assert_eq!(loaded.max_files_per_dir, Some(5));
assert!(loaded.ignore.as_ref().unwrap().contains(".git"));
assert_eq!(loaded.viewonly, Some(true));
fs::remove_file(conf_path).ok();
}