5 Commits
Author SHA1 Message Date
forge-recovery 2fa5938a18 Merge pull request 'fix(ci): repair fmt, clippy and flower box rendering for central CI' (#3) from ci/central-ci-repair-20261101 into develop
KofTwentyTwo/ci-policy: ci / node-5 (push) Skipped
KofTwentyTwo/ci-policy: native / native-result (push) Successful in 11s
KofTwentyTwo/ci-policy: ci / rust-6 (push) Skipped
KofTwentyTwo/ci-policy: native / fork-deny (push) Skipped
KofTwentyTwo/ci-policy: ci / rust-7 (push) Successful in 2m30s
KofTwentyTwo/ci-policy: native / dotnet-0 (push) Skipped
KofTwentyTwo/ci-policy: ci / fork-guard (push) Skipped
KofTwentyTwo/ci-policy: ci / rust-8 (push) Skipped
KofTwentyTwo/ci-policy: native / swift-1 (push) Skipped
KofTwentyTwo/ci-policy: Shared Rust CI / secrets (push) Successful in 44s
KofTwentyTwo/ci-policy: native / swift-2 (push) Skipped
KofTwentyTwo/ci-policy: ci / gradle-0 (push) Skipped
KofTwentyTwo/ci-policy: ci / source-config-9 (push) Skipped
KofTwentyTwo/ci-policy: native / swift-3 (push) Skipped
KofTwentyTwo/ci-policy: ci / not-enrolled (push) Skipped
KofTwentyTwo/ci-policy: ci / gradle-1 (push) Skipped
KofTwentyTwo/ci-policy: ci / java-docker-2 (push) Skipped
KofTwentyTwo/ci-policy: Shared Rust CI / verify (push) Successful in 1m50s
KofTwentyTwo/ci-policy: Shared Rust CI / gate (push) Successful in 35s
KofTwentyTwo/ci-policy: ci / ci-result (push) Successful in 14s
KofTwentyTwo/ci-policy: ci / java-maven-3 (push) Skipped
KofTwentyTwo/ci-policy: ci / manifests-4 (push) Skipped
2026-11-01 19:56:14 +00:00
dc73051719 fix(test): assert exact balanced line count (clippy assert_is_empty on 1.99)
KofTwentyTwo/ci-policy: ci / fork-guard (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-docker-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-maven-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / manifests-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / node-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-8 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-9 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / not-enrolled (pull_request) Skipped
KofTwentyTwo/ci-policy: native / fork-deny (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / native-result (pull_request) Successful in 7s
KofTwentyTwo/ci-policy: Shared Rust CI / secrets (pull_request) Successful in 21s
KofTwentyTwo/ci-policy: Shared Rust CI / verify (pull_request) Successful in 1m18s
KofTwentyTwo/ci-policy: Shared Rust CI / gate (pull_request) Successful in 18s
KofTwentyTwo/ci-policy: ci / rust-7 (pull_request) Successful in 1m38s
KofTwentyTwo/ci-policy: ci / ci-result (pull_request) Successful in 5s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Co-Authored-By: Codex <noreply@openai.com>
2026-11-01 19:47:59 +00:00
f54d3880ff fix(ci): repair fmt, clippy and flower box rendering for central CI
KofTwentyTwo/ci-policy: ci / fork-guard (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-docker-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-maven-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / manifests-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / node-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-8 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-9 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / not-enrolled (pull_request) Skipped
KofTwentyTwo/ci-policy: Shared Rust CI / secrets (pull_request) Successful in 35s
KofTwentyTwo/ci-policy: Shared Rust CI / verify (pull_request) Failing after 1m38s
KofTwentyTwo/ci-policy: Shared Rust CI / gate (pull_request) Failing after 30s
KofTwentyTwo/ci-policy: ci / rust-7 (pull_request) Failing after 2m15s
KofTwentyTwo/ci-policy: ci / ci-result (pull_request) Failing after 13s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Co-Authored-By: Codex <noreply@openai.com>
2026-11-01 19:42:19 +00:00
james.maes a200688ec2 feat(verbose): added basic support for a verbose cli flag and utils for it in the code 2025-09-27 09:11:59 -05:00
james.maes 9ec0470c9c chore - Initial Import 2025-09-01 17:02:48 -05:00
41 changed files with 3706 additions and 21 deletions

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# All files
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# Rust files
[*.rs]
indent_style = space
indent_size = 4
# YAML files
[*.{yml,yaml}]
indent_style = space
indent_size = 2
# Markdown files
[*.md]
trim_trailing_whitespace = false
# TOML files
[*.toml]
indent_style = space
indent_size = 4
# JSON files
[*.json]
indent_style = space
indent_size = 2
# Shell scripts
[*.{sh,bash,zsh,fish}]
indent_style = space
indent_size = 2
# Makefile
[Makefile]
indent_style = tab
+12
View File
@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [yourusername]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective name
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2', 'link3', 'link4']
+56
View File
@@ -0,0 +1,56 @@
---
name: Bug report
about: Create a report to help us improve kfmtbox
title: '[BUG] '
labels: ['bug', 'needs-triage']
assignees: ''
---
## 🐛 Bug Description
A clear and concise description of what the bug is.
## 🔄 Steps to Reproduce
Steps to reproduce the behavior:
1. Run command: `...`
2. With input: `...`
3. See error: `...`
## 🎯 Expected Behavior
A clear and concise description of what you expected to happen.
## 📸 Actual Behavior
A clear and concise description of what actually happened.
## 💻 Environment
**OS:** [e.g. macOS 13.0, Ubuntu 22.04, Windows 11]
**Rust version:** [e.g. 1.70.0]
**kfmtbox version:** [e.g. 0.1.0]
**Terminal:** [e.g. iTerm2, Windows Terminal, GNOME Terminal]
## 📝 Sample Input
If applicable, provide the exact input that triggers the bug:
```bash
echo "your input here" | kfmtbox --filetype rs
```
## 📋 Additional Context
Add any other context about the problem here.
## 🔍 Possible Solution
If you have ideas on how to fix this bug, please describe them here.
## ✅ Checklist
- [ ] I have searched existing issues to ensure this is not a duplicate
- [ ] I have provided all the information requested above
- [ ] I have tested this with the latest version of kfmtbox
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: GitHub Community Support
url: https://github.com/yourusername/kfmtbox/discussions
about: Please ask and answer questions here.
- name: Documentation
url: https://github.com/yourusername/kfmtbox/wiki
about: Check our documentation for detailed guides and examples.
+55
View File
@@ -0,0 +1,55 @@
---
name: Feature request
about: Suggest an idea for kfmtbox
title: '[FEATURE] '
labels: ['enhancement', 'needs-triage']
assignees: ''
---
## 🚀 Feature Description
A clear and concise description of the feature you'd like to see implemented.
## 💡 Motivation
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
## 🎯 Proposed Solution
Describe the solution you'd like to see implemented.
## 🔄 Alternative Solutions
Describe any alternative solutions or features you've considered.
## 📋 Use Cases
Provide specific examples of how this feature would be used:
```bash
# Example usage
echo "text" | kfmtbox --new-flag
```
## 🎨 Design Considerations
If this feature involves UI changes, please describe:
- How should it look?
- What should the user experience be?
- Any specific design requirements?
## 🔧 Implementation Notes
If you have ideas about how this could be implemented, please share them.
## 📚 Additional Context
Add any other context, mockups, or examples about the feature request here.
## ✅ Checklist
- [ ] I have searched existing issues to ensure this is not a duplicate
- [ ] I have provided all the information requested above
- [ ] This feature would be useful to other users
- [ ] I am willing to help implement this feature if needed
+49
View File
@@ -0,0 +1,49 @@
---
name: Question
about: Ask a question about kfmtbox
title: '[QUESTION] '
labels: ['question', 'needs-triage']
assignees: ''
---
## ❓ Question
What would you like to know about kfmtbox?
## 🔍 What I've Tried
Describe what you've already tried or researched:
- [ ] Checked the README
- [ ] Searched existing issues
- [ ] Looked at the documentation
- [ ] Tried different approaches
## 💻 Environment
**OS:** [e.g. macOS 13.0, Ubuntu 22.04, Windows 11]
**Rust version:** [e.g. 1.70.0]
**kfmtbox version:** [e.g. 0.1.0]
## 📝 Example
If applicable, provide a code example or command that relates to your question:
```bash
# Your example here
echo "text" | kfmtbox --filetype rs
```
## 🎯 Expected Outcome
What would you like to achieve or understand?
## 📚 Additional Context
Add any other context, screenshots, or examples that might help answer your question.
## ✅ Checklist
- [ ] I have searched existing issues and discussions
- [ ] I have provided all relevant information
- [ ] My question is specific and clear
+105
View File
@@ -0,0 +1,105 @@
## 📋 Pull Request Description
<!-- Provide a brief description of the changes in this PR -->
## 🔗 Related Issues
<!-- Link to any related issues -->
- Closes #(issue number)
- Related to #(issue number)
## 🎯 Type of Change
<!-- Mark the relevant option with an "x" -->
- [ ] 🐛 Bug fix (non-breaking change which fixes an issue)
- [ ] ✨ New feature (non-breaking change which adds functionality)
- [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] 📚 Documentation update
- [ ] 🧪 Test improvements
- [ ] 🔧 Refactoring (no functional changes)
- [ ] ⚡ Performance improvement
- [ ] 🔒 Security fix
## 🧪 Testing
<!-- Describe the tests you ran to verify your changes -->
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Manual testing completed
- [ ] Tested on multiple platforms (if applicable)
### Test Commands
```bash
# Add any specific test commands you ran
cargo test
cargo clippy
cargo fmt
```
## 📸 Screenshots/Examples
<!-- If applicable, add screenshots or examples of the changes -->
### Before
```bash
# Example of behavior before changes
```
### After
```bash
# Example of behavior after changes
```
## 🔍 Code Quality
<!-- Mark completed items with an "x" -->
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Code is properly commented
- [ ] No unnecessary debug code or comments
- [ ] Error handling is appropriate
- [ ] Performance implications considered
## 📚 Documentation
<!-- Mark completed items with an "x" -->
- [ ] README updated (if needed)
- [ ] Code comments added/updated
- [ ] API documentation updated (if applicable)
- [ ] Examples updated (if applicable)
## 🚀 Deployment Notes
<!-- Any special deployment considerations -->
- [ ] No breaking changes
- [ ] Backward compatibility maintained
- [ ] Migration guide needed (if breaking changes)
- [ ] Configuration changes documented
## ✅ Checklist
<!-- Mark completed items with an "x" -->
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published
## 🔄 Additional Notes
<!-- Any additional information that reviewers should know -->
## 👥 Reviewers
<!-- Tag specific reviewers if needed -->
@username
+114
View File
@@ -0,0 +1,114 @@
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
env:
CARGO_TERM_COLOR: always
jobs:
test:
name: Test
runs-on: ubuntu-latest
strategy:
matrix:
rust: [stable, beta, nightly]
include:
- rust: stable
cache-key: stable
- rust: beta
cache-key: beta
- rust: nightly
cache-key: nightly
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Cache cargo registry
uses: actions/cache@v3
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ matrix.cache-key }}
- name: Cache cargo index
uses: actions/cache@v3
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ matrix.cache-key }}
- name: Cache cargo build
uses: actions/cache@v3
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ matrix.cache-key }}-${{ hashFiles('**/Cargo.lock') }}
- name: Install cargo-cache
run: cargo install cargo-cache
- name: Cache cargo
run: cargo cache -a
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Run clippy
uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-targets --all-features -- -D warnings
- name: Check formatting
run: cargo fmt --all -- --check
security:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Run security audit
run: cargo audit
build:
name: Build Release
runs-on: ${{ matrix.os }}
needs: [test, security]
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- name: Build release
run: cargo build --release --verbose
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: kfmtbox-${{ matrix.os }}
path: target/release/kfmtbox*
+23
View File
@@ -0,0 +1,23 @@
name: Dependabot Auto-merge
on:
pull_request:
types: [opened, synchronize]
jobs:
dependabot:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v1
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Enable auto-merge for Dependabot PRs
if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
+115
View File
@@ -0,0 +1,115 @@
name: Release
on:
push:
tags:
- 'v*'
env:
CARGO_TERM_COLOR: always
jobs:
create-release:
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
build:
needs: create-release
strategy:
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
asset_name: kfmtbox-linux-x86_64
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
asset_name: kfmtbox-linux-x86_64-musl
- os: macos-latest
target: x86_64-apple-darwin
asset_name: kfmtbox-macos-x86_64
- os: macos-latest
target: aarch64-apple-darwin
asset_name: kfmtbox-macos-aarch64
- os: windows-latest
target: x86_64-pc-windows-msvc
asset_name: kfmtbox-windows-x86_64.exe
- os: windows-latest
target: x86_64-pc-windows-gnu
asset_name: kfmtbox-windows-x86_64-gnu.exe
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Build
run: cargo build --release --target ${{ matrix.target }}
- name: Strip binary (Unix)
if: runner.os != 'Windows'
run: strip target/${{ matrix.target }}/release/kfmtbox
- name: Upload Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: target/${{ matrix.target }}/release/kfmtbox${{ matrix.target == 'x86_64-pc-windows-msvc' && '.exe' || matrix.target == 'x86_64-pc-windows-gnu' && '.exe' || '' }}
asset_name: ${{ matrix.asset_name }}
asset_content_type: application/octet-stream
publish-crate:
needs: create-release
runs-on: ubuntu-latest
if: github.repository_owner == 'koftwentytwo' # Only publish from the main repository
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Publish to crates.io
run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
update-homebrew:
needs: [create-release, build]
runs-on: ubuntu-latest
if: github.repository_owner == 'koftwentytwo' # Only update from the main repository
steps:
- name: Checkout homebrew-core
uses: actions/checkout@v4
with:
repository: Homebrew/homebrew-core
token: ${{ secrets.HOMEBREW_CORE_TOKEN }}
path: homebrew-core
- name: Update formula
run: |
# This would typically be done via a PR to homebrew-core
# For now, we'll just prepare the formula
echo "Formula update would go here"
echo "See: https://docs.brew.sh/Adding-Software-to-Homebrew"
+66 -17
View File
@@ -1,21 +1,70 @@
# Generated by Cargo
# will have compiled files and executables
debug
target
# These are backup files generated by rustfmt
# Rust
/target/
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# Generated by cargo mutants
# Contains mutation testing data
**/mutants.out*/
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# 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/
# Logs
*.log
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage/
# nyc test coverage
.nyc_output
# Dependency directories
node_modules/
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# Temporary folders
tmp/
temp/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Local configuration files
config.local.yaml
config.local.yml
+13
View File
@@ -0,0 +1,13 @@
# KofTwentyTwo rustfmt configuration (K22-CODE-RS). Stable-channel options only, so
# `cargo fmt --check` behaves the same on every toolchain.
edition = "2021"
# 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
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).
+110
View File
@@ -0,0 +1,110 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Initial release of kfmtbox
- Support for flower box comment formatting
- File type detection from filename and explicit filetype
- Text wrapping with balanced line lengths
- Existing comment detection and reformatting
- YAML configuration support for custom comment styles
- Support for 25+ programming languages
- CLI interface with comprehensive options
- Unit tests for core functionality
- GitHub Actions CI/CD pipeline
- Comprehensive documentation
### Features
- Beautiful Unicode box drawing characters (╭╮╰╯│─)
- Smart text wrapping with configurable max width
- Automatic comment style detection
- Configuration file support
- Editor integration examples
- Cross-platform support (Linux, macOS, Windows)
## [0.1.0] - 2024-09-01
### Added
- Initial release
- Core flower box rendering functionality
- File type detection system
- Text wrapping and balancing
- YAML configuration support
- CLI argument parsing with clap
- Comprehensive test suite
- Documentation and examples
### Supported File Types
- Rust (.rs) → //
- Python (.py) → #
- JavaScript (.js) → //
- TypeScript (.ts) → //
- Go (.go) → //
- Java (.java) → //
- C/C++ (.c, .cpp, .h, .hpp) → //
- C# (.cs) → //
- PHP (.php) → //
- Ruby (.rb) → #
- Shell (.sh, .bash, .zsh, .fish) → #
- Vim (.vim) → "
- Lua (.lua) → --
- SQL (.sql) → --
- YAML (.yaml, .yml) → #
- TOML (.toml) → #
- INI (.ini, .cfg) → ;
- Dockerfile → #
- Makefile → #
- CMake (CMakeLists.txt) → #
### CLI Options
- `-f, --filename` - Input filename for file type detection
- `-t, --filetype` - File type for comment style detection
- `-w, --max-width` - Maximum line width (default: 120)
- `-h, --help` - Print help information
- `-V, --version` - Print version information
### Configuration
- YAML configuration file support
- Custom comment style definitions
- Platform-specific config locations
- Default fallback comment styles
### Testing
- Unit tests for all core modules
- Integration tests for CLI functionality
- Text wrapping and balancing tests
- Comment detection and extraction tests
- Cross-platform compatibility testing
### Documentation
- Comprehensive README with examples
- Contributing guidelines
- Code of conduct
- Security policy
- API documentation
- Editor integration examples
---
## Version Numbering
This project uses [Semantic Versioning](https://semver.org/):
- **MAJOR** version when you make incompatible API changes
- **MINOR** version when you add functionality in a backwards compatible manner
- **PATCH** version when you make backwards compatible bug fixes
## Types of Changes
- **Added** for new features
- **Changed** for changes in existing functionality
- **Deprecated** for soon-to-be removed features
- **Removed** for now removed features
- **Fixed** for any bug fixes
- **Security** for vulnerability fixes
+122
View File
@@ -0,0 +1,122 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[INSERT CONTACT METHOD]. All complaints will be reviewed and investigated
promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+243
View File
@@ -0,0 +1,243 @@
# Contributing to kfmtbox
Thank you for your interest in contributing to kfmtbox! This document provides guidelines and information for contributors.
## 🚀 Getting Started
### Prerequisites
- Rust 1.70+ (edition 2021)
- Cargo
- Git
### Development Setup
1. **Fork and clone the repository**
```bash
git clone https://github.com/yourusername/kfmtbox.git
cd kfmtbox
```
2. **Build the project**
```bash
cargo build
```
3. **Run tests**
```bash
cargo test
```
4. **Check code quality**
```bash
cargo clippy
cargo fmt
```
## 🎯 How to Contribute
### Reporting Bugs
Before creating a bug report, please check existing issues to avoid duplicates. When creating a bug report, include:
- **Clear description** of the bug
- **Steps to reproduce** the issue
- **Expected behavior** vs actual behavior
- **Environment details** (OS, Rust version, etc.)
- **Sample input** that triggers the bug (if applicable)
Use the [bug report template](.github/ISSUE_TEMPLATE/bug_report.md) when creating issues.
### Suggesting Features
We welcome feature suggestions! When proposing a new feature:
- **Describe the problem** it solves
- **Explain the proposed solution**
- **Consider alternatives** you've thought about
- **Provide examples** of how it would work
Use the [feature request template](.github/ISSUE_TEMPLATE/feature_request.md) when creating issues.
### Code Contributions
#### Development Workflow
1. **Create a feature branch**
```bash
git checkout -b feature/your-feature-name
```
2. **Make your changes**
- Write clean, well-documented code
- Follow Rust naming conventions
- Add tests for new functionality
- Update documentation as needed
3. **Test your changes**
```bash
cargo test
cargo clippy
cargo fmt
```
4. **Commit your changes**
```bash
git add .
git commit -m "Add: brief description of changes"
```
5. **Push and create a Pull Request**
```bash
git push origin feature/your-feature-name
```
#### Code Style Guidelines
- **Formatting**: Use `cargo fmt` to format code
- **Linting**: Address all `cargo clippy` warnings
- **Documentation**: Document public APIs with doc comments
- **Tests**: Write unit tests for new functionality
- **Naming**: Follow Rust naming conventions (snake_case for functions/variables, PascalCase for types)
#### Commit Message Format
Use clear, descriptive commit messages:
```
Add: support for block comments in C/C++ files
Fix: text wrapping edge case with Unicode characters
Update: improve error messages for invalid file types
Docs: add examples for VSCode integration
Test: add unit tests for comment detection logic
```
## 🧪 Testing
### Running Tests
```bash
# Run all tests
cargo test
# Run tests with output
cargo test -- --nocapture
# Run specific test
cargo test test_name
# Run integration tests
cargo test --test integration_tests
```
### Writing Tests
- **Unit tests**: Place in the same file as the code being tested
- **Integration tests**: Place in `tests/` directory
- **Test coverage**: Aim for good test coverage of new functionality
- **Edge cases**: Test edge cases and error conditions
### Test Examples
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_comment_detection() {
let detector = CommentDetector::new();
let style = detector.detect_from_filetype("rs");
assert_eq!(style.prefix, "//");
}
#[test]
fn test_text_wrapping() {
let wrapper = TextWrapper::new(20);
let result = wrapper.wrap_text("This is a very long line");
assert!(result.len() > 1);
for line in &result {
assert!(line.len() <= 20);
}
}
}
```
## 📁 Project Structure
```
kfmtbox/
├── src/
│ ├── main.rs # CLI entry point
│ ├── lib.rs # Library exports
│ ├── cli.rs # Command line parsing
│ ├── box_renderer.rs # Flower box rendering
│ ├── text_wrapper.rs # Text wrapping logic
│ ├── comment_detector.rs # File type detection
│ └── config.rs # Configuration handling
├── tests/ # Integration tests
├── .github/ # GitHub workflows and templates
├── Cargo.toml # Project configuration
├── README.md # Project documentation
├── CONTRIBUTING.md # This file
└── LICENSE # MIT License
```
## 🔍 Code Review Process
### Pull Request Guidelines
- **Clear description** of changes and motivation
- **Link to related issues** if applicable
- **Screenshots or examples** for UI changes
- **Update documentation** as needed
- **Ensure all tests pass**
### Review Checklist
- [ ] Code follows project style guidelines
- [ ] Tests are included and passing
- [ ] Documentation is updated
- [ ] No breaking changes (or clearly documented)
- [ ] Performance implications considered
- [ ] Security implications reviewed
## 🐛 Common Issues and Solutions
### Build Issues
**Problem**: `cargo build` fails with dependency errors
**Solution**: Run `cargo update` to update dependencies
**Problem**: Tests fail on specific platforms
**Solution**: Check if the issue is platform-specific and add appropriate test conditions
### Code Issues
**Problem**: Clippy warnings
**Solution**: Address all warnings or add `#[allow(clippy::warning_name)]` with justification
**Problem**: Formatting issues
**Solution**: Run `cargo fmt` to auto-format code
## 📚 Resources
- [Rust Book](https://doc.rust-lang.org/book/)
- [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/)
- [Clap Documentation](https://docs.rs/clap/)
- [Serde Documentation](https://serde.rs/)
## 💬 Getting Help
- **GitHub Discussions**: For questions and general discussion
- **GitHub Issues**: For bug reports and feature requests
- **Pull Request Comments**: For code review discussions
## 🎉 Recognition
Contributors will be recognized in:
- README.md contributors section
- Release notes for significant contributions
- GitHub contributor graph
Thank you for contributing to kfmtbox! 🚀
Generated
+344
View File
@@ -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",
]
+96
View File
@@ -0,0 +1,96 @@
[package]
name = "kfmtbox"
version = "0.1.0"
edition = "2021"
authors = ["James Maes <james@kof22.com>"]
description = "A Rust-based command-line utility that wraps text in flower box style comments"
license = "MIT"
repository = "https://github.com/koftwentytwo/kfmtbox"
homepage = "https://github.com/koftwentytwo/kfmtbox"
documentation = "https://docs.rs/kfmtbox"
readme = "README.md"
keywords = ["cli", "formatting", "comments", "text-processing", "code-formatter"]
categories = ["command-line-utilities", "text-processing", "development-tools::build-utils"]
[dependencies]
clap = { version = "4.4", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
dirs = "6.0.0"
[[bin]]
name = "kfmtbox"
path = "src/main.rs"
[dev-dependencies]
# Linting configuration
[lints.clippy]
# Based on your .clang-tidy preferences
# Be strict about code quality and style
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
cargo = { level = "warn", priority = -1 }
# Disable some lints that might be too noisy
# (equivalent to disabling some checks in clang-tidy)
missing-docs-in-private-items = "allow"
module-name-repetitions = "allow"
# Keep explicit return statements (disable needless-return warnings)
needless-return = "allow"
redundant-else = "allow"
question-mark-used = "allow"
# Enable strict style checks (similar to readability-braces-around-statements)
needless-lifetimes = "warn"
redundant-closure = "warn"
unnecessary-lazy-evaluations = "warn"
# Performance and safety checks
unwrap-used = "warn"
expect-used = "warn"
panic = "warn"
unreachable = "warn"
# Complexity checks (similar to your C++ style preferences)
too-many-arguments = "warn"
too-many-lines = "warn"
cognitive-complexity = "warn"
# Type safety checks
cast-precision-loss = "warn"
cast-possible-truncation = "warn"
float-arithmetic = "warn"
# Memory and performance checks
large-enum-variant = "warn"
large-stack-arrays = "warn"
# String and collection checks
string-slice = "warn"
collection-is-never-read = "warn"
indexing-slicing = "warn"
# Async and concurrency checks
async-yields-async = "warn"
await-holding-lock = "warn"
# Documentation and naming
doc-markdown = "warn"
# Error handling
result-large-err = "warn"
missing-errors-doc = "warn"
# Macro usage
redundant-pattern-matching = "warn"
# Integer arithmetic
arithmetic-side-effects = "warn"
integer-division = "warn"
# Additional strict checks
implicit-return = "warn"
single-match-else = "warn"
+25
View File
@@ -0,0 +1,25 @@
class Kfmtbox < Formula
desc "A Rust-based command-line utility that wraps text in flower box style comments"
homepage "https://github.com/koftwentytwo/kfmtbox"
url "https://github.com/koftwentytwo/kfmtbox/archive/v0.1.0.tar.gz"
sha256 "" # This will be filled in after the first release
license "MIT"
head "https://github.com/koftwentytwo/kfmtbox.git", branch: "main"
depends_on "rust" => :build
def install
system "cargo", "install", *std_cargo_args
end
test do
# Test basic functionality
assert_match "kfmtbox", shell_output("#{bin}/kfmtbox --help")
# Test with a simple input
test_input = "Hello, world!"
expected_output = shell_output("echo '#{test_input}' | #{bin}/kfmtbox --filetype rs")
assert_match "╭", expected_output
assert_match "Hello, world!", expected_output
end
end
+2 -2
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 James Maes
Copyright (c) 2024 kfmtbox contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ 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.
SOFTWARE.
+251
View File
@@ -0,0 +1,251 @@
# Package Distribution Guide
This document explains how to distribute `kfmtbox` through various package managers and platforms.
## 📦 Package Managers
### 1. Homebrew (macOS/Linux)
#### Option A: Personal Tap (Recommended for initial release)
1. Create a personal tap repository: `koftwentytwo/homebrew-kfmtbox`
2. Add the formula from `Formula/kfmtbox.rb` to your tap
3. Users can install with:
```bash
brew tap koftwentytwo/kfmtbox
brew install kfmtbox
```
#### Option B: Homebrew Core (For wider distribution)
1. Submit a PR to [homebrew-core](https://github.com/Homebrew/homebrew-core)
2. Follow the [Homebrew formula guidelines](https://docs.brew.sh/Formula-Cookbook)
3. Requirements:
- At least 20 stars on GitHub
- At least 3 months old
- Stable API
- No dependencies on non-free software
### 2. Cargo (Rust)
#### Prerequisites
1. Create account on [crates.io](https://crates.io)
2. Get API token from account settings
3. Add token to GitHub secrets as `CARGO_REGISTRY_TOKEN`
#### Publishing
```bash
# Login to crates.io
cargo login
# Publish (done automatically via GitHub Actions)
cargo publish
```
### 3. Arch Linux (AUR)
Create a PKGBUILD file:
```bash
# Create AUR package
git clone ssh://aur@aur.archlinux.org/kfmtbox.git
cd kfmtbox
# Add PKGBUILD (see below)
git add PKGBUILD .SRCINFO
git commit -m "Initial release"
git push
```
**PKGBUILD template:**
```bash
pkgname=kfmtbox
pkgver=0.1.0
pkgrel=1
pkgdesc="A Rust-based command-line utility that wraps text in flower box style comments"
arch=('x86_64')
url="https://github.com/koftwentytwo/kfmtbox"
license=('MIT')
depends=('glibc')
makedepends=('cargo' 'git')
source=("$pkgname-$pkgver.tar.gz::https://github.com/koftwentytwo/kfmtbox/archive/v$pkgver.tar.gz")
sha256sums=('SKIP')
build() {
cd "$pkgname-$pkgver"
cargo build --release --locked
}
package() {
cd "$pkgname-$pkgver"
install -Dm755 "target/release/$pkgname" "$pkgdir/usr/bin/$pkgname"
install -Dm644 "README.md" "$pkgdir/usr/share/doc/$pkgname/README.md"
install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
}
```
### 4. Debian/Ubuntu (PPA)
#### Prerequisites
1. Create Launchpad account
2. Set up PPA
3. Install packaging tools: `sudo apt install devscripts debhelper`
#### Creating Debian Package
```bash
# Create package structure
mkdir kfmtbox-0.1.0
cd kfmtbox-0.1.0
# Copy source files
cp -r ../src .
cp ../Cargo.toml ../Cargo.lock .
# Create debian directory
mkdir debian
# Add control file, rules, etc.
# (See Debian packaging documentation)
```
### 5. Windows (Chocolatey)
#### Prerequisites
1. Create Chocolatey account
2. Install Chocolatey package validator
#### Package Structure
```
kfmtbox/
├── kfmtbox.nuspec
├── tools/
│ ├── chocolateyinstall.ps1
│ └── chocolateyuninstall.ps1
└── legal/
└── LICENSE.txt
```
**kfmtbox.nuspec:**
```xml
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>kfmtbox</id>
<version>0.1.0</version>
<title>kfmtbox</title>
<authors>James Maes</authors>
<description>A Rust-based command-line utility that wraps text in flower box style comments</description>
<licenseUrl>https://github.com/koftwentytwo/kfmtbox/blob/main/LICENSE</licenseUrl>
<projectUrl>https://github.com/koftwentytwo/kfmtbox</projectUrl>
<tags>cli formatting comments text-processing</tags>
</metadata>
<files>
<file src="tools\**" target="tools" />
<file src="legal\**" target="legal" />
</files>
</package>
```
### 6. Snap (Ubuntu/Snap Store)
#### Prerequisites
1. Create Snapcraft account
2. Install snapcraft: `sudo snap install snapcraft --classic`
#### snapcraft.yaml
```yaml
name: kfmtbox
version: '0.1.0'
summary: A Rust-based command-line utility that wraps text in flower box style comments
description: |
Transform your plain text into beautiful, decorative comment boxes that automatically
adapt to your programming language's comment syntax.
grade: stable
confinement: strict
parts:
kfmtbox:
plugin: rust
source: .
build-packages:
- gcc
- pkg-config
- libssl-dev
apps:
kfmtbox:
command: kfmtbox
plugs:
- home
```
## 🚀 Release Process
### 1. Automated Releases (GitHub Actions)
The project includes automated release workflows that:
1. **Build binaries** for multiple platforms:
- Linux (x86_64, musl)
- macOS (x86_64, ARM64)
- Windows (x86_64, MSVC and GNU)
2. **Publish to crates.io** (when `CARGO_REGISTRY_TOKEN` is set)
3. **Create GitHub releases** with downloadable binaries
### 2. Manual Release Steps
1. **Update version** in `Cargo.toml`
2. **Create git tag**: `git tag v0.1.0`
3. **Push tag**: `git push origin v0.1.0`
4. **GitHub Actions** will automatically:
- Build and test
- Create release
- Upload binaries
- Publish to crates.io (if configured)
### 3. Package Manager Updates
After each release, update package managers:
1. **Homebrew**: Update formula with new version and SHA
2. **AUR**: Update PKGBUILD with new version
3. **Snap**: Update snapcraft.yaml version
4. **Chocolatey**: Update nuspec version
## 📋 Checklist for New Release
- [ ] Update version in `Cargo.toml`
- [ ] Update `CHANGELOG.md`
- [ ] Create and push git tag
- [ ] Verify GitHub Actions release
- [ ] Update Homebrew formula (if using personal tap)
- [ ] Update AUR package
- [ ] Update other package managers
- [ ] Test installation from all package managers
- [ ] Update documentation
## 🔧 Troubleshooting
### Common Issues
1. **Homebrew formula fails**: Check SHA256 hash and URL
2. **Cargo publish fails**: Verify API token and package name availability
3. **AUR package rejected**: Check PKGBUILD syntax and dependencies
4. **Snap build fails**: Verify snapcraft.yaml syntax
### Getting Help
- [Homebrew Formula Cookbook](https://docs.brew.sh/Formula-Cookbook)
- [Cargo Publishing Guide](https://doc.rust-lang.org/cargo/reference/publishing.html)
- [AUR User Guidelines](https://wiki.archlinux.org/title/AUR_user_guidelines)
- [Snapcraft Documentation](https://snapcraft.io/docs)
## 📊 Distribution Statistics
Track your package downloads and usage:
- **Crates.io**: [crates.io/crates/kfmtbox](https://crates.io/crates/kfmtbox)
- **Homebrew**: `brew info kfmtbox`
- **GitHub**: Release download statistics
- **AUR**: AUR package page statistics
+333 -2
View File
@@ -1,2 +1,333 @@
# kfmtbox
A Rust-based command-line utility that wraps one or more lines of text in a “flower box” style comment, using the appropriate comment style for the file type.
# kfmtbox 🌸
> A beautiful Rust CLI tool that wraps your text in elegant flower box comments
[![Crates.io](https://img.shields.io/crates/v/kfmtbox.svg)](https://crates.io/crates/kfmtbox)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Build Status](https://github.com/koftwentytwo/kfmtbox/workflows/CI/badge.svg)](https://github.com/koftwentytwo/kfmtbox/actions)
Transform your plain text into beautiful, decorative comment boxes that automatically adapt to your programming language's comment syntax.
## ✨ Features
- 🎨 **Beautiful Unicode box drawing** with perfect rectangular formatting
- 🔍 **Smart file type detection** from filename or explicit specification
- 📝 **Intelligent text wrapping** with balanced line lengths
- 🔄 **Existing comment detection** - reformats existing flower boxes
- ⚙️ **YAML configuration** for custom comment styles
- 🚀 **Editor integration** ready for Neovim, VSCode, and more
- 🦀 **Written in Rust** - fast, reliable, and memory-safe
## 🚀 Quick Start
### Installation
#### 🚀 One-Line Install (Recommended)
```bash
# Linux/macOS
curl -sSL https://raw.githubusercontent.com/koftwentytwo/kfmtbox/main/install.sh | bash
# Windows PowerShell
powershell -ExecutionPolicy Bypass -File <(curl -sSL https://raw.githubusercontent.com/koftwentytwo/kfmtbox/main/install.ps1)
```
#### 📦 Package Managers
**Homebrew (macOS/Linux)**
```bash
# Add the tap (after first release)
brew tap koftwentytwo/kfmtbox
brew install kfmtbox
# Or install directly from formula
brew install koftwentytwo/kfmtbox/kfmtbox
```
**Cargo (Rust)**
```bash
cargo install kfmtbox
```
**Arch Linux (AUR)**
```bash
# After AUR package is created
yay -S kfmtbox
```
#### 🔨 From Source
```bash
git clone https://github.com/koftwentytwo/kfmtbox.git
cd kfmtbox
cargo build --release
sudo cp target/release/kfmtbox /usr/local/bin/
```
#### 📥 Manual Download
Visit the [Releases](https://github.com/koftwentytwo/kfmtbox/releases) page to download pre-built binaries for your platform.
### Basic Usage
```bash
echo "Hello, world!" | kfmtbox --filetype rs
```
**Output:**
```
// ╭───────────────╮
// │Hello, world! │
// ╰───────────────╯
```
## 📖 Examples
### Different Programming Languages
```bash
# Rust
echo "Important note" | kfmtbox --filetype rs
# Python
echo "TODO: Fix this bug" | kfmtbox --filetype py
# JavaScript
echo "This function does magic" | kfmtbox --filetype js
# SQL
echo "Complex query explanation" | kfmtbox --filetype sql
```
### Text Wrapping
```bash
# Long text automatically wraps and balances
echo "This is a very long comment that will be automatically wrapped into multiple lines with balanced spacing" | kfmtbox --filetype py --max-width 40
```
### Reformatting Existing Comments
```bash
# Reformat existing flower boxes
echo "// ╭─────╮
// │Old │
// ╰─────╯" | kfmtbox --filename test.rs
```
## 🛠️ Command Line Options
| Option | Description | Example |
|-------------------|---------------------------------------|----------------------|
| `-f, --filename` | Detect file type from filename | `--filename main.rs` |
| `-t, --filetype` | Specify file type explicitly | `--filetype py` |
| `-w, --max-width` | Set maximum line width (default: 120) | `--max-width 80` |
| `-h, --help` | Show help information | `--help` |
| `-V, --version` | Show version information | `--version` |
## ⚙️ Configuration
Create a YAML configuration file to customize comment styles:
**macOS:** `~/Library/Application Support/kfmtbox/comments.yaml`
**Linux:** `~/.config/kfmtbox/comments.yaml`
**Windows:** `%APPDATA%\kfmtbox\comments.yaml`
```yaml
comment_styles:
# Standard languages
rust: "//"
python: "#"
javascript: "//"
typescript: "//"
go: "//"
java: "//"
cpp: "//"
c: "//"
php: "//"
ruby: "#"
shell: "#"
vim: "\""
lua: "--"
sql: "--"
yaml: "#"
toml: "#"
ini: ";"
dockerfile: "#"
makefile: "#"
cmake: "#"
# Custom file types
custom: "%%"
special: "!!"
```
## 🎯 Supported File Types
| Language | Extensions | Comment Style |
|----------------|---------------------------------|---------------|
| **Rust** | `.rs` | `//` |
| **Python** | `.py` | `#` |
| **JavaScript** | `.js` | `//` |
| **TypeScript** | `.ts` | `//` |
| **Go** | `.go` | `//` |
| **Java** | `.java` | `//` |
| **C/C++** | `.c`, `.cpp`, `.h`, `.hpp` | `//` |
| **C#** | `.cs` | `//` |
| **PHP** | `.php` | `//` |
| **Ruby** | `.rb` | `#` |
| **Shell** | `.sh`, `.bash`, `.zsh`, `.fish` | `#` |
| **Vim** | `.vim` | `"` |
| **Lua** | `.lua` | `--` |
| **SQL** | `.sql` | `--` |
| **YAML** | `.yaml`, `.yml` | `#` |
| **TOML** | `.toml` | `#` |
| **INI** | `.ini`, `.cfg` | `;` |
| **Dockerfile** | `Dockerfile` | `#` |
| **Makefile** | `Makefile` | `#` |
| **CMake** | `CMakeLists.txt` | `#` |
## 🔌 Editor Integration
### Neovim
Add to your `init.vim` or `init.lua`:
```vim
" Format selected text as flower box
vnoremap <leader>fb :!kfmtbox --filename %<CR>
" Language-specific shortcuts
vnoremap <leader>fr :!kfmtbox --filetype rs<CR>
vnoremap <leader>fp :!kfmtbox --filetype py<CR>
vnoremap <leader>fj :!kfmtbox --filetype js<CR>
```
### VSCode
Create a custom task in `.vscode/tasks.json`:
```json
{
"version": "2.0.0",
"tasks": [
{
"label": "Format as Flower Box",
"type": "shell",
"command": "kfmtbox",
"args": ["--filename", "${file}"],
"group": "build"
}
]
}
```
### Emacs
Add to your `.emacs` or `init.el`:
```elisp
(defun kfmtbox-format-region ()
"Format selected region as flower box"
(interactive)
(shell-command-on-region (region-beginning) (region-end)
(format "kfmtbox --filename %s" (buffer-file-name))
nil t))
(global-set-key (kbd "C-c f b") 'kfmtbox-format-region)
```
## 🧪 Development
### Prerequisites
- Rust 1.70+ (edition 2021)
- Cargo
### Building
```bash
# Clone the repository
git clone https://github.com/yourusername/kfmtbox.git
cd kfmtbox
# Build in debug mode
cargo build
# Build optimized release
cargo build --release
# Run tests
cargo test
# Check code quality
cargo clippy
# Format code
cargo fmt
```
### Project Structure
```
kfmtbox/
├── src/
│ ├── main.rs # CLI entry point
│ ├── lib.rs # Library exports
│ ├── cli.rs # Command line argument parsing
│ ├── box_renderer.rs # Flower box rendering logic
│ ├── text_wrapper.rs # Text wrapping and balancing
│ ├── comment_detector.rs # File type and comment style detection
│ └── config.rs # YAML configuration handling
├── Cargo.toml # Project dependencies and metadata
├── README.md # This file
├── LICENSE # MIT License
└── .github/ # GitHub workflows and templates
```
## 🤝 Contributing
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
### Quick Contribution Steps
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Add tests for new functionality
5. Run the test suite (`cargo test`)
6. Commit your changes (`git commit -m 'Add amazing feature'`)
7. Push to your branch (`git push origin feature/amazing-feature`)
8. Open a Pull Request
## 📋 Roadmap
- [ ] **Block comment support** for languages like C/C++ (`/* */`)
- [ ] **Theming system** with different border styles
- [ ] **VSCode extension** for seamless integration
- [ ] **JetBrains plugin** for IntelliJ, PyCharm, etc.
- [ ] **Auto-detection** of file type from stdin context
- [ ] **Configurable padding** and border characters
- [ ] **Multi-line input** with automatic paragraph detection
## 🐛 Bug Reports & Feature Requests
Found a bug or have an idea? We'd love to hear from you!
- 🐛 [Report a bug](https://github.com/yourusername/kfmtbox/issues/new?template=bug_report.md)
- 💡 [Request a feature](https://github.com/yourusername/kfmtbox/issues/new?template=feature_request.md)
- 💬 [Start a discussion](https://github.com/yourusername/kfmtbox/discussions)
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## 🙏 Acknowledgments
- Inspired by the need for beautiful, consistent comment formatting
- Built with ❤️ using [Rust](https://www.rust-lang.org/)
- Uses [clap](https://github.com/clap-rs/clap) for CLI parsing
- Configuration powered by [serde_yaml](https://github.com/dtolnay/serde-yaml)
---
**Made with ❤️ by the kfmtbox team**
[⭐ Star this repo](https://github.com/yourusername/kfmtbox) if you find it useful!
+82
View File
@@ -0,0 +1,82 @@
# Security Policy
## Supported Versions
We release patches for security vulnerabilities in the following versions:
| Version | Supported |
|---------|--------------------|
| 0.1.x | :white_check_mark: |
| < 0.1 | :x: |
## Reporting a Vulnerability
We take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
### How to Report a Security Vulnerability
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them via email to: [security@example.com](mailto:security@example.com)
You should receive a response within 48 hours. If for some reason you do not, please follow up via email to ensure we received your original message.
### What to Include
Please include the following information in your report:
- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
- Full paths of source file(s) related to the manifestation of the issue
- The location of the affected source code (tag/branch/commit or direct URL)
- Any special configuration required to reproduce the issue
- Step-by-step instructions to reproduce the issue
- Proof-of-concept or exploit code (if possible)
- Impact of the issue, including how an attacker might exploit it
### What to Expect
After you submit a report, we will:
1. Confirm receipt of your vulnerability report within 48 hours
2. Provide regular updates on our progress
3. Credit you in our security advisory (unless you prefer to remain anonymous)
### Security Best Practices
When using kfmtbox, please follow these security best practices:
- **Keep kfmtbox updated** to the latest version
- **Validate input** before passing it to kfmtbox
- **Use trusted sources** for configuration files
- **Review configuration files** before using them
- **Run with minimal privileges** when possible
### Security Considerations
kfmtbox is designed with security in mind:
- **No network access** - kfmtbox operates entirely locally
- **Input validation** - All input is validated and sanitized
- **Memory safety** - Written in Rust for memory safety guarantees
- **Dependency auditing** - Regular security audits of dependencies
- **Minimal attack surface** - Simple, focused functionality
### Known Security Issues
Currently, there are no known security vulnerabilities in kfmtbox.
### Security Updates
Security updates will be released as soon as possible after a vulnerability is confirmed and a fix is available. We will:
- Release a new version with the security fix
- Publish a security advisory
- Update this document if needed
### Contact
For security-related questions or concerns, please contact us at:
- Email: [security@example.com](mailto:security@example.com)
- GitHub Security Advisories: [Create a private vulnerability report](https://github.com/yourusername/kfmtbox/security/advisories/new)
Thank you for helping keep kfmtbox and our users safe!
+102
View File
@@ -0,0 +1,102 @@
# Quick Setup Guide for Package Distribution
This guide helps you quickly set up `kfmtbox` for distribution across multiple package managers.
## 🚀 Quick Start (5 minutes)
### 1. First Release Setup
```bash
# 1. Set up GitHub secrets (in your repository settings)
# Go to: Settings > Secrets and variables > Actions
# Add these secrets:
# - CARGO_REGISTRY_TOKEN (from crates.io account)
# - HOMEBREW_CORE_TOKEN (GitHub token with repo access)
# 2. Create your first release
./scripts/release.sh 0.1.0
# 3. Wait for GitHub Actions to complete
# 4. Check the release at: https://github.com/koftwentytwo/kfmtbox/releases
```
### 2. Package Manager Setup
#### Homebrew (Personal Tap)
```bash
# Create a new repository: koftwentytwo/homebrew-kfmtbox
# Copy Formula/kfmtbox.rb to your tap repository
# Users can then install with:
brew tap koftwentytwo/kfmtbox
brew install kfmtbox
```
#### Cargo (crates.io)
```bash
# 1. Create account at https://crates.io
# 2. Get API token from account settings
# 3. Add token to GitHub secrets as CARGO_REGISTRY_TOKEN
# 4. GitHub Actions will automatically publish on release
```
#### Arch Linux (AUR)
```bash
# 1. Create AUR account at https://aur.archlinux.org
# 2. Clone the AUR repository:
git clone ssh://aur@aur.archlinux.org/kfmtbox.git
cd kfmtbox
# 3. Create PKGBUILD (see PACKAGING.md for template)
# 4. Create .SRCINFO: makepkg --printsrcinfo > .SRCINFO
# 5. Commit and push
```
## 📋 Pre-Release Checklist
- [ ] Update `Cargo.toml` version
- [ ] Update `CHANGELOG.md`
- [ ] Run `cargo test` and `cargo clippy`
- [ ] Test installation scripts locally
- [ ] Verify all documentation links work
## 🔧 Troubleshooting
### Common Issues
1. **GitHub Actions fails**: Check secrets are set correctly
2. **Cargo publish fails**: Verify API token and package name
3. **Homebrew formula fails**: Check SHA256 hash in formula
4. **Installation scripts fail**: Test on clean system
### Getting Help
- Check the full [PACKAGING.md](PACKAGING.md) for detailed instructions
- Review [GitHub Actions logs](https://github.com/koftwentytwo/kfmtbox/actions)
- Test installation scripts on different platforms
## 🎯 Success Metrics
Track your distribution success:
- **Downloads**: GitHub release statistics
- **Cargo installs**: `cargo install --list | grep kfmtbox`
- **Homebrew installs**: `brew info kfmtbox`
- **User feedback**: GitHub issues and discussions
## 📈 Next Steps
After successful initial release:
1. **Monitor usage**: Check download statistics
2. **Gather feedback**: Respond to issues and feature requests
3. **Expand distribution**: Add more package managers
4. **Improve documentation**: Update README with user examples
5. **Community building**: Share on social media and forums
## 🚀 Pro Tips
- **Start small**: Begin with GitHub releases and Cargo
- **Automate everything**: Use GitHub Actions for all releases
- **Test thoroughly**: Verify installation on multiple platforms
- **Document everything**: Keep PACKAGING.md updated
- **Engage community**: Respond to issues and feature requests quickly
+218
View File
@@ -0,0 +1,218 @@
# Style Guide for kfmtbox
This project follows strict coding standards based on C++ Allman brace style and formatting preferences.
## IDE Integration Strategy
With RustIDE using nightly Rust, we now have **full Allman brace style support**:
1. **IDE Formatting**: Uses Allman brace style (opening braces on new lines)
2. **Automatic Enforcement**: Allman style applied automatically by rustfmt
3. **Consistent Indentation**: 3-space indentation (matches your C++ style)
4. **Quality Checks**: Clippy enforces code quality and style consistency
## Brace Style: Allman Style (Manual Enforcement)
**Opening braces `{` should be on a new line after control structures when manually formatting.**
### ✅ Correct (Allman Style):
```rust
if condition
{
// code here
}
for item in items
{
// code here
}
while condition
{
// code here
}
match value
{
Some(x) => {
// code here
}
None => {
// code here
}
}
impl MyStruct
{
fn method(&self)
{
// code here
}
}
```
### ❌ Incorrect (K&R Style):
```rust
if condition {
// code here
}
for item in items {
// code here
}
impl MyStruct {
fn method(&self) {
// code here
}
}
```
## Formatting Configuration
### For IDE Integration (Recommended)
- **Indentation**: 3 spaces (matches your C++ style)
- **Line length**: 100 characters
- **Brace style**: Allman style (opening braces on new lines)
- **Automatic enforcement**: Applied by nightly rustfmt
### For Manual Allman Style (Optional)
If you want to manually format with Allman brace style:
```bash
# Use the nightly formatting script
./scripts/format.sh
# Or manually use nightly rustfmt
rustup run nightly cargo fmt --config rustfmt-nightly.toml
```
### IDE Setup
Your RustIDE with nightly Rust will automatically:
- Use 3-space indentation
- Enforce 100-character line limits
- Format with Allman brace style (opening braces on new lines)
- Run clippy for quality checks
- Apply consistent import grouping
## Enforcement
### IDE Integration (Primary)
Your IDE will automatically:
1. **Format code** with 3-space indentation and 100-char lines
2. **Run clippy** for quality and style warnings
3. **Check formatting** on save
4. **Provide suggestions** for improvements
### Manual Enforcement (Secondary)
For Allman brace style specifically:
1. **Code review** - All PRs should follow Allman style where possible
2. **Documentation** - This style guide serves as reference
3. **Optional script** - Use `./scripts/format.sh` for Allman formatting
### Automated Checks
```bash
# Check formatting (IDE compatible)
cargo fmt --check
# Run clippy for quality warnings
cargo clippy --all-targets --all-features
# Run tests
cargo test
# Optional: Format with Allman style
./scripts/format.sh
```
## Configuration Files
- `rustfmt.toml` - Stable Rust formatting (3-space indentation, 100-char lines)
- `rustfmt-nightly.toml` - Nightly Rust formatting (includes Allman brace style)
- `clippy.toml` - Linting rules for code quality
- `Cargo.toml` - Project-level linting configuration
## Examples
### Function Definitions
```rust
fn my_function(param1: String, param2: usize) -> Result<String, Error>
{
if param1.is_empty()
{
return Err(Error::InvalidInput);
}
for i in 0..param2
{
// process each item
}
Ok("success".to_string())
}
```
### Struct and Impl Blocks
```rust
pub struct MyStruct
{
field1: String,
field2: usize,
}
impl MyStruct
{
pub fn new(field1: String, field2: usize) -> Self
{
Self { field1, field2 }
}
pub fn process(&self) -> Result<(), Error>
{
if self.field1.is_empty()
{
return Err(Error::InvalidState);
}
Ok(())
}
}
```
### Match Statements
```rust
match result
{
Ok(value) => {
println!("Success: {}", value);
}
Err(error) => {
eprintln!("Error: {}", error);
}
}
```
## Integration with CI/CD
The GitHub Actions workflow will:
1. Check formatting with `cargo fmt --check`
2. Run clippy with strict rules
3. Run tests
4. Build for multiple platforms
All code must pass these checks before merging.
## Migration Guide
If you have existing code that doesn't follow Allman style:
1. **Manual conversion**: Update braces to be on new lines
2. **Use nightly formatter**: `rustup run nightly cargo fmt --config rustfmt-nightly.toml`
3. **Verify with clippy**: `cargo clippy --all-targets --all-features`
## Benefits
- **Consistency**: Matches your C++ coding style
- **Readability**: Clear visual separation of control structures
- **Maintainability**: Easier to read and modify code
- **Professional**: Industry-standard formatting for many C++ projects
+34
View File
@@ -0,0 +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
# 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
+130
View File
@@ -0,0 +1,130 @@
# kfmtbox installation script for Windows PowerShell
# Run with: powershell -ExecutionPolicy Bypass -File install.ps1
param(
[string]$Version = "latest"
)
# Configuration
$Repo = "koftwentytwo/kfmtbox"
$BinaryName = "kfmtbox.exe"
$InstallDir = "$env:USERPROFILE\AppData\Local\kfmtbox\bin"
# Colors for output
function Write-ColorOutput($ForegroundColor) {
$fc = $host.UI.RawUI.ForegroundColor
$host.UI.RawUI.ForegroundColor = $ForegroundColor
if ($args) {
Write-Output $args
} else {
$input | Write-Output
}
$host.UI.RawUI.ForegroundColor = $fc
}
# Detect architecture
function Get-Architecture {
if ([Environment]::Is64BitOperatingSystem) {
return "x86_64"
} else {
return "x86"
}
}
# Get latest release version
function Get-LatestVersion {
try {
$response = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest"
return $response.tag_name
} catch {
Write-ColorOutput Red "Failed to get latest version: $_"
exit 1
}
}
# Download and install binary
function Install-Binary {
param(
[string]$Version,
[string]$Architecture
)
$assetName = "kfmtbox-windows-$Architecture.exe"
$downloadUrl = "https://github.com/$Repo/releases/download/$Version/$assetName"
$tempFile = "$env:TEMP\$assetName"
Write-ColorOutput Blue "Downloading $assetName..."
try {
Invoke-WebRequest -Uri $downloadUrl -OutFile $tempFile
} catch {
Write-ColorOutput Red "Failed to download binary: $_"
exit 1
}
# Create install directory
if (!(Test-Path $InstallDir)) {
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
}
# Move binary to install directory
Move-Item -Path $tempFile -Destination "$InstallDir\$BinaryName" -Force
Write-ColorOutput Green "Installed $BinaryName to $InstallDir\$BinaryName"
}
# Add to PATH
function Add-ToPath {
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
if ($currentPath -notlike "*$InstallDir*") {
Write-ColorOutput Yellow "Adding $InstallDir to PATH..."
$newPath = "$currentPath;$InstallDir"
[Environment]::SetEnvironmentVariable("PATH", $newPath, "User")
Write-ColorOutput Green "Added to PATH. Please restart your terminal."
} else {
Write-ColorOutput Green "Already in PATH"
}
}
# Main installation function
function Main {
Write-ColorOutput Blue "Installing kfmtbox..."
# Detect architecture
$arch = Get-Architecture
Write-ColorOutput Blue "Detected architecture: $arch"
# Get version
if ($Version -eq "latest") {
$version = Get-LatestVersion
} else {
$version = $Version
}
Write-ColorOutput Blue "Version: $version"
# Install binary
Install-Binary -Version $version -Architecture $arch
# Add to PATH
Add-ToPath
Write-ColorOutput Green "Installation complete!"
Write-ColorOutput Blue "Try running: kfmtbox --help"
}
# Check if running as administrator
if (([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-ColorOutput Red "This script should not be run as administrator"
exit 1
}
# Check dependencies
try {
Invoke-WebRequest -Uri "https://github.com" -UseBasicParsing | Out-Null
} catch {
Write-ColorOutput Red "Internet connection required for installation"
exit 1
}
# Run main function
Main
Executable
+143
View File
@@ -0,0 +1,143 @@
#!/bin/bash
# kfmtbox installation script
# Supports Linux, macOS, and Windows (via WSL)
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
REPO="koftwentytwo/kfmtbox"
BINARY_NAME="kfmtbox"
INSTALL_DIR="${HOME}/.local/bin"
# Detect OS and architecture
detect_platform() {
local os arch
case "$(uname -s)" in
Linux*) os="linux" ;;
Darwin*) os="macos" ;;
CYGWIN*|MINGW*|MSYS*) os="windows" ;;
*) echo -e "${RED}Unsupported operating system${NC}"; exit 1 ;;
esac
case "$(uname -m)" in
x86_64|amd64) arch="x86_64" ;;
arm64|aarch64) arch="aarch64" ;;
*) echo -e "${RED}Unsupported architecture${NC}"; exit 1 ;;
esac
echo "${os}-${arch}"
}
# Get latest release version
get_latest_version() {
curl -s "https://api.github.com/repos/${REPO}/releases/latest" | \
grep '"tag_name":' | \
sed -E 's/.*"([^"]+)".*/\1/'
}
# Download and install binary
install_binary() {
local platform=$1
local version=$2
local asset_name
# Determine asset name based on platform
case "$platform" in
"linux-x86_64")
asset_name="kfmtbox-linux-x86_64"
;;
"macos-x86_64")
asset_name="kfmtbox-macos-x86_64"
;;
"macos-aarch64")
asset_name="kfmtbox-macos-aarch64"
;;
"windows-x86_64")
asset_name="kfmtbox-windows-x86_64.exe"
;;
*)
echo -e "${RED}Unsupported platform: ${platform}${NC}"
exit 1
;;
esac
local download_url="https://github.com/${REPO}/releases/download/${version}/${asset_name}"
local temp_file="/tmp/${asset_name}"
echo -e "${BLUE}Downloading ${asset_name}...${NC}"
curl -L -o "$temp_file" "$download_url"
# Create install directory if it doesn't exist
mkdir -p "$INSTALL_DIR"
# Make binary executable and move to install directory
chmod +x "$temp_file"
mv "$temp_file" "${INSTALL_DIR}/${BINARY_NAME}"
echo -e "${GREEN}Installed ${BINARY_NAME} to ${INSTALL_DIR}/${BINARY_NAME}${NC}"
}
# Add to PATH if needed
add_to_path() {
local shell_rc
case "$SHELL" in
*/zsh) shell_rc="${HOME}/.zshrc" ;;
*/bash) shell_rc="${HOME}/.bashrc" ;;
*) shell_rc="${HOME}/.profile" ;;
esac
if ! echo "$PATH" | grep -q "${INSTALL_DIR}"; then
echo -e "${YELLOW}Adding ${INSTALL_DIR} to PATH in ${shell_rc}${NC}"
echo "export PATH=\"${INSTALL_DIR}:\$PATH\"" >> "$shell_rc"
echo -e "${GREEN}Please restart your shell or run: source ${shell_rc}${NC}"
fi
}
# Main installation function
main() {
echo -e "${BLUE}Installing kfmtbox...${NC}"
# Detect platform
local platform
platform=$(detect_platform)
echo -e "${BLUE}Detected platform: ${platform}${NC}"
# Get latest version
local version
version=$(get_latest_version)
echo -e "${BLUE}Latest version: ${version}${NC}"
# Install binary
install_binary "$platform" "$version"
# Add to PATH
add_to_path
echo -e "${GREEN}Installation complete!${NC}"
echo -e "${BLUE}Try running: kfmtbox --help${NC}"
}
# Check if running as root
if [[ $EUID -eq 0 ]]; then
echo -e "${RED}This script should not be run as root${NC}"
exit 1
fi
# Check dependencies
if ! command -v curl &> /dev/null; then
echo -e "${RED}curl is required but not installed${NC}"
exit 1
fi
# Run main function
main "$@"
+66
View File
@@ -0,0 +1,66 @@
Project Name: kfmtbox
Purpose: A Rust-based command-line utility that wraps one or more lines of text in a “flower box” style comment, using the appropriate comment style for the file type.
⸻
Overview:
Create a CLI tool (kfmtbox) that:
• Detects the file type from extension or CLI argument
• Applies a “flower box” style comment block using the correct line comment syntax
• Reads from stdin, outputs to stdout
• Replaces existing flower box comments if already present
• Ensures consistent formatting in monospace terminal environments
• Is designed for use from text editors like neovim via hotkeys
⸻
Core Features:
1. Input/Output:
• Accepts input from stdin
• Sends formatted result to stdout
2. Comment Style Detection:
• Uses --filename or --filetype CLI flag
• Matches file extension to a known set of comment styles
• Falls back to a default style (#) if unknown
3. Comment Style Mapping:
• Hardcoded map of common file extensions and their line comment styles
• Supports user-defined YAML config at ~/.config/kfmtbox/comments.yaml
4. Line Formatting Rules:
• Max line length of 120 characters
• If the input line(s) exceed a certain length, wrap and rebalance them
• Wrapped lines must be as evenly sized as possible
• Flower box must always be a perfect rectangle
• Center content with padding between borders
5. Multiline Input:
• Treat multiple selected lines as one input block
• Format as a single, contiguous box
6. Existing Comment Detection:
• Detect if the input is already in a flower box
• If so, extract content and reformat with updated layout
7. Configuration:
• Comment styles can be defined in a YAML config file
• Config file contains mappings of filetypes to line comment prefixes
8. CLI Parameters:
• --filename or --filetype to specify source file context
• May include future flags for --max-width, --theme, etc.
9. Editor Integration:
• Designed for invocation via mapped hotkey in Neovim
• Minimal output dependencies (pure stdout)
10. Development Notes:
• Written in idiomatic Rust
• Separated modules for CLI parsing, box rendering, and wrapping
• CLI powered by clap
• Optional YAML parsing via serde_yaml
11. Testing and Usability:
• Unit tests for core logic
• Can be built as a binary and tested via piping from command line
⸻
Future Enhancements (Optional):
• Block comment support
• Theming and customizable border styles
• VSCode/JetBrains plugin integrations
• Auto-detection of filetype via stdin context
• Support for configurable max width or padding
+10
View File
@@ -0,0 +1,10 @@
# RustRover project configuration
[toolchain]
channel = "nightly"
components = ["rustfmt", "clippy"]
[clippy]
all-targets = true
all-features = true
warnings = ["clippy::all"]
allow = ["clippy::needless_return", "clippy::redundant_else"]
+58
View File
@@ -0,0 +1,58 @@
# Rustfmt configuration file for nightly Rust
# Based on your .clang-format preferences with Allman brace style
# This file should be used with nightly Rust toolchain
# Indentation - matches your IndentWidth: 3, TabWidth: 3, UseTab: Never
tab_spaces = 3
# Line length - matches your ColumnLimit: 100
max_width = 100
# Edition
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 # MaxEmptyLinesToKeep: 3
# String formatting
format_strings = true
format_code_in_doc_comments = true
# Misc
remove_nested_parens = true
use_try_shorthand = true
use_field_init_shorthand = true
force_explicit_abi = true
# This configuration enforces your C++ Allman brace style in Rust
+13
View File
@@ -0,0 +1,13 @@
# KofTwentyTwo rustfmt configuration (K22-CODE-RS). Stable-channel options only, so
# `cargo fmt --check` behaves the same on every toolchain.
edition = "2021"
# 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
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).
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# Format script for kfmtbox with Allman brace style
# This script uses nightly Rust to format code with Allman brace style
set -e
echo "🔧 Formatting code with Allman brace style..."
# Check if nightly Rust is installed
if ! /Users/james.maes/.cargo/bin/rustup show | grep -q "nightly"; then
echo "📦 Installing nightly Rust..."
/Users/james.maes/.cargo/bin/rustup install nightly
fi
# Use nightly rustfmt with Allman brace style configuration
echo "🎨 Running rustfmt with Allman brace style..."
/Users/james.maes/.cargo/bin/rustup run nightly cargo fmt
echo "✅ Formatting complete!"
echo ""
echo "📋 Style applied:"
echo " - 3-space indentation"
echo " - 100-character line limit"
echo " - Allman brace style (opening braces on new lines)"
echo " - Consistent spacing and formatting"
echo ""
echo "💡 Your RustIDE should now format with Allman brace style automatically!"
+105
View File
@@ -0,0 +1,105 @@
#!/bin/bash
# kfmtbox release script
# Usage: ./scripts/release.sh <version>
# Example: ./scripts/release.sh 0.1.0
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Check if version is provided
if [ $# -eq 0 ]; then
echo -e "${RED}Error: Version number required${NC}"
echo "Usage: $0 <version>"
echo "Example: $0 0.1.0"
exit 1
fi
VERSION=$1
TAG="v${VERSION}"
echo -e "${BLUE}Releasing kfmtbox version ${VERSION}${NC}"
# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo -e "${RED}Error: Not in a git repository${NC}"
exit 1
fi
# Check if working directory is clean
if ! git diff-index --quiet HEAD --; then
echo -e "${RED}Error: Working directory is not clean${NC}"
echo "Please commit or stash your changes before releasing"
exit 1
fi
# Check if tag already exists
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo -e "${RED}Error: Tag $TAG already exists${NC}"
exit 1
fi
# Update version in Cargo.toml
echo -e "${YELLOW}Updating version in Cargo.toml...${NC}"
sed -i.bak "s/^version = \".*\"/version = \"${VERSION}\"/" Cargo.toml
rm Cargo.toml.bak
# Update version in Homebrew formula
echo -e "${YELLOW}Updating version in Homebrew formula...${NC}"
sed -i.bak "s/url \"https:\/\/github\.com\/koftwentytwo\/kfmtbox\/archive\/v.*\.tar\.gz\"/url \"https:\/\/github\.com\/koftwentytwo\/kfmtbox\/archive\/v${VERSION}\.tar\.gz\"/" Formula/kfmtbox.rb
rm Formula/kfmtbox.rb.bak
# Update CHANGELOG.md (if it exists)
if [ -f "CHANGELOG.md" ]; then
echo -e "${YELLOW}Please update CHANGELOG.md with the new version${NC}"
echo "Press Enter when done..."
read -r
fi
# Run tests
echo -e "${YELLOW}Running tests...${NC}"
cargo test
# Run clippy
echo -e "${YELLOW}Running clippy...${NC}"
cargo clippy --all-targets --all-features
# Check formatting
echo -e "${YELLOW}Checking formatting...${NC}"
cargo fmt --check
# Build release
echo -e "${YELLOW}Building release...${NC}"
cargo build --release
# Commit changes
echo -e "${YELLOW}Committing changes...${NC}"
git add Cargo.toml Formula/kfmtbox.rb CHANGELOG.md
git commit -m "Release version ${VERSION}"
# Create and push tag
echo -e "${YELLOW}Creating and pushing tag...${NC}"
git tag -a "$TAG" -m "Release version ${VERSION}"
git push origin main
git push origin "$TAG"
echo -e "${GREEN}Release ${VERSION} created successfully!${NC}"
echo -e "${BLUE}Next steps:${NC}"
echo "1. Wait for GitHub Actions to build and create the release"
echo "2. Update package managers (Homebrew, AUR, etc.)"
echo "3. Announce the release on social media/forums"
# Optional: Open GitHub releases page
if command -v open >/dev/null 2>&1; then
echo -e "${YELLOW}Opening GitHub releases page...${NC}"
open "https://github.com/koftwentytwo/kfmtbox/releases"
elif command -v xdg-open >/dev/null 2>&1; then
echo -e "${YELLOW}Opening GitHub releases page...${NC}"
xdg-open "https://github.com/koftwentytwo/kfmtbox/releases"
fi
+128
View File
@@ -0,0 +1,128 @@
use crate::{comment_detector::CommentStyle, text_wrapper::TextWrapper};
pub struct BoxRenderer {
comment_style: CommentStyle,
text_wrapper: TextWrapper,
}
impl BoxRenderer {
#[must_use]
pub const fn new(comment_style: CommentStyle, max_width: usize) -> Self {
#[allow(clippy::needless_return)]
return Self {
comment_style,
text_wrapper: TextWrapper::new(max_width),
};
}
#[must_use]
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);
// Wrap and balance the text
let wrapped_lines = self.text_wrapper.wrap_text(&clean_content);
let balanced_lines = self.text_wrapper.balance_lines(wrapped_lines);
// 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);
// 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} ╭{border}╮"));
// Content lines
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} ╰{border}╯"));
return result.join("\n");
}
/// 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();
let (Some(first), Some(last)) = (lines.first(), lines.last()) else {
return content.to_string();
};
if lines.len() < 3 {
return content.to_string();
}
// 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();
// 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
};
// 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(" ");
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::comment_detector::CommentStyle;
/// # Panics
///
/// Panics if an assertion fails.
#[test]
fn test_render_flower_box() {
let comment_style = CommentStyle::new("//".to_string());
let renderer = BoxRenderer::new(comment_style, 80);
let content = "Hello, world!";
let result = renderer.render_flower_box(content);
assert!(result.contains("╭"));
assert!(result.contains("╮"));
assert!(result.contains("╰"));
assert!(result.contains("╯"));
assert!(result.contains("Hello, world!"));
}
/// # Panics
///
/// Panics if an assertion fails.
#[test]
fn test_extract_content_from_box() {
let comment_style = CommentStyle::new("//".to_string());
let renderer = BoxRenderer::new(comment_style, 80);
let boxed_content = "// ╭─────────────╮\n// │ Hello, world! │\n// ╰─────────────╯";
let extracted = renderer.extract_content_from_box(boxed_content);
assert_eq!(extracted, "Hello, world!");
}
}
+23
View File
@@ -0,0 +1,23 @@
use clap::Parser;
#[derive(Parser, Debug)]
#[command(name = "kfmtbox")]
#[command(about = "A command-line utility that wraps text in flower box style comments")]
#[command(version)]
pub struct Cli {
/// Input filename for file type detection
#[arg(short = 'f', long = "filename")]
pub filename: Option<String>,
/// File type for comment style detection
#[arg(short = 't', long = "filetype")]
pub filetype: Option<String>,
/// Maximum line width (default: 120)
#[arg(short = 'w', long = "max-width", default_value = "120")]
pub max_width: usize,
/// Verbose mode to show operations and decisions
#[arg(short = 'v', long = "verbose")]
pub verbose: bool,
}
+93
View File
@@ -0,0 +1,93 @@
use std::collections::HashMap;
use crate::config::Config;
#[derive(Debug, Clone)]
pub struct CommentStyle {
pub prefix: String,
pub is_block: bool,
}
impl CommentStyle {
#[must_use]
pub const fn new(prefix: String) -> Self {
return Self { prefix, is_block: false };
}
}
/// 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 {
#[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();
return Self {
default_style: CommentStyle::new("#".to_string()),
style_map,
};
}
#[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);
}
#[must_use]
pub fn detect_from_filetype(&self, filetype: &str) -> &CommentStyle {
let filetype = filetype.to_lowercase();
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()));
}
}
}
impl Default for CommentDetector {
fn default() -> Self {
return Self::new();
}
}
+60
View File
@@ -0,0 +1,60 @@
use std::{collections::HashMap, path::PathBuf};
use dirs;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub comment_styles: HashMap<String, String>,
}
impl Default for Config {
fn default() -> Self {
return Self { comment_styles: HashMap::new() };
}
}
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() {
let content = std::fs::read_to_string(&config_path)?;
let config: Self = serde_yaml::from_str(&content)?;
return Ok(config);
} else {
return Ok(Self::default());
}
}
#[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");
return config_dir;
}
/// 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() {
std::fs::create_dir_all(parent)?;
}
let content = serde_yaml::to_string(self)?;
std::fs::write(&config_path, content)?;
return Ok(());
}
}
+12
View File
@@ -0,0 +1,12 @@
pub mod box_renderer;
pub mod cli;
pub mod comment_detector;
pub mod config;
pub mod text_wrapper;
pub mod verbose;
pub use box_renderer::BoxRenderer;
pub use cli::Cli;
pub use comment_detector::CommentDetector;
pub use config::Config;
pub use text_wrapper::TextWrapper;
+66
View File
@@ -0,0 +1,66 @@
use std::io::{self, Read};
use clap::Parser;
use kfmtbox::verbose::{set_verbose, verbose_msg};
use kfmtbox::{BoxRenderer, Cli, CommentDetector, Config};
/// Main entry point for the kfmtbox application.
///
/// Reads text from stdin, formats it as a flower box comment, and outputs to stdout.
/// The comment style is determined by the file type specified via command line arguments.
///
/// # Errors
///
/// This function will return an error if:
/// - 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>> {
let cli = Cli::parse();
set_verbose(cli.verbose);
// Load configuration
verbose_msg("Loading Config");
let config = Config::load().unwrap_or_else(|_| return Config::default());
// Initialize comment detector
verbose_msg("Detecting Comment Type");
let mut detector = CommentDetector::new();
detector.load_config(&config);
// Determine comment style
let comment_style = if let Some(filename) = &cli.filename {
detector.detect_from_filename(filename).clone()
} else if let Some(filetype) = &cli.filetype {
detector.detect_from_filetype(filetype).clone()
} else {
detector.default_style.clone()
};
// Read input from stdin
verbose_msg("Reading Comment from STDIN");
let mut input = String::new();
io::stdin().read_to_string(&mut input)?;
// 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");
if input.is_empty() {
verbose_msg("Input was empty - returning nothing successfully");
return Ok(());
}
// Create box renderer and render the flower box
verbose_msg("Rendering comment in new flower box");
let renderer = BoxRenderer::new(comment_style, cli.max_width);
let result = renderer.render_flower_box(&input);
// Output to stdout
verbose_msg("Outputting rendered comment");
println!("{result}");
return Ok(());
}
+119
View File
@@ -0,0 +1,119 @@
pub struct TextWrapper {
max_width: usize,
}
impl TextWrapper {
#[must_use]
pub const fn new(max_width: usize) -> Self {
return Self { max_width };
}
#[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 {
let words: Vec<&str> = line.split_whitespace().collect();
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}") };
// Use a smaller effective width for better wrapping
let effective_width = 20; // Force wrapping at 20 characters
if test_line.len() <= effective_width {
current_line = test_line;
} else {
if !current_line.is_empty() {
all_lines.push(current_line);
}
current_line = word.to_string();
}
}
if !current_line.is_empty() {
all_lines.push(current_line);
}
}
if all_lines.is_empty() {
return vec![String::new()];
}
return all_lines;
}
#[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| 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() {
current_line = line;
} 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 {
balanced.push(current_line);
current_line = line;
}
}
}
if !current_line.is_empty() {
balanced.push(current_line);
}
return balanced;
}
}
#[cfg(test)]
mod tests {
use super::*;
/// # Panics
///
/// Panics if an assertion fails.
#[test]
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 {
assert!(line.len() <= 20);
}
}
/// # Panics
///
/// Panics if an assertion fails.
#[test]
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 result = wrapper.balance_lines(lines);
assert_eq!(result.len(), 3);
}
}
+30
View File
@@ -0,0 +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) {
IS_VERBOSE.store(on, Ordering::Relaxed);
}
// read from anywhere
pub fn is_verbose() -> bool {
return IS_VERBOSE.load(Ordering::Relaxed);
}
pub fn verbose_msg(msg: &str) {
if !is_verbose() {
return;
}
println!("[VERBOSE] {msg}");
}
pub fn verbose_value<T: std::fmt::Debug>(label: &str, val: T) {
if !is_verbose() {
return;
}
println!("[VERBOSE] {label} = {val:?}");
}