Compare commits
6 Commits
e13e60193c
...
f22845d7c8
Author | SHA1 | Date | |
---|---|---|---|
f22845d7c8 | |||
04252c355b | |||
0652a34486 | |||
d683594d6a | |||
1210da7601 | |||
f13829f14d |
22
.gitignore
vendored
22
.gitignore
vendored
@ -1,7 +1,6 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
debug/**
|
||||
target/**
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
@ -9,9 +8,14 @@ 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
|
||||
|
||||
# Develop
|
||||
TODO.md
|
||||
desktop.ini
|
||||
.vscode/**
|
||||
|
||||
# Releases (realAscot)
|
||||
releases/**
|
||||
bin/**
|
||||
|
32
.vscode/tasks.json
vendored
Normal file
32
.vscode/tasks.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
27
CHANGELOG.md
Normal file
27
CHANGELOG.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Changelog treeScanner (Rust)
|
||||
|
||||
- **2025-05-17 - v1.0.0**
|
||||
- **Geändert:**
|
||||
- [x] Komplette Neuumsetzung des TreeScanner-Tools in Rust
|
||||
- [x] Aufbau einer modularen Projektstruktur (`treebuilder.rs`, `args.rs`, `loader.rs`, `ascii_spinner.rs`, `logger.rs`)
|
||||
- [x] CLI-Parsing mit `clap` umgesetzt, inkl. Aliase und Hilfetexte
|
||||
- [x] Fortschrittsanzeige mit ASCII-Spinner als wiederverwendbares Modul integriert
|
||||
- [x] Unterstützung für `.treescanner.conf` im Home-Verzeichnis eingebaut (inkl. Auto-Erzeugung und TOML-Validierung)
|
||||
- [x] Optionale Parameter wie `--max-depth`, `--viewonly`, `--ignore`, `--align-comments` implementiert
|
||||
- [x] Fallback-Logik: CLI-Eingaben überschreiben Konfiguration bei Kollision
|
||||
- [x] Ausgabe wahlweise in Konsole oder Datei (Standard: `tree.txt`)
|
||||
- [x] Unit-Tests für Konfiguration und Kommentar-Ausrichtung hinzugefügt (`tests/config_tests.rs`)
|
||||
- [x] Fehlerausgaben für ungültige Konfiguration oder fehlende Verzeichnisse eingebaut
|
||||
- [x] Makefile mit Windows-Kompatibilität für Build + Copy-Ziel erstellt
|
||||
- [x] Icon und Metadaten in .exe eingebettet via `build.rs` und `rc.exe`
|
||||
|
||||
- **Hinzugefügt:**
|
||||
- [x] CLI-Argument `--ignore` unterstützt sowohl Kommaseparierung (CLI) als auch Listen (TOML)
|
||||
- [x] Debug- und Silent-Modus zur Steuerung der Ausgaben
|
||||
- [x] Unterstützung für Konfigurationsparameter `output`, `viewonly`, `language`, `align_comments`
|
||||
- [x] Automatische Erstellung einer gültigen Standardkonfiguration, wenn keine `.treescanner.conf` vorhanden ist
|
||||
- [x] Fehler-Feedback mit TOML-Zeilenangaben bei ungültiger Konfiguration
|
||||
|
||||
- **Entfernt:**
|
||||
- [x] Python-Version vollständig ersetzt
|
||||
- [x] Temporäre Hilfsfunktionen aus der Portierungsphase entfernt
|
399
Cargo.lock
generated
399
Cargo.lock
generated
@ -2,6 +2,62 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.22"
|
||||
@ -17,6 +73,73 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embed-resource"
|
||||
version = "2.5.1"
|
||||
@ -37,12 +160,57 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.9.0"
|
||||
@ -53,18 +221,52 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
@ -83,6 +285,23 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
@ -92,6 +311,19 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
@ -133,6 +365,12 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
@ -144,6 +382,39 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.22"
|
||||
@ -187,9 +458,14 @@ checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
||||
|
||||
[[package]]
|
||||
name = "treescanner"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dirs",
|
||||
"embed-resource",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -198,6 +474,12 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "vswhom"
|
||||
version = "0.1.0"
|
||||
@ -218,13 +500,37 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -233,13 +539,29 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -248,42 +570,90 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.10"
|
||||
@ -300,5 +670,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
14
Cargo.toml
14
Cargo.toml
@ -1,7 +1,19 @@
|
||||
[package]
|
||||
name = "treescanner"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
edition = "2024"
|
||||
|
||||
[package.metadata.winres]
|
||||
subsystem = "console"
|
||||
|
||||
[build-dependencies]
|
||||
embed-resource = "2.4"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.8"
|
||||
dirs = "5"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
26
LICENSE
Normal file
26
LICENSE
Normal file
@ -0,0 +1,26 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Adam Skotarczak <adam@skotarczak.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Software”), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
1. The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
2. Attribution Requirement:
|
||||
If the Software is forked, reused, or substantially modified and redistributed,
|
||||
visible attribution must be provided in the documentation (e.g., README file), stating:
|
||||
“This software is based on work by Adam Skotarczak (https://github.com/realAscot)”.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
43
Makefile
Normal file
43
Makefile
Normal file
@ -0,0 +1,43 @@
|
||||
# =======================================
|
||||
# 🦀 Windows-kompatibles Makefile für TreeScanner
|
||||
# =======================================
|
||||
|
||||
PROJECT_NAME := treescanner
|
||||
BUILD_DIR := target/release
|
||||
OUT_DIR := bin
|
||||
|
||||
# Standardziel
|
||||
.PHONY: all
|
||||
all: build copy
|
||||
|
||||
# Release-Build
|
||||
.PHONY: build
|
||||
build:
|
||||
cargo build --release
|
||||
|
||||
# Kopiere EXE in bin\
|
||||
.PHONY: copy
|
||||
copy:
|
||||
if not exist "$(OUT_DIR)" mkdir "$(OUT_DIR)"
|
||||
copy /Y "$(BUILD_DIR)\$(PROJECT_NAME).exe" "$(OUT_DIR)\$(PROJECT_NAME).exe"
|
||||
|
||||
# Lösche alles außer bin\
|
||||
.PHONY: clean
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
# Tests
|
||||
.PHONY: test
|
||||
test:
|
||||
cargo test
|
||||
|
||||
# Lokale Installation
|
||||
.PHONY: install
|
||||
install:
|
||||
cargo install --path . --root install-local
|
||||
|
||||
# Vollständiger Reset
|
||||
.PHONY: reset
|
||||
reset: clean
|
||||
if exist "$(OUT_DIR)" rmdir /S /Q "$(OUT_DIR)"
|
||||
if exist "install-local" rmdir /S /Q "install-local"
|
183
README.md
183
README.md
@ -2,33 +2,208 @@
|
||||
|
||||

|
||||
|
||||
Dieses Tool ist im Rahmen meines persönlichen Projekts von C nach Rust zu wechseln.
|
||||
**TreeScanner** ist ein leichtgewichtiges, portables CLI-Tool zur rekursiven Analyse von Verzeichnisstrukturen.
|
||||
Es erzeugt eine klar strukturierte ASCII-Ausgabe und eignet sich hervorragend für Dokumentation, Debugging oder Buildsysteme.
|
||||
|
||||
> 🔧 Diese Version ist eine komplette Neuentwicklung in **Rust** und ersetzt das ursprüngliche Python-Projekt:
|
||||
> ➜ [treeScannerASCII (Python)](https://github.com/realAscot/treeScannerASCII)
|
||||
|
||||
Der original treeScanner in Python ist unter <https://github.com/realAscot/treeScannerASCII> zu finden. Dieser ist auch als Python-Modul zu verwenden.
|
||||
|
||||
---
|
||||
|
||||
## Inhalt
|
||||
|
||||
- [TreeScanner CLI Verzeichnisscanner](#treescanner-cli-verzeichnisscanner)
|
||||
- [Inhalt](#inhalt)
|
||||
- [Beschreibung](#beschreibung)
|
||||
- [Installation](#installation)
|
||||
- [Über .zip Archiv](#über-zip-archiv)
|
||||
- [Installer](#installer)
|
||||
- [Struktur](#struktur)
|
||||
- [Features](#features)
|
||||
- [✨ Features](#-features)
|
||||
- [▶️ Verwendung](#️-verwendung)
|
||||
- [🖼 Beispielausgabe](#-beispielausgabe)
|
||||
- [⚙️ Konfiguration `.treescanner.conf`](#️-konfiguration-treescannerconf)
|
||||
- [🔍 Ort](#-ort)
|
||||
- [📘 Format](#-format)
|
||||
- [📝 Format (.toml)](#-format-toml)
|
||||
- [Lizenz](#lizenz)
|
||||
- [Eingesetzte Libraries (MIT-kompatibel):](#eingesetzte-libraries-mit-kompatibel)
|
||||
- [💬 Kontakt](#-kontakt)
|
||||
|
||||
---
|
||||
|
||||
## Beschreibung
|
||||
|
||||
Der treeScanner.exe ist ursprünglich als ein Tool entwickelt worden mit dem man Verzeichnisstrukturen für Dokumentationen erzeugen konnte.
|
||||
TreeScanner durchsucht Verzeichnisse rekursiv, filtert optional bestimmte Ordner aus und gibt eine **strukturierte ASCII-Baumdarstellung** mit Icons und optional ausgerichteten Kommentaren aus.
|
||||
Er eignet sich für technische Dokumentationen, Versionskontrollen, Release-Skripte und CI/CD-Prozesse.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Über .zip Archiv
|
||||
|
||||
### Installer
|
||||
|
||||
Der Installer `treeScanner-Setup.exe` der jeweils die aktuelle Version auf GitHub enthällt, bietet Dir an das Programm im Windows-Verzeichnis zu installieren.
|
||||
Dies ist zwar unüblich aber
|
||||
|
||||
---
|
||||
|
||||
## Struktur
|
||||
|
||||
**Diese Struktur ist eine Ausgabe des Tools:**
|
||||
**GEPLANTE STRUKTUR (DEV)**
|
||||
|
||||
```plaintext
|
||||
|
||||
📁 treeScanner #
|
||||
├── 📁 .cargo #
|
||||
│ └── 📄 config.toml #
|
||||
├── 📁 .vscode #
|
||||
│ └── 📄 tasks.json #
|
||||
├── 📁 media #
|
||||
│ ├── 📄 logo-treescanner.png #
|
||||
│ └── 📄 logo-treescanner_512x512.png #
|
||||
├── 📁 resources #
|
||||
│ ├── 📄 icon.ico #
|
||||
│ └── 📄 version.rc #
|
||||
├── 📁 src #
|
||||
│ ├── 📁 app #
|
||||
│ │ ├── 📄 mod.rs #
|
||||
│ │ └── 📄 treebuilder.rs #
|
||||
│ ├── 📁 config #
|
||||
│ │ ├── 📄 args.rs #
|
||||
│ │ ├── 📄 loader.rs #
|
||||
│ │ └── 📄 mod.rs #
|
||||
│ ├── 📁 utils #
|
||||
│ │ ├── 📄 ascii_spinner.rs #
|
||||
│ │ ├── 📄 logger.rs #
|
||||
│ │ └── 📄 mod.rs #
|
||||
│ ├── 📄 lib.rs #
|
||||
│ └── 📄 main.rs #
|
||||
├── 📁 tests #
|
||||
│ ├── 📄 config_tests.rs #
|
||||
│ └── 📄 treebuilder_tests.rs #
|
||||
├── 📄 .gitignore #
|
||||
├── 📄 build.rs #
|
||||
├── 📄 Cargo.lock #
|
||||
├── 📄 Cargo.toml #
|
||||
├── 📄 CHANGELOG.md #
|
||||
├── 📄 LICENSE #
|
||||
├── 📄 Makefile #
|
||||
├── 📄 README.md #
|
||||
└── 📄 VERSION #
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
## ✨ Features
|
||||
|
||||
- 📁 ASCII-Baumdarstellung mit Icons (📁, 📄)
|
||||
- 🚫 Ignorierliste per CLI oder Konfig-Datei
|
||||
- ⏫ Limit für Tiefe (`--max-depth`) und Dateianzahl pro Verzeichnis
|
||||
- 📄 Ausgabe in Datei oder Konsole
|
||||
- ⚙ Konfigurierbar via `~/.treescanner.conf`
|
||||
- 🌀 Fortschrittsanzeige beim Scannen (Spinner)
|
||||
- 💬 Optionale Kommentarspalte (`--align-comments`)
|
||||
- 🧪 Getestete Komponenten (unit-tested)
|
||||
- 🔕 Silent-Modus (`--quiet`)
|
||||
- 🛠 Portable Binary (`.exe`) ohne externe Abhängigkeiten
|
||||
|
||||
---
|
||||
|
||||
## ▶️ Verwendung
|
||||
|
||||
```bash
|
||||
# Einfacher Scan (aktuelles Verzeichnis)
|
||||
./treescanner.exe
|
||||
|
||||
# Mit Tiefe 3, ohne speichern
|
||||
./treescanner.exe --max-depth 3 --viewonly
|
||||
|
||||
# Mit Kommentar-Ausrichtung
|
||||
./treescanner.exe --align-comments
|
||||
|
||||
# Ergebnis in Datei mit anderem Pfad speichern
|
||||
./treescanner.exe --output ./struktur/tree.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🖼 Beispielausgabe
|
||||
|
||||
```plaintext
|
||||
|
||||
📁 ./src/
|
||||
├── 📄 main.rs #
|
||||
├── 📁 app/ #
|
||||
│ └── 📄 treebuilder.rs #
|
||||
└── 📁 utils/ #
|
||||
├── 📄 ascii_spinner.rs #
|
||||
└── 📄 logger.rs #
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Konfiguration `.treescanner.conf`
|
||||
|
||||
### 🔍 Ort
|
||||
|
||||
Standardmäßig gesucht im **Benutzerverzeichnis**:
|
||||
|
||||
```plaintext
|
||||
Windows: C:\Users\<Benutzername>\.treescanner.conf
|
||||
Linux: /home/<user>/.treescanner.conf
|
||||
```
|
||||
|
||||
### 📘 Format
|
||||
|
||||
### 📝 Format (.toml)
|
||||
|
||||
```toml
|
||||
max_depth = 3
|
||||
max_files_per_dir = 100
|
||||
ignore = [".git", "target", ".vscode"]
|
||||
output = "tree.txt"
|
||||
viewonly = false
|
||||
align_comments = false
|
||||
```
|
||||
|
||||
- CLI-Einstellungen überschreiben Konfigurationswerte bei Kollision
|
||||
- Die Datei wird beim ersten Start automatisch erzeugt, falls sie fehlt
|
||||
- Der Pfad ist **nicht fest kodiert**, sondern dynamisch via `dirs::home_dir()` ermittelt
|
||||
|
||||
---
|
||||
|
||||
## Lizenz
|
||||
|
||||
Dieses Projekt steht unter der [MIT-Lizenz](./LICENSE).
|
||||
|
||||
### Eingesetzte Libraries (MIT-kompatibel):
|
||||
|
||||
| Crate | Lizenz |
|
||||
|-----------------|------------|
|
||||
| `clap` | MIT/Apache |
|
||||
| `dirs` | MIT/Apache |
|
||||
| `serde` | MIT/Apache |
|
||||
| `serde_derive` | MIT/Apache |
|
||||
| `toml` | MIT/Apache |
|
||||
| `tempfile` | MIT/Apache (nur für Tests) |
|
||||
| `console` | MIT |
|
||||
|
||||
⚠️ Alle eingebundenen Libraries sind **MIT- oder Apache-2.0-kompatibel** und dürfen ohne Einschränkungen in proprietären oder Open-Source-Projekten verwendet werden.
|
||||
|
||||
siehe [LICENSE](./LICENSE)
|
||||
|
||||
---
|
||||
|
||||
## 💬 Kontakt
|
||||
|
||||
**Adam Skotarczak**
|
||||
✉ [adam@skotarczak.net](mailto:adam@skotarczak.net)
|
||||
🔗 [realAscot auf GitHub](https://github.com/realAscot)
|
||||
|
6
desktop.ini
Normal file
6
desktop.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[.ShellClassInfo]
|
||||
IconResource=.\resources\icon.ico,0
|
||||
[ViewState]
|
||||
Mode=
|
||||
Vid=
|
||||
FolderType=Documents
|
@ -1,3 +1 @@
|
||||
pub fn run() {
|
||||
|
||||
}
|
||||
pub mod treebuilder;
|
||||
|
112
src/app/treebuilder.rs
Normal file
112
src/app/treebuilder.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Struktur zur Konfiguration des Verzeichnis-Scans.
|
||||
pub struct TreeBuilderConfig {
|
||||
pub root_path: PathBuf,
|
||||
pub max_depth: Option<usize>,
|
||||
pub max_files_per_dir: usize,
|
||||
pub ignored_dirs: Vec<String>,
|
||||
pub folder_icon: String,
|
||||
pub file_icon: String,
|
||||
pub align_comments: bool,
|
||||
}
|
||||
|
||||
/// Verantwortlich für das Erzeugen der ASCII-Baumstruktur.
|
||||
pub struct TreeBuilder {
|
||||
pub config: TreeBuilderConfig,
|
||||
folder_count: usize,
|
||||
file_count: usize,
|
||||
}
|
||||
|
||||
impl TreeBuilder {
|
||||
pub fn new(config: TreeBuilderConfig) -> Self {
|
||||
Self {
|
||||
config,
|
||||
folder_count: 0,
|
||||
file_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Startet den Scan und liefert das Ergebnis als String.
|
||||
pub fn build_tree(&mut self) -> Vec<String> {
|
||||
let root = self.config.root_path.clone();
|
||||
let mut lines = vec![format!("{} {}/", self.config.folder_icon, root.display())];
|
||||
self.scan_dir(&root, 0, "", &mut lines);
|
||||
lines
|
||||
}
|
||||
|
||||
fn scan_dir(&mut self, path: &Path, depth: usize, prefix: &str, lines: &mut Vec<String>) {
|
||||
if let Some(max) = self.config.max_depth {
|
||||
if depth >= max {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let entries = match fs::read_dir(path) {
|
||||
Ok(entries) => entries.filter_map(Result::ok).collect::<Vec<_>>(),
|
||||
Err(_) => {
|
||||
lines.push(format!("{}└── [Zugriff verweigert] {}", prefix, path.display()));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut folders = vec![];
|
||||
let mut files = vec![];
|
||||
|
||||
for entry in entries {
|
||||
let file_name = entry.file_name().into_string().unwrap_or_default();
|
||||
if entry.path().is_dir() {
|
||||
if !self.config.ignored_dirs.contains(&file_name) {
|
||||
folders.push((file_name, entry.path()));
|
||||
}
|
||||
} else {
|
||||
files.push(file_name);
|
||||
}
|
||||
}
|
||||
|
||||
for (idx, (name, path)) in folders.iter().enumerate() {
|
||||
self.folder_count += 1;
|
||||
let connector = if idx < folders.len() - 1 || !files.is_empty() { "├── " } else { "└── " };
|
||||
lines.push(format!("{}{}{} {}", prefix, connector, self.config.folder_icon, name));
|
||||
|
||||
let new_prefix = if idx < folders.len() - 1 || !files.is_empty() {
|
||||
format!("{}│ ", prefix)
|
||||
} else {
|
||||
format!("{} ", prefix)
|
||||
};
|
||||
|
||||
self.scan_dir(path, depth + 1, &new_prefix, lines);
|
||||
}
|
||||
|
||||
let visible_files = &files[..files.len().min(self.config.max_files_per_dir)];
|
||||
let remaining = files.len().saturating_sub(visible_files.len());
|
||||
|
||||
for (idx, name) in visible_files.iter().enumerate() {
|
||||
self.file_count += 1;
|
||||
let connector = if idx < visible_files.len() - 1 || remaining > 0 { "├── " } else { "└── " };
|
||||
lines.push(format!("{}{}{} {}", prefix, connector, self.config.file_icon, name));
|
||||
}
|
||||
|
||||
if remaining > 0 {
|
||||
lines.push(format!("{}└── <und {} weitere Dateien>", prefix, remaining));
|
||||
}
|
||||
}
|
||||
|
||||
/// Gibt die Anzahl gescannter Ordner und Dateien zurück.
|
||||
pub fn stats(&self) -> (usize, usize) {
|
||||
(self.folder_count, self.file_count)
|
||||
}
|
||||
|
||||
/// Richtet die Ausgabezeilen mit Kommentarspalte aus.
|
||||
pub fn align_lines_with_comments(&self, lines: &[String]) -> Vec<String> {
|
||||
let max_len = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||
lines
|
||||
.iter()
|
||||
.map(|line| {
|
||||
let padding = " ".repeat(max_len - line.len() + 2);
|
||||
format!("{}{}#", line, padding)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
59
src/config/args.rs
Normal file
59
src/config/args.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// CLI-Argumente für TreeScanner
|
||||
#[derive(Parser, Debug)]
|
||||
#[command( author ="Adam Skotarczak <adam@skotarczak.net>",
|
||||
version= "0.2.0",
|
||||
about = "TreeScanner: Verzeichnisse als ASCII-Baum visualisieren.",
|
||||
|
||||
long_about = r#"
|
||||
TreeScanner ist ein leichtgewichtiges CLI-Tool zur strukturierten Darstellung von Verzeichnisinhalten.
|
||||
|
||||
Funktionen:
|
||||
- Ausgabe als ASCII-Baum
|
||||
- Optionen für Tiefe, Datei-Limit und Ignorierlisten
|
||||
- Fortschrittsanzeige im Terminal
|
||||
- Unterstützung für Konfigurationsdateien und CLI
|
||||
|
||||
Beispiel:
|
||||
treescanner.exe --max-depth 3 --ignore .git,target
|
||||
"#
|
||||
)]
|
||||
pub struct CliArgs {
|
||||
/// Root-Verzeichnis für den Scan (Standard: aktuelles Verzeichnis)
|
||||
#[arg(default_value = ".")]
|
||||
pub root_path: PathBuf,
|
||||
|
||||
/// Maximale Scan-Tiefe
|
||||
#[arg(long)]
|
||||
pub max_depth: Option<usize>,
|
||||
|
||||
/// Maximale Dateianzahl pro Verzeichnis
|
||||
#[arg(long, default_value_t = 100)]
|
||||
pub max_files_per_dir: usize,
|
||||
|
||||
/// Verzeichnisse ignorieren (z. B. .git,target) durch Komma getrennt, ohne Leerzeichen.
|
||||
#[arg(short = 'x', long, value_delimiter = ',')]
|
||||
pub ignore: Vec<String>,
|
||||
|
||||
/// Ausgabeziel (Standard: tree.txt)
|
||||
#[arg(short = 'o', long)]
|
||||
pub output: Option<PathBuf>,
|
||||
|
||||
/// Nur in Konsole anzeigen, keine Ausgabedatei speichern
|
||||
#[arg(long)]
|
||||
pub viewonly: bool,
|
||||
|
||||
/// Debug-Modus aktivieren
|
||||
#[arg(short = 'D', long)]
|
||||
pub debug: bool,
|
||||
|
||||
/// Keine Statusausgaben
|
||||
#[arg(short = 'q', long)]
|
||||
pub quiet: bool,
|
||||
|
||||
/// Kommentare ausrichten (DEV: optisch instabil)
|
||||
#[arg(short = 'c', long, default_value_t = false)]
|
||||
pub align_comments: bool,
|
||||
}
|
87
src/config/loader.rs
Normal file
87
src/config/loader.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize, Default)]
|
||||
#[allow(dead_code)]
|
||||
pub struct ConfigFile {
|
||||
pub max_depth: Option<usize>,
|
||||
pub max_files_per_dir: Option<usize>,
|
||||
pub ignore: Option<HashSet<String>>,
|
||||
pub language: Option<String>,
|
||||
pub align_comments: Option<bool>,
|
||||
pub output: Option<String>,
|
||||
pub viewonly: Option<bool>,
|
||||
}
|
||||
|
||||
/// Lädt die Konfigurationsdatei aus dem Home-Verzeichnis (`~/.treescanner.conf`).
|
||||
///
|
||||
/// Falls die Datei nicht existiert, wird eine gültige Standard-Konfigurationsdatei erstellt.
|
||||
/// Bei Fehlern während des Lesens oder Parsens wird `None` zurückgegeben.
|
||||
///
|
||||
/// # Rückgabewert
|
||||
/// * `Some(ConfigFile)` – wenn Datei existiert und geparst werden konnte
|
||||
/// * `None` – bei Fehlern oder wenn Datei nicht erstellt/parst werden konnte
|
||||
pub fn load_config_from_home() -> Option<ConfigFile> {
|
||||
let home = dirs::home_dir()?;
|
||||
let config_path = home.join(".treescanner.conf");
|
||||
|
||||
// Versuche, die Datei zu lesen
|
||||
match fs::read_to_string(&config_path) {
|
||||
Ok(content) => {
|
||||
match toml::from_str::<ConfigFile>(&content) {
|
||||
Ok(cfg) => Some(cfg),
|
||||
Err(e) => {
|
||||
eprintln!("⚠️ Konfigurationsdatei ungültig (TOML-Fehler): {e}");
|
||||
println!("ℹ️ Du findest die Datei hier: {}", config_path.display());
|
||||
println!("🔁 Du kannst sie löschen, sie wird beim nächsten Start neu erstellt.");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Datei existiert nicht – erstelle neue Standard-Konfigurationsdatei
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
let default_config = r#"
|
||||
# TreeScanner Standard-Konfiguration
|
||||
# Nicht gesetzte Felder werden intern auf Defaultwerte gesetzt.
|
||||
|
||||
# max_depth = 5 # default: unendlich
|
||||
# max_files_per_dir = 1000 # default: 100
|
||||
ignore = [".git", ".fingerprint"] # default: ""
|
||||
language = "de"
|
||||
align_comments = false # default: false
|
||||
output = "treescanner.txt" # default: tree.txt
|
||||
viewonly = false # default: false
|
||||
"#;
|
||||
|
||||
let result = fs::File::create(&config_path)
|
||||
.and_then(|mut f| f.write_all(default_config.trim_start().as_bytes()));
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
println!(
|
||||
"\n✏️ Standard-Konfigurationsdatei erstellt: < {} >\n",
|
||||
config_path.display()
|
||||
);
|
||||
// Lies die frisch geschriebene Datei direkt ein
|
||||
fs::read_to_string(&config_path)
|
||||
.ok()
|
||||
.and_then(|s| toml::from_str(&s).ok())
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("⚠️ Konnte Standard-Konfigurationsdatei nicht schreiben: {e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Anderer Lese-Fehler (z. B. Berechtigung)
|
||||
Err(e) => {
|
||||
eprintln!("⚠️ Fehler beim Lesen der Konfigurationsdatei: {e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
2
src/config/mod.rs
Normal file
2
src/config/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod args;
|
||||
pub mod loader;
|
3
src/lib.rs
Normal file
3
src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod config;
|
||||
pub mod app;
|
||||
pub mod utils;
|
103
src/main.rs
103
src/main.rs
@ -1,10 +1,101 @@
|
||||
|
||||
// Importiert das Modul `utils`, das vermutlich Unterfunktionen wie Fenster oder Logging enthält
|
||||
mod utils;
|
||||
mod config;
|
||||
mod app;
|
||||
mod utils;
|
||||
|
||||
use app::treebuilder::{TreeBuilder, TreeBuilderConfig};
|
||||
use config::args::CliArgs;
|
||||
use config::loader::load_config_from_home;
|
||||
use utils::ascii_spinner::start_spinner;
|
||||
use clap::Parser;
|
||||
use std::fs;
|
||||
use std::time::Instant;
|
||||
use utils::logger::init_logger;
|
||||
|
||||
/// Gibt die verstrichene Zeit seit `timer` aus.
|
||||
fn view_timer(timer: &Instant) {
|
||||
println!("\n⏱️ Gesamtlaufzeit des Scans: {:.2?}", timer.elapsed());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Initialisiert das Logger-System (für Debug- oder Datei-Logging - nicht implementiert)
|
||||
utils::logger::init();
|
||||
app::run();
|
||||
init_logger();
|
||||
let args = CliArgs::parse();
|
||||
let file_config = load_config_from_home().unwrap_or_default();
|
||||
let timer = Instant::now();
|
||||
|
||||
if !args.root_path.is_dir() {
|
||||
eprintln!("⚠️ Fehler: '{}' ist kein gültiges Verzeichnis.", args.root_path.display());
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if let Some(parent) = args.output.as_ref().and_then(|p| p.parent()) {
|
||||
if let Err(e) = fs::create_dir_all(parent) {
|
||||
eprintln!("⚠️ Fehler beim Erstellen des Zielordners: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
let config = TreeBuilderConfig {
|
||||
root_path: args.root_path.clone(),
|
||||
max_depth: args.max_depth.or(file_config.max_depth),
|
||||
max_files_per_dir: if args.max_files_per_dir != 100 {
|
||||
args.max_files_per_dir
|
||||
} else {
|
||||
file_config.max_files_per_dir.unwrap_or(100)
|
||||
},
|
||||
// Ignorierte Verzeichnisse: CLI hat Vorrang, dann Config, sonst leer
|
||||
ignored_dirs: match (!args.ignore.is_empty(), file_config.ignore) {
|
||||
(true, _) => args.ignore,
|
||||
(false, Some(set)) => set.into_iter().collect(),
|
||||
(false, None) => vec![],
|
||||
},
|
||||
folder_icon: "📁".to_string(),
|
||||
file_icon: "📄".to_string(),
|
||||
align_comments: args.align_comments,
|
||||
};
|
||||
|
||||
let mut builder = TreeBuilder::new(config);
|
||||
|
||||
let (stop_spinner, spinner_handle) = if !args.quiet {
|
||||
let (s, h) = start_spinner(8);
|
||||
(Some(s), Some(h))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let mut output = builder.build_tree().join("\n");
|
||||
|
||||
if builder.config.align_comments {
|
||||
let lines = output.lines().map(String::from).collect::<Vec<_>>();
|
||||
output = builder.align_lines_with_comments(&lines).join("\n");
|
||||
}
|
||||
|
||||
if let Some(stop) = stop_spinner {
|
||||
let _ = stop.send(());
|
||||
}
|
||||
if let Some(handle) = spinner_handle {
|
||||
let _ = handle.join();
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
if !viewonly {
|
||||
if let Err(e) = fs::write(&output_path, &output) {
|
||||
eprintln!("Fehler beim Schreiben der Datei: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
if !args.quiet {
|
||||
let (folders, files) = builder.stats();
|
||||
println!(
|
||||
"Erfasst wurden {} Ordner und {} Dateien. Ergebnis in {} gespeichert.",
|
||||
folders,
|
||||
files,
|
||||
output_path.display()
|
||||
);
|
||||
view_timer(&timer);
|
||||
}
|
||||
} else {
|
||||
println!("{}", output);
|
||||
view_timer(&timer);
|
||||
}
|
||||
}
|
||||
|
38
src/utils/ascii_spinner.rs
Normal file
38
src/utils/ascii_spinner.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use std::sync::mpsc::{self, Sender};
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::time::Duration;
|
||||
use std::io::Write;
|
||||
|
||||
/// 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
|
||||
/// ```no_run
|
||||
/// # use treescanner::utils::ascii_spinner;
|
||||
/// let (stop, handle) = ascii_spinner::start_spinner(8); // 8 Ticks pro Sekunde
|
||||
/// let _ = stop.send(());
|
||||
/// let _ = handle.join();
|
||||
/// ```
|
||||
pub fn start_spinner(ticks_per_second: u64) -> (Sender<()>, JoinHandle<()>) {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let frames = vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
||||
let clamped = ticks_per_second.clamp(1, 20);
|
||||
let interval = Duration::from_millis(1000 / clamped);
|
||||
|
||||
let handle = thread::spawn(move || {
|
||||
let mut idx = 0;
|
||||
while rx.try_recv().is_err() {
|
||||
print!("\r[{}] läuft ...", frames[idx % frames.len()]);
|
||||
let _ = std::io::stdout().flush();
|
||||
idx += 1;
|
||||
thread::sleep(interval);
|
||||
}
|
||||
// Spinnerzeile zuverlässig löschen
|
||||
print!("\rr\x1B[2K\r"); // ANSI: ganze Zeile löschen
|
||||
let _ = std::io::stdout().flush();
|
||||
});
|
||||
|
||||
(tx, handle)
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
pub fn init() {
|
||||
pub fn init_logger() {
|
||||
}
|
||||
|
@ -1 +1,2 @@
|
||||
pub mod logger;
|
||||
pub mod ascii_spinner;
|
||||
|
54
tests/config_tests.rs
Normal file
54
tests/config_tests.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use treescanner::config::loader::{load_config_from_home, ConfigFile};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::fs::write;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/// Test: `ignore` aus Konfigurationsdatei wird korrekt geladen
|
||||
#[test]
|
||||
fn test_ignore_from_config_file() {
|
||||
let temp_home = tempfile::tempdir().unwrap();
|
||||
let config_path = temp_home.path().join(".treescanner.conf");
|
||||
|
||||
let config_content = r#"
|
||||
ignore = [".git", "target", ".vscode"]
|
||||
"#;
|
||||
write(&config_path, config_content).expect("Konnte temporäre Config nicht schreiben.");
|
||||
|
||||
unsafe {
|
||||
std::env::set_var("HOME", temp_home.path()); // für Unix
|
||||
std::env::set_var("USERPROFILE", temp_home.path()); // für Windows
|
||||
}
|
||||
|
||||
let config = load_config_from_home().expect("Konfig konnte nicht geladen werden.");
|
||||
let ignore_set = config.ignore.expect("ignore-Feld fehlt in Config");
|
||||
|
||||
assert!(ignore_set.contains(".git"));
|
||||
assert!(ignore_set.contains("target"));
|
||||
assert!(ignore_set.contains(".vscode"));
|
||||
}
|
35
tests/treebuilder_tests.rs
Normal file
35
tests/treebuilder_tests.rs
Normal file
@ -0,0 +1,35 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use treescanner::app::treebuilder::{TreeBuilder, TreeBuilderConfig};
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn mock_tree_builder() -> TreeBuilder {
|
||||
let config = TreeBuilderConfig {
|
||||
root_path: PathBuf::from("/mock"),
|
||||
max_depth: Some(1),
|
||||
max_files_per_dir: 3,
|
||||
ignored_dirs: vec![],
|
||||
folder_icon: "📁".to_string(),
|
||||
file_icon: "📄".to_string(),
|
||||
align_comments: true,
|
||||
};
|
||||
TreeBuilder::new(config)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_align_lines_with_comments() {
|
||||
let builder = mock_tree_builder();
|
||||
let lines = vec![
|
||||
"📁 src/".to_string(),
|
||||
"├── 📄 main.rs".to_string(),
|
||||
"└── 📄 lib.rs".to_string(),
|
||||
];
|
||||
|
||||
let aligned = builder.align_lines_with_comments(&lines);
|
||||
|
||||
// Prüfe, ob jede Zeile mit einem # endet
|
||||
for line in aligned {
|
||||
assert!(line.trim_end().ends_with('#'), "Fehlendes #: {}", line);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user