Merge branch 'develop'

This commit is contained in:
Adam Skotarczak 2025-05-17 21:04:24 +02:00
commit 04252c355b
Signed by: realAscot
GPG Key ID: 4CB9B8D93A96A538
22 changed files with 1217 additions and 34 deletions

19
.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,9 +9,12 @@ 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
# Releases (realAscot)
releases/**
bin/**

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"
}
]
}

27
CHANGELOG.md Normal file
View 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
View File

@ -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",
]

View File

@ -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
View 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
View 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
View File

@ -2,33 +2,208 @@
![TreeScanner-Logo](./media/logo-treescanner_512x512.png)
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)

View File

@ -1 +1 @@
0.1.0
1.0.0

6
desktop.ini Normal file
View File

@ -0,0 +1,6 @@
[.ShellClassInfo]
IconResource=C:\Users\adam\Documents\Werkbank\Projekte\Projekte-Rust\treeScannerRust\resources\icon.ico,0
[ViewState]
Mode=
Vid=
FolderType=Documents

View File

@ -1,3 +1 @@
pub fn run() {
}
pub mod treebuilder;

112
src/app/treebuilder.rs Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
pub mod args;
pub mod loader;

3
src/lib.rs Normal file
View File

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

View File

@ -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);
}
}

View 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)
}

View File

@ -1,2 +1,2 @@
pub fn init() {
pub fn init_logger() {
}

View File

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

54
tests/config_tests.rs Normal file
View 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"));
}

View 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);
}
}
}