Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fa5938a18
|
||
|
|
dc73051719
|
||
|
|
f54d3880ff
|
No files matched your search
@@ -1,7 +1,6 @@
|
||||
# Rust
|
||||
/target/
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
|
||||
+11
-56
@@ -1,58 +1,13 @@
|
||||
# Rustfmt configuration file
|
||||
# Based on your .clang-format preferences
|
||||
# Configured for nightly Rust with Allman brace style
|
||||
|
||||
# Indentation - matches your IndentWidth: 3, TabWidth: 3, UseTab: Never
|
||||
tab_spaces = 3
|
||||
|
||||
# Line length - matches your ColumnLimit: 100
|
||||
max_width = 100
|
||||
|
||||
# Edition
|
||||
# KofTwentyTwo rustfmt configuration (K22-CODE-RS). Stable-channel options only, so
|
||||
# `cargo fmt --check` behaves the same on every toolchain.
|
||||
edition = "2021"
|
||||
|
||||
# Brace style - matches your BreakBeforeBraces: Allman
|
||||
brace_style = "AlwaysNextLine" # Opening braces on new lines
|
||||
control_brace_style = "AlwaysNextLine" # Control structures with braces on new lines
|
||||
|
||||
# Comments - matches your AlignTrailingComments: true
|
||||
wrap_comments = true
|
||||
comment_width = 100
|
||||
normalize_comments = false # ReflowComments: false
|
||||
|
||||
# Imports and modules
|
||||
imports_granularity = "Crate"
|
||||
imports_layout = "Mixed"
|
||||
group_imports = "StdExternalCrate"
|
||||
|
||||
# Functions and expressions
|
||||
fn_single_line = false # AllowShortFunctionsOnASingleLine: None
|
||||
where_single_line = false
|
||||
|
||||
# Structs and enums
|
||||
struct_field_align_threshold = 0
|
||||
enum_discrim_align_threshold = 0
|
||||
|
||||
# Spacing
|
||||
space_before_colon = false
|
||||
space_after_colon = true
|
||||
spaces_around_ranges = false
|
||||
|
||||
# Trailing commas
|
||||
trailing_comma = "Vertical"
|
||||
|
||||
# Empty lines
|
||||
empty_item_single_line = true
|
||||
# max_empty_lines_to_keep = 3 # Not available in current nightly
|
||||
|
||||
# String formatting
|
||||
format_strings = true
|
||||
format_code_in_doc_comments = true
|
||||
|
||||
# Misc
|
||||
remove_nested_parens = true
|
||||
use_try_shorthand = true
|
||||
# Kingsrook: 3-space indentation (tab_spaces is a stable option).
|
||||
tab_spaces = 3
|
||||
hard_tabs = false
|
||||
newline_style = "Unix"
|
||||
# Kingsrook: no practical line limit. 200 keeps rustfmt's wrapping heuristics sane.
|
||||
max_width = 200
|
||||
use_field_init_shorthand = true
|
||||
force_explicit_abi = true
|
||||
|
||||
# This configuration enforces your C++ Allman brace style in Rust
|
||||
use_try_shorthand = true
|
||||
# Not set: brace_style = "AlwaysNextLine" and blank_lines_upper_bound are nightly-only.
|
||||
# Rust therefore keeps rustfmt's same-line braces (profile rule K22-CODE-RS-02).
|
||||
Generated
+344
@@ -0,0 +1,344 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "kfmtbox"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dirs",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.191"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[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.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.229"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.34+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
+33
-31
@@ -1,32 +1,34 @@
|
||||
# Clippy configuration file
|
||||
# This file should be in the project root
|
||||
|
||||
# Allow explicit return statements
|
||||
# Note: needless-return is not a valid clippy.toml option
|
||||
# Instead, we use the Cargo.toml configuration
|
||||
|
||||
# Other valid clippy.toml options:
|
||||
cognitive-complexity-threshold = 10
|
||||
too-many-arguments-threshold = 7
|
||||
too-many-lines-threshold = 100
|
||||
type-complexity-threshold = 200
|
||||
large-error-threshold = 128
|
||||
stack-size-threshold = 256
|
||||
enum-variant-size-threshold = 200
|
||||
array-size-threshold = 512
|
||||
max-fn-params-bools = 3
|
||||
max-struct-bools = 3
|
||||
max-trait-bounds = 3
|
||||
literal-representation-threshold = 10
|
||||
min-ident-chars-threshold = 3
|
||||
single-char-binding-names-threshold = 2
|
||||
missing-docs-in-crate-items = true
|
||||
check-private-items = true
|
||||
arithmetic-side-effects-allowed = []
|
||||
allow-unwrap-in-tests = false
|
||||
allow-expect-in-tests = false
|
||||
allow-panic-in-tests = false
|
||||
allow-dbg-in-tests = false
|
||||
allow-print-in-tests = false
|
||||
semicolon-inside-block-ignore-singleline = false
|
||||
# Clippy configuration file
|
||||
# This file should be in the project root
|
||||
|
||||
# Allow explicit return statements
|
||||
# Note: needless-return is not a valid clippy.toml option
|
||||
# Instead, we use the Cargo.toml configuration
|
||||
|
||||
# Other valid clippy.toml options:
|
||||
cognitive-complexity-threshold = 10
|
||||
too-many-arguments-threshold = 7
|
||||
too-many-lines-threshold = 100
|
||||
type-complexity-threshold = 200
|
||||
large-error-threshold = 128
|
||||
# Bytes (clippy default 512000). 256 cannot be met by ordinary frames: measured debug-build frames are 297-1633 bytes
|
||||
# (largest: render_flower_box at 1633, extract_content_from_box at 1551). 2048 keeps a gate just above them.
|
||||
stack-size-threshold = 2048
|
||||
enum-variant-size-threshold = 200
|
||||
array-size-threshold = 512
|
||||
max-fn-params-bools = 3
|
||||
max-struct-bools = 3
|
||||
max-trait-bounds = 3
|
||||
literal-representation-threshold = 10
|
||||
min-ident-chars-threshold = 3
|
||||
single-char-binding-names-threshold = 2
|
||||
missing-docs-in-crate-items = true
|
||||
check-private-items = true
|
||||
arithmetic-side-effects-allowed = []
|
||||
allow-unwrap-in-tests = false
|
||||
allow-expect-in-tests = false
|
||||
allow-panic-in-tests = false
|
||||
allow-dbg-in-tests = false
|
||||
allow-print-in-tests = false
|
||||
semicolon-inside-block-ignore-singleline = false
|
||||
semicolon-outside-block-ignore-multiline = false
|
||||
+11
-56
@@ -1,58 +1,13 @@
|
||||
# Rustfmt configuration file
|
||||
# Based on your .clang-format preferences
|
||||
# Configured for nightly Rust with Allman brace style
|
||||
|
||||
# Indentation - matches your IndentWidth: 3, TabWidth: 3, UseTab: Never
|
||||
tab_spaces = 3
|
||||
|
||||
# Line length - matches your ColumnLimit: 100
|
||||
max_width = 100
|
||||
|
||||
# Edition
|
||||
# KofTwentyTwo rustfmt configuration (K22-CODE-RS). Stable-channel options only, so
|
||||
# `cargo fmt --check` behaves the same on every toolchain.
|
||||
edition = "2021"
|
||||
|
||||
# Brace style - matches your BreakBeforeBraces: Allman
|
||||
brace_style = "AlwaysNextLine" # Opening braces on new lines
|
||||
control_brace_style = "AlwaysNextLine" # Control structures with braces on new lines
|
||||
|
||||
# Comments - matches your AlignTrailingComments: true
|
||||
wrap_comments = true
|
||||
comment_width = 100
|
||||
normalize_comments = false # ReflowComments: false
|
||||
|
||||
# Imports and modules
|
||||
imports_granularity = "Crate"
|
||||
imports_layout = "Mixed"
|
||||
group_imports = "StdExternalCrate"
|
||||
|
||||
# Functions and expressions
|
||||
fn_single_line = false # AllowShortFunctionsOnASingleLine: None
|
||||
where_single_line = false
|
||||
|
||||
# Structs and enums
|
||||
struct_field_align_threshold = 0
|
||||
enum_discrim_align_threshold = 0
|
||||
|
||||
# Spacing
|
||||
space_before_colon = false
|
||||
space_after_colon = true
|
||||
spaces_around_ranges = false
|
||||
|
||||
# Trailing commas
|
||||
trailing_comma = "Vertical"
|
||||
|
||||
# Empty lines
|
||||
empty_item_single_line = true
|
||||
# max_empty_lines_to_keep = 3 # Not available in current nightly
|
||||
|
||||
# String formatting
|
||||
format_strings = true
|
||||
format_code_in_doc_comments = true
|
||||
|
||||
# Misc
|
||||
remove_nested_parens = true
|
||||
use_try_shorthand = true
|
||||
# Kingsrook: 3-space indentation (tab_spaces is a stable option).
|
||||
tab_spaces = 3
|
||||
hard_tabs = false
|
||||
newline_style = "Unix"
|
||||
# Kingsrook: no practical line limit. 200 keeps rustfmt's wrapping heuristics sane.
|
||||
max_width = 200
|
||||
use_field_init_shorthand = true
|
||||
force_explicit_abi = true
|
||||
|
||||
# This configuration enforces your C++ Allman brace style in Rust
|
||||
use_try_shorthand = true
|
||||
# Not set: brace_style = "AlwaysNextLine" and blank_lines_upper_bound are nightly-only.
|
||||
# Rust therefore keeps rustfmt's same-line braces (profile rule K22-CODE-RS-02).
|
||||
+51
-102
@@ -1,17 +1,13 @@
|
||||
|
||||
use crate::{comment_detector::CommentStyle, text_wrapper::TextWrapper};
|
||||
|
||||
pub struct BoxRenderer
|
||||
{
|
||||
pub struct BoxRenderer {
|
||||
comment_style: CommentStyle,
|
||||
text_wrapper: TextWrapper,
|
||||
}
|
||||
|
||||
impl BoxRenderer
|
||||
{
|
||||
impl BoxRenderer {
|
||||
#[must_use]
|
||||
pub fn new(comment_style: CommentStyle, max_width: usize) -> Self
|
||||
{
|
||||
pub const fn new(comment_style: CommentStyle, max_width: usize) -> Self {
|
||||
#[allow(clippy::needless_return)]
|
||||
return Self {
|
||||
comment_style,
|
||||
@@ -20,8 +16,7 @@ impl BoxRenderer
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn render_flower_box(&self, content: &str) -> String
|
||||
{
|
||||
pub fn render_flower_box(&self, content: &str) -> String {
|
||||
// Check if content is already in a flower box
|
||||
let clean_content = self.extract_content_from_box(content);
|
||||
|
||||
@@ -29,130 +24,82 @@ impl BoxRenderer
|
||||
let wrapped_lines = self.text_wrapper.wrap_text(&clean_content);
|
||||
let balanced_lines = self.text_wrapper.balance_lines(wrapped_lines);
|
||||
|
||||
// Find the maximum line length for box width
|
||||
let max_line_len = balanced_lines
|
||||
.iter()
|
||||
.map(|line| line.len())
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
// Find the widest line, in characters, for the box width
|
||||
let max_line_len = balanced_lines.iter().map(|line| return line.chars().count()).max().unwrap_or(0);
|
||||
|
||||
let box_width = (max_line_len + 4).max(10); // Minimum width of 10
|
||||
// Inner width: one space either side of the text, minimum of 10
|
||||
let inner_width = max_line_len.saturating_add(2).max(10);
|
||||
let prefix = &self.comment_style.prefix;
|
||||
let border = "─".repeat(inner_width);
|
||||
|
||||
// Build the flower box
|
||||
let mut result = Vec::new();
|
||||
|
||||
// Top border
|
||||
result.push(format!(
|
||||
"{prefix} {}",
|
||||
"/".repeat(box_width)
|
||||
));
|
||||
result.push(format!("{prefix} ╭{border}╮"));
|
||||
|
||||
// Content lines
|
||||
for line in balanced_lines
|
||||
{
|
||||
let padding = box_width - line.len() - 2;
|
||||
let padded_line = format!(
|
||||
"{prefix} {line}{} {prefix}",
|
||||
" ".repeat(padding)
|
||||
);
|
||||
result.push(padded_line);
|
||||
for line in balanced_lines {
|
||||
let padding = inner_width.saturating_sub(line.chars().count()).saturating_sub(2);
|
||||
result.push(format!("{prefix} │ {line}{} │", " ".repeat(padding)));
|
||||
}
|
||||
|
||||
// Bottom border
|
||||
result.push(format!(
|
||||
"{prefix} {}",
|
||||
"/".repeat(box_width)
|
||||
));
|
||||
result.push(format!("{prefix} ╰{border}╯"));
|
||||
|
||||
result.join("\n")
|
||||
return result.join("\n");
|
||||
}
|
||||
|
||||
/// Extracts content from an existing flower box comment.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may panic if the input contains malformed flower box syntax
|
||||
/// or if string slicing operations fail due to UTF-8 boundary issues.
|
||||
fn extract_content_from_box(&self, content: &str) -> String
|
||||
{
|
||||
/// Extracts content from an existing flower box comment, or returns the input unchanged.
|
||||
fn extract_content_from_box(&self, content: &str) -> String {
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
if lines.len() < 3
|
||||
{
|
||||
let (Some(first), Some(last)) = (lines.first(), lines.last()) else {
|
||||
return content.to_string();
|
||||
};
|
||||
if lines.len() < 3 {
|
||||
return content.to_string();
|
||||
}
|
||||
|
||||
let prefix = &self.comment_style.prefix;
|
||||
// Check if this looks like a flower box
|
||||
let (first_line, last_line) = (first.trim(), last.trim());
|
||||
if !(first_line.contains('╭') && first_line.contains('╮') && last_line.contains('╰') && last_line.contains('╯')) {
|
||||
return content.to_string();
|
||||
}
|
||||
|
||||
let prefix = self.comment_style.prefix.as_str();
|
||||
let mut content_lines = Vec::new();
|
||||
|
||||
// Check if this looks like a flower box
|
||||
let first_line = lines[0].trim();
|
||||
let last_line = lines[lines.len() - 1].trim();
|
||||
// Extract content from the middle lines
|
||||
for line in lines.iter().skip(1).take(lines.len().saturating_sub(2)) {
|
||||
// Remove the comment prefix first
|
||||
let clean_line = if line.trim_start().starts_with(prefix) {
|
||||
line.split_once(prefix).map_or(*line, |(_, rest)| return rest)
|
||||
} else {
|
||||
line
|
||||
};
|
||||
|
||||
if first_line.contains("╭")
|
||||
&& first_line.contains("╮")
|
||||
&& last_line.contains("╰")
|
||||
&& last_line.contains("╯")
|
||||
{
|
||||
// Extract content from middle lines
|
||||
for line in lines.iter().skip(1).take(lines.len() - 2)
|
||||
{
|
||||
// Remove the comment prefix first
|
||||
let clean_line = if line.trim_start().starts_with(prefix)
|
||||
{
|
||||
&line[line.find(prefix).unwrap() + prefix.len()..]
|
||||
}
|
||||
else
|
||||
{
|
||||
line
|
||||
};
|
||||
|
||||
// Find the content between the pipe characters using char-based approach
|
||||
let chars: Vec<char> = clean_line.chars().collect();
|
||||
let mut start_idx = None;
|
||||
let mut end_idx = None;
|
||||
|
||||
for (i, ch) in chars.iter().enumerate()
|
||||
{
|
||||
if *ch == '│'
|
||||
{
|
||||
if start_idx.is_none()
|
||||
{
|
||||
start_idx = Some(i + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
end_idx = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(start), Some(end)) = (start_idx, end_idx)
|
||||
{
|
||||
if end > start
|
||||
{
|
||||
let content_part: String = chars[start..end].iter().collect();
|
||||
content_lines.push(content_part.trim().to_string());
|
||||
}
|
||||
// The content sits between the first two pipe characters
|
||||
let mut parts = clean_line.split('│').skip(1);
|
||||
if let (Some(inner), Some(_)) = (parts.next(), parts.next()) {
|
||||
if !inner.is_empty() {
|
||||
content_lines.push(inner.trim().to_string());
|
||||
}
|
||||
}
|
||||
return content_lines.join(" ");
|
||||
}
|
||||
#[allow(clippy::needless_return)]
|
||||
return content.to_string();
|
||||
return content_lines.join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests
|
||||
{
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::comment_detector::CommentStyle;
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if an assertion fails.
|
||||
#[test]
|
||||
fn test_render_flower_box()
|
||||
{
|
||||
fn test_render_flower_box() {
|
||||
let comment_style = CommentStyle::new("//".to_string());
|
||||
let renderer = BoxRenderer::new(comment_style, 80);
|
||||
let content = "Hello, world!";
|
||||
@@ -165,9 +112,11 @@ mod tests
|
||||
assert!(result.contains("Hello, world!"));
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if an assertion fails.
|
||||
#[test]
|
||||
fn test_extract_content_from_box()
|
||||
{
|
||||
fn test_extract_content_from_box() {
|
||||
let comment_style = CommentStyle::new("//".to_string());
|
||||
let renderer = BoxRenderer::new(comment_style, 80);
|
||||
|
||||
|
||||
+1
-2
@@ -4,8 +4,7 @@ use clap::Parser;
|
||||
#[command(name = "kfmtbox")]
|
||||
#[command(about = "A command-line utility that wraps text in flower box style comments")]
|
||||
#[command(version)]
|
||||
pub struct Cli
|
||||
{
|
||||
pub struct Cli {
|
||||
/// Input filename for file type detection
|
||||
#[arg(short = 'f', long = "filename")]
|
||||
pub filename: Option<String>,
|
||||
|
||||
+64
-72
@@ -1,101 +1,93 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CommentStyle
|
||||
{
|
||||
pub struct CommentStyle {
|
||||
pub prefix: String,
|
||||
pub is_block: bool,
|
||||
}
|
||||
|
||||
impl CommentStyle
|
||||
{
|
||||
pub fn new(prefix: String) -> Self
|
||||
{
|
||||
return Self {
|
||||
prefix,
|
||||
is_block: false,
|
||||
};
|
||||
impl CommentStyle {
|
||||
#[must_use]
|
||||
pub const fn new(prefix: String) -> Self {
|
||||
return Self { prefix, is_block: false };
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommentDetector
|
||||
{
|
||||
/// Default comment prefixes for common file types.
|
||||
const DEFAULT_STYLES: &[(&str, &str)] = &[
|
||||
("rs", "//"),
|
||||
("py", "#"),
|
||||
("js", "//"),
|
||||
("ts", "//"),
|
||||
("go", "//"),
|
||||
("java", "//"),
|
||||
("cpp", "//"),
|
||||
("c", "//"),
|
||||
("h", "//"),
|
||||
("hpp", "//"),
|
||||
("cs", "//"),
|
||||
("php", "//"),
|
||||
("rb", "#"),
|
||||
("sh", "#"),
|
||||
("bash", "#"),
|
||||
("zsh", "#"),
|
||||
("fish", "#"),
|
||||
("vim", "\""),
|
||||
("lua", "--"),
|
||||
("sql", "--"),
|
||||
("yaml", "#"),
|
||||
("yml", "#"),
|
||||
("toml", "#"),
|
||||
("ini", ";"),
|
||||
("cfg", ";"),
|
||||
("conf", "#"),
|
||||
("dockerfile", "#"),
|
||||
("makefile", "#"),
|
||||
("cmake", "#"),
|
||||
];
|
||||
|
||||
pub struct CommentDetector {
|
||||
pub default_style: CommentStyle,
|
||||
style_map: HashMap<String, CommentStyle>,
|
||||
}
|
||||
|
||||
impl CommentDetector
|
||||
{
|
||||
pub fn new() -> Self
|
||||
{
|
||||
let mut style_map = HashMap::new();
|
||||
impl CommentDetector {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
let style_map = DEFAULT_STYLES
|
||||
.iter()
|
||||
.map(|&(filetype, prefix)| return (filetype.to_string(), CommentStyle::new(prefix.to_string())))
|
||||
.collect();
|
||||
|
||||
// Default comment styles for common file types
|
||||
style_map.insert("rs".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("py".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("js".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("ts".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("go".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("java".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("cpp".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("c".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("h".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("hpp".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("cs".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("php".to_string(), CommentStyle::new("//".to_string()));
|
||||
style_map.insert("rb".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("sh".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("bash".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("zsh".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("fish".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("vim".to_string(), CommentStyle::new("\"".to_string()));
|
||||
style_map.insert("lua".to_string(), CommentStyle::new("--".to_string()));
|
||||
style_map.insert("sql".to_string(), CommentStyle::new("--".to_string()));
|
||||
style_map.insert("yaml".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("yml".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("toml".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("ini".to_string(), CommentStyle::new(";".to_string()));
|
||||
style_map.insert("cfg".to_string(), CommentStyle::new(";".to_string()));
|
||||
style_map.insert("conf".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("dockerfile".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("makefile".to_string(), CommentStyle::new("#".to_string()));
|
||||
style_map.insert("cmake".to_string(), CommentStyle::new("#".to_string()));
|
||||
|
||||
Self {
|
||||
return Self {
|
||||
default_style: CommentStyle::new("#".to_string()),
|
||||
style_map,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn detect_from_filename(&self, filename: &str) -> &CommentStyle
|
||||
{
|
||||
if let Some(ext) = filename.split('.').last()
|
||||
{
|
||||
let ext = ext.to_lowercase();
|
||||
self.style_map.get(&ext).unwrap_or(&self.default_style)
|
||||
}
|
||||
else
|
||||
{
|
||||
&self.default_style
|
||||
}
|
||||
#[must_use]
|
||||
pub fn detect_from_filename(&self, filename: &str) -> &CommentStyle {
|
||||
let ext = filename.rsplit('.').next().unwrap_or_default().to_lowercase();
|
||||
return self.style_map.get(&ext).unwrap_or(&self.default_style);
|
||||
}
|
||||
|
||||
pub fn detect_from_filetype(&self, filetype: &str) -> &CommentStyle
|
||||
{
|
||||
#[must_use]
|
||||
pub fn detect_from_filetype(&self, filetype: &str) -> &CommentStyle {
|
||||
let filetype = filetype.to_lowercase();
|
||||
self.style_map.get(&filetype).unwrap_or(&self.default_style)
|
||||
return self.style_map.get(&filetype).unwrap_or(&self.default_style);
|
||||
}
|
||||
|
||||
pub fn load_config(&mut self, config: &Config)
|
||||
{
|
||||
for (filetype, prefix) in &config.comment_styles
|
||||
{
|
||||
self
|
||||
.style_map
|
||||
.insert(filetype.clone(), CommentStyle::new(prefix.clone()));
|
||||
pub fn load_config(&mut self, config: &Config) {
|
||||
for (filetype, prefix) in &config.comment_styles {
|
||||
self.style_map.insert(filetype.clone(), CommentStyle::new(prefix.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CommentDetector {
|
||||
fn default() -> Self {
|
||||
return Self::new();
|
||||
}
|
||||
}
|
||||
+28
-31
@@ -1,63 +1,60 @@
|
||||
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use dirs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Config
|
||||
{
|
||||
pub struct Config {
|
||||
pub comment_styles: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Default for Config
|
||||
{
|
||||
fn default() -> Self
|
||||
{
|
||||
Self {
|
||||
comment_styles: HashMap::new(),
|
||||
}
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
return Self { comment_styles: HashMap::new() };
|
||||
}
|
||||
}
|
||||
|
||||
impl Config
|
||||
{
|
||||
pub fn load() -> Result<Self, Box<dyn std::error::Error>>
|
||||
{
|
||||
impl Config {
|
||||
/// Loads the configuration from the user's config directory, or returns the default when no file exists.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the config file cannot be read or is not valid YAML.
|
||||
pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let config_path = Self::get_config_path();
|
||||
|
||||
if config_path.exists()
|
||||
{
|
||||
if config_path.exists() {
|
||||
let content = std::fs::read_to_string(&config_path)?;
|
||||
let config: Config = serde_yaml::from_str(&content)?;
|
||||
Ok(config)
|
||||
}
|
||||
else
|
||||
{
|
||||
Ok(Config::default())
|
||||
let config: Self = serde_yaml::from_str(&content)?;
|
||||
return Ok(config);
|
||||
} else {
|
||||
return Ok(Self::default());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_config_path() -> PathBuf
|
||||
{
|
||||
let mut config_dir = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||
#[must_use]
|
||||
pub fn get_config_path() -> PathBuf {
|
||||
let mut config_dir = dirs::config_dir().unwrap_or_else(|| return PathBuf::from("."));
|
||||
config_dir.push("kfmtbox");
|
||||
config_dir.push("comments.yaml");
|
||||
config_dir
|
||||
return config_dir;
|
||||
}
|
||||
|
||||
pub fn save(&self) -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
/// Writes the configuration to the user's config directory, creating it if needed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the directory or file cannot be written, or the config cannot be serialized.
|
||||
pub fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config_path = Self::get_config_path();
|
||||
|
||||
// Create config directory if it doesn't exist
|
||||
if let Some(parent) = config_path.parent()
|
||||
{
|
||||
if let Some(parent) = config_path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
let content = serde_yaml::to_string(self)?;
|
||||
std::fs::write(&config_path, content)?;
|
||||
Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
+10
-17
@@ -1,8 +1,8 @@
|
||||
use std::io::{self, Read};
|
||||
|
||||
use clap::Parser;
|
||||
use kfmtbox::{BoxRenderer, Cli, CommentDetector, Config};
|
||||
use kfmtbox::verbose::{set_verbose, verbose_msg};
|
||||
use kfmtbox::{BoxRenderer, Cli, CommentDetector, Config};
|
||||
|
||||
/// Main entry point for the kfmtbox application.
|
||||
///
|
||||
@@ -15,15 +15,14 @@ use kfmtbox::verbose::{set_verbose, verbose_msg};
|
||||
/// - Input cannot be read from stdin
|
||||
/// - Configuration file cannot be loaded or parsed
|
||||
/// - Output cannot be written to stdout
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
set_verbose(cli.verbose);
|
||||
|
||||
// Load configuration
|
||||
verbose_msg("Loading Config");
|
||||
let config = Config::load().unwrap_or_else(|_| Config::default());
|
||||
let config = Config::load().unwrap_or_else(|_| return Config::default());
|
||||
|
||||
// Initialize comment detector
|
||||
verbose_msg("Detecting Comment Type");
|
||||
@@ -31,16 +30,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>>
|
||||
detector.load_config(&config);
|
||||
|
||||
// Determine comment style
|
||||
let comment_style = if let Some(filename) = &cli.filename
|
||||
{
|
||||
let comment_style = if let Some(filename) = &cli.filename {
|
||||
detector.detect_from_filename(filename).clone()
|
||||
}
|
||||
else if let Some(filetype) = &cli.filetype
|
||||
{
|
||||
} else if let Some(filetype) = &cli.filetype {
|
||||
detector.detect_from_filetype(filetype).clone()
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
detector.default_style.clone()
|
||||
};
|
||||
|
||||
@@ -52,10 +46,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>>
|
||||
// Remove trailing newline if present and normalize line endings
|
||||
verbose_msg("Cleaning Comment (Removing Newlines and Returns");
|
||||
let input = input.trim_end();
|
||||
let input = input.replace("\r\n", "\n").replace("\n\r", "\n").replace("\r", "\n");
|
||||
let input = input.replace("\r\n", "\n").replace("\n\r", "\n").replace('\r', "\n");
|
||||
|
||||
if input.is_empty()
|
||||
{
|
||||
if input.is_empty() {
|
||||
verbose_msg("Input was empty - returning nothing successfully");
|
||||
return Ok(());
|
||||
}
|
||||
@@ -67,7 +60,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>>
|
||||
|
||||
// Output to stdout
|
||||
verbose_msg("Outputting rendered comment");
|
||||
println!("{}", result);
|
||||
println!("{result}");
|
||||
|
||||
Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
+40
-71
@@ -1,113 +1,84 @@
|
||||
#[allow(clippy::needless_return)]
|
||||
|
||||
pub struct TextWrapper
|
||||
{
|
||||
pub struct TextWrapper {
|
||||
max_width: usize,
|
||||
}
|
||||
|
||||
impl TextWrapper
|
||||
{
|
||||
pub fn new(max_width: usize) -> Self
|
||||
{
|
||||
impl TextWrapper {
|
||||
#[must_use]
|
||||
pub const fn new(max_width: usize) -> Self {
|
||||
return Self { max_width };
|
||||
}
|
||||
|
||||
pub fn wrap_text(&self, text: &str) -> Vec<String>
|
||||
{
|
||||
#[must_use]
|
||||
pub fn wrap_text(&self, text: &str) -> Vec<String> {
|
||||
// First, split by newlines to preserve existing line breaks
|
||||
let input_lines: Vec<&str> = text.split('\n').collect();
|
||||
let mut all_lines = Vec::new();
|
||||
|
||||
for line in input_lines
|
||||
{
|
||||
for line in input_lines {
|
||||
let words: Vec<&str> = line.split_whitespace().collect();
|
||||
if words.is_empty()
|
||||
{
|
||||
if words.is_empty() {
|
||||
all_lines.push(String::new());
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut current_line = String::new();
|
||||
|
||||
for word in words
|
||||
{
|
||||
let test_line = if current_line.is_empty()
|
||||
{
|
||||
word.to_string()
|
||||
}
|
||||
else
|
||||
{
|
||||
format!("{} {}", current_line, word)
|
||||
};
|
||||
for word in words {
|
||||
let test_line = if current_line.is_empty() { word.to_string() } else { format!("{current_line} {word}") };
|
||||
|
||||
// Use a smaller effective width for better wrapping
|
||||
let effective_width = 20; // Force wrapping at 20 characters
|
||||
if test_line.len() <= effective_width
|
||||
{
|
||||
if test_line.len() <= effective_width {
|
||||
current_line = test_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
if !current_line.is_empty()
|
||||
{
|
||||
} else {
|
||||
if !current_line.is_empty() {
|
||||
all_lines.push(current_line);
|
||||
}
|
||||
current_line = word.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
if !current_line.is_empty()
|
||||
{
|
||||
if !current_line.is_empty() {
|
||||
all_lines.push(current_line);
|
||||
}
|
||||
}
|
||||
|
||||
if all_lines.is_empty()
|
||||
{
|
||||
if all_lines.is_empty() {
|
||||
return vec![String::new()];
|
||||
}
|
||||
|
||||
return all_lines;
|
||||
}
|
||||
|
||||
pub fn balance_lines(&self, lines: Vec<String>) -> Vec<String>
|
||||
{
|
||||
if lines.len() <= 1
|
||||
{
|
||||
#[must_use]
|
||||
pub fn balance_lines(&self, lines: Vec<String>) -> Vec<String> {
|
||||
if lines.len() <= 1 {
|
||||
return lines;
|
||||
}
|
||||
|
||||
let total_chars: usize = lines.iter().map(|line| line.len()).sum();
|
||||
let avg_chars_per_line = total_chars / lines.len();
|
||||
let target_width = (avg_chars_per_line + self.max_width) / 2;
|
||||
let total_chars: usize = lines.iter().map(|line| return line.len()).sum();
|
||||
let avg_chars_per_line = total_chars.checked_div(lines.len()).unwrap_or(0);
|
||||
let target_width = usize::midpoint(avg_chars_per_line, self.max_width);
|
||||
let max_combined_width = 25; // Don't combine lines if result would be longer than 25 chars
|
||||
|
||||
let mut balanced = Vec::new();
|
||||
let mut current_line = String::new();
|
||||
|
||||
for line in lines
|
||||
{
|
||||
if current_line.is_empty()
|
||||
{
|
||||
for line in lines {
|
||||
if current_line.is_empty() {
|
||||
current_line = line;
|
||||
}
|
||||
else
|
||||
{
|
||||
let test_line = format!("{} {}", current_line, line);
|
||||
if test_line.len() <= target_width && test_line.len() <= max_combined_width
|
||||
{
|
||||
} else {
|
||||
let test_line = format!("{current_line} {line}");
|
||||
if test_line.len() <= target_width && test_line.len() <= max_combined_width {
|
||||
current_line = test_line;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
balanced.push(current_line);
|
||||
current_line = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !current_line.is_empty()
|
||||
{
|
||||
if !current_line.is_empty() {
|
||||
balanced.push(current_line);
|
||||
}
|
||||
|
||||
@@ -116,35 +87,33 @@ impl TextWrapper
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests
|
||||
{
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if an assertion fails.
|
||||
#[test]
|
||||
fn test_wrap_text()
|
||||
{
|
||||
fn test_wrap_text() {
|
||||
let wrapper = TextWrapper::new(20);
|
||||
let text = "This is a very long line that should be wrapped into multiple lines";
|
||||
let result = wrapper.wrap_text(text);
|
||||
|
||||
assert!(result.len() > 1);
|
||||
for line in &result
|
||||
{
|
||||
for line in &result {
|
||||
assert!(line.len() <= 20);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if an assertion fails.
|
||||
#[test]
|
||||
fn test_balance_lines()
|
||||
{
|
||||
fn test_balance_lines() {
|
||||
let wrapper = TextWrapper::new(20);
|
||||
let lines = vec![
|
||||
"Short".to_string(),
|
||||
"Very long line that exceeds".to_string(),
|
||||
"Another short".to_string(),
|
||||
];
|
||||
let lines = vec!["Short".to_string(), "Very long line that exceeds".to_string(), "Another short".to_string()];
|
||||
let result = wrapper.balance_lines(lines);
|
||||
|
||||
assert!(result.len() >= 1);
|
||||
assert_eq!(result.len(), 3);
|
||||
}
|
||||
}
|
||||
+10
-21
@@ -1,41 +1,30 @@
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
// global flag
|
||||
pub static IS_VERBOSE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
// set from anywhere
|
||||
pub fn set_verbose(on: bool)
|
||||
{
|
||||
pub fn set_verbose(on: bool) {
|
||||
IS_VERBOSE.store(on, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// read from anywhere
|
||||
pub fn is_verbose() -> bool
|
||||
{
|
||||
IS_VERBOSE.load(Ordering::Relaxed)
|
||||
pub fn is_verbose() -> bool {
|
||||
return IS_VERBOSE.load(Ordering::Relaxed);
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn verbose_msg(msg: &str)
|
||||
{
|
||||
if!is_verbose()
|
||||
{
|
||||
pub fn verbose_msg(msg: &str) {
|
||||
if !is_verbose() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("[VERBOSE] {}", msg);
|
||||
println!("[VERBOSE] {msg}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn verbose_value<T: std::fmt::Debug>(label: &str, val: T)
|
||||
{
|
||||
if!is_verbose()
|
||||
{
|
||||
pub fn verbose_value<T: std::fmt::Debug>(label: &str, val: T) {
|
||||
if !is_verbose() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("[VERBOSE] {} = {:?}", label, val);
|
||||
}
|
||||
println!("[VERBOSE] {label} = {val:?}");
|
||||
}
|
||||
Reference in new issue
Block a user