diff --git a/.config/.markdownlint.yml b/.config/.markdownlint.yml new file mode 100755 index 0000000..d318928 --- /dev/null +++ b/.config/.markdownlint.yml @@ -0,0 +1,11 @@ +line-length: + code_blocks: false + line_length: 120 + strict: true + tables: false +no-duplicate-heading: false +no-inline-html: + allowed_elements: + - span +single-title: + front_matter_title: "" diff --git a/.config/.yamlfmt b/.config/.yamlfmt new file mode 100644 index 0000000..d023c60 --- /dev/null +++ b/.config/.yamlfmt @@ -0,0 +1,13 @@ +exclude: + - .cargo + - .mise + - .mise-data + - coverage + - target + - tmp +formatter: + eof_newline: true + line_ending: lf + retain_line_breaks_single: true + trim_trailing_whitespace: true + type: basic diff --git a/.config/editorconfig-checker.json b/.config/editorconfig-checker.json new file mode 100755 index 0000000..c9361ff --- /dev/null +++ b/.config/editorconfig-checker.json @@ -0,0 +1,11 @@ +{ + "Exclude": [ + ".cargo", + ".idea", + ".mise", + ".mise-data", + "coverage", + "target", + "tmp" + ] +} diff --git a/.config/mise.toml b/.config/mise.toml new file mode 100644 index 0000000..79deeb6 --- /dev/null +++ b/.config/mise.toml @@ -0,0 +1,32 @@ +[task_config] +includes = ["tasks"] + +[tasks.format] +description = "Format all files" +alias = ["fmt"] +depends = ["format:**"] + +[tasks.lint] +description = "Lint all files" +alias = ["l"] +depends = ["lint:**"] + +[tools] +"cargo:cargo-audit" = "latest" +"cargo:cargo-llvm-cov" = "latest" +"cargo:cargo-nextest" = "latest" +"cargo:cargo-outdated" = "latest" +"cargo:cargo-sort" = "latest" +editorconfig-checker = "latest" +markdownlint-cli2 = "latest" +taplo = "latest" +usage = "latest" +yamlfmt = "latest" + +[[tools.rust]] +components = "clippy" +version = "latest" + +[[tools.rust]] +components = "rustfmt" +version = "nightly" diff --git a/rustfmt.toml b/.config/rustfmt.toml old mode 100644 new mode 100755 similarity index 72% rename from rustfmt.toml rename to .config/rustfmt.toml index 0a68d0b..fea3b35 --- a/rustfmt.toml +++ b/.config/rustfmt.toml @@ -3,6 +3,7 @@ group_imports = "StdExternalCrate" hard_tabs = false imports_granularity = "Crate" max_width = 120 +reorder_impl_items = true reorder_imports = true +struct_lit_single_line = false tab_spaces = 2 -use_small_heuristics = "Max" diff --git a/.config/taplo.toml b/.config/taplo.toml new file mode 100755 index 0000000..58eb364 --- /dev/null +++ b/.config/taplo.toml @@ -0,0 +1,10 @@ +exclude = [ + ".cargo/**", + ".git/**", + ".jj/**", + ".mise/**", + ".mise-data/**", + "coverage/**", + "target/**", +] +include = ["**/*.toml"] diff --git a/.config/tomlfmt.toml b/.config/tomlfmt.toml new file mode 100755 index 0000000..9ae29c4 --- /dev/null +++ b/.config/tomlfmt.toml @@ -0,0 +1 @@ +indent_count = 2 diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 120000 index 0000000..cb27c86 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +../docs/policy/code-of-conduct.md \ No newline at end of file diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 120000 index 0000000..15c1026 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1 @@ +../docs/dev/README.md \ No newline at end of file diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 120000 index 0000000..ff0f2cd --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1 @@ +../docs/policy/security.md \ No newline at end of file diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 5820133..28d36e0 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,9 +1,11 @@ name: Shared Setup +description: Install mise, Rust toolchain, and development dependencies runs: using: "composite" steps: - uses: jdx/mise-action@v3 - uses: Swatinem/rust-cache@v2 - - name: Setup - shell: sh - run: mise run setup + - shell: bash + run: |- + rustup component add --toolchain nightly-x86_64-unknown-linux-gnu rustfmt + rustup component add --toolchain nightly-x86_64-unknown-linux-gnu clippy diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b9750ae..37544a5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,17 +1,16 @@ name: Build - permissions: contents: read pull-requests: write - on: push: branches: - main + tags: + - "[0-9]+.*" pull_request: branches: - main - jobs: lint: runs-on: ubuntu-latest @@ -20,23 +19,22 @@ jobs: - uses: ./.github/actions/setup - name: Lint run: mise run lint - + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: ./.github/actions/setup + - name: Check + run: mise run check test: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup - - name: Test - run: mise run test:coverage - + - name: Unit Test + run: mise run test package: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: ./.github/actions/setup diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..70c5329 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,68 @@ +name: Release +permissions: + actions: write + contents: write +on: + push: + tags: + - "[0-9]+.*" +jobs: + wait-for-build: + runs-on: ubuntu-latest + steps: + - name: Wait for Build workflow + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + sha="${{ github.sha }}" + echo "Waiting for Build workflow to complete on $sha..." + while true; do + result=$(gh api "repos/${{ github.repository }}/actions/workflows/build.yml/runs" \ + --jq ".workflow_runs[] | select(.head_sha == \"$sha\") | .conclusion" | head -1) + case "$result" in + success) echo "Build passed"; exit 0 ;; + failure|cancelled|timed_out) echo "Build $result"; exit 1 ;; + *) echo "Build still running..."; sleep 30 ;; + esac + done + create-release: + needs: wait-for-build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + draft: true + title: v$tag + token: ${{ secrets.GITHUB_TOKEN }} + publish-crate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + - name: Publish to crates.io + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish + publish-release: + needs: + - publish-crate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Publish release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release edit "${{ github.ref_name }}" --draft=false + notify-discord: + needs: publish-release + runs-on: ubuntu-latest + steps: + - name: Post to Discord + env: + DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} + run: |- + curl -H "Content-Type: application/json" \ + -d "{\"content\": \"**dir_spec v${{ github.ref_name }}** has been released! https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}\"}" \ + "$DISCORD_WEBHOOK_URL" diff --git a/.markdownlint.yml b/.markdownlint.yml deleted file mode 100644 index 5f019f3..0000000 --- a/.markdownlint.yml +++ /dev/null @@ -1,8 +0,0 @@ -line-length: - code_blocks: false - line_length: 120 - strict: true - tables: false - -no-duplicate-heading: - siblings_only: true diff --git a/.markdownlintignore b/.markdownlintignore deleted file mode 100644 index b83d222..0000000 --- a/.markdownlintignore +++ /dev/null @@ -1 +0,0 @@ -/target/ diff --git a/.yamllint.yml b/.yamllint.yml deleted file mode 100644 index c70dbdb..0000000 --- a/.yamllint.yml +++ /dev/null @@ -1,3 +0,0 @@ -document-start: disable -line-length: - max: 120 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c59678..4872767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,7 +101,6 @@ The format is based on [Keep a Changelog], and this project adheres to [Break Ve -[Unreleased]: https://github.com/aaronmallen/dir_spec/compare/0.5.0...HEAD [0.5.0]: https://github.com/aaronmallen/dir_spec/compare/0.4.0...0.5.0 [0.4.0]: https://github.com/aaronmallen/dir_spec/compare/0.3.0...0.4.0 [0.3.0]: https://github.com/aaronmallen/dir_spec/compare/0.2.0...0.3.0 diff --git a/Cargo.lock b/Cargo.lock index 486bf86..9e4b22e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,23 +2,17 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - [[package]] name = "bitflags" -version = "2.9.3" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "dir_spec" @@ -29,25 +23,24 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.175" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -55,22 +48,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-link", ] [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] @@ -97,65 +90,7 @@ dependencies = [ ] [[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" diff --git a/LLMS.md b/LLMS.md deleted file mode 100644 index 997fd6e..0000000 --- a/LLMS.md +++ /dev/null @@ -1,62 +0,0 @@ -# LLMs - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -`dir_spec` is a cross-platform Rust library for resolving XDG and platform-specific directories. It prioritizes XDG -compliance across all platforms while providing sensible platform-specific fallbacks. - -## Development Commands - -All development tasks are managed through `mise` (formerly rtx): - -```bash -# Build the project -mise run build # or: mise run b - -# Run linting and formatting checks -mise run lint # or: mise run l - -# Run tests -mise run test:unit # Unit tests with cargo nextest -mise run test:coverage # Tests with code coverage - -# Security audit -mise run audit - -# Clean build artifacts -mise run clean - -# Project setup -mise run setup -``` - -## Architecture - -The library is organized with platform-specific modules: - -- `src/lib.rs` - Main library entry point and public API -- `src/xdg.rs` - XDG Base Directory Specification implementation -- `src/linux.rs` - Linux-specific directory resolution -- `src/macos.rs` - macOS-specific directory resolution -- `src/windows.rs` - Windows-specific directory resolution - -Each platform module implements directory resolution logic that: - -1. First checks for XDG environment variables -2. Falls back to platform-specific defaults if XDG vars aren't set - -## Testing Strategy - -- Tests are embedded in source files using `#[cfg(test)]` modules -- Uses `cargo nextest` for faster test execution -- Each platform module contains platform-specific tests -- Tests verify both XDG compliance and platform-specific fallbacks - -## Key Development Notes - -- **Zero dependencies** - Only uses Rust's standard library -- **Clippy pedantic** - Strict linting is enforced via `.clippy.toml` -- **Platform compatibility** - Currently supports Linux and macOS (Windows support temporarily removed from CI) -- **Type safety** - All directory methods return `Option` diff --git a/README.md b/README.md index d527e42..3db00b8 100644 --- a/README.md +++ b/README.md @@ -54,27 +54,27 @@ fn main() { ## Supported Directories -| Method | XDG Variable | Linux Default | macOS Default | Windows Default | -|-------------------|------------------------|----------------------|----------------------------------|-----------------------------| -| `bin_home()` | `XDG_BIN_HOME` | `~/.local/bin` | `~/.local/bin` | `%LOCALAPPDATA%\Programs` | -| `cache_home()` | `XDG_CACHE_HOME` | `~/.cache` | `~/Library/Caches` | `%LOCALAPPDATA%` | -| `config_home()` | `XDG_CONFIG_HOME` | `~/.config` | `~/Library/Application Support` | `%APPDATA%` | -| `config_local()` | — | `~/.config`¹ | `~/Library/Application Support`¹ | `%LOCALAPPDATA%` | -| `data_home()` | `XDG_DATA_HOME` | `~/.local/share` | `~/Library/Application Support` | `%APPDATA%` | -| `data_local()` | — | `~/.local/share`¹ | `~/Library/Application Support`¹ | `%LOCALAPPDATA%` | -| `desktop()` | `XDG_DESKTOP_DIR` | `~/Desktop` | `~/Desktop` | `%USERPROFILE%\Desktop` | -| `documents()` | `XDG_DOCUMENTS_DIR` | `~/Documents` | `~/Documents` | `%USERPROFILE%\Documents` | -| `downloads()` | `XDG_DOWNLOAD_DIR` | `~/Downloads` | `~/Downloads` | `%USERPROFILE%\Downloads` | -| `fonts()` | — | `~/.local/share/fonts` | `~/Library/Fonts` | `None`² | -| `home()` | `HOME` / `USERPROFILE` | `$HOME` | `$HOME` | `%USERPROFILE%` | -| `music()` | `XDG_MUSIC_DIR` | `~/Music` | `~/Music` | `%USERPROFILE%\Music` | -| `pictures()` | `XDG_PICTURES_DIR` | `~/Pictures` | `~/Pictures` | `%USERPROFILE%\Pictures` | -| `preferences()` | — | `~/.config`¹ | `~/Library/Preferences` | `%APPDATA%`¹ | -| `publicshare()` | `XDG_PUBLICSHARE_DIR` | `~/Public` | `~/Public` | `C:\Users\Public` | -| `runtime()` | `XDG_RUNTIME_DIR` | `$TMPDIR` or `/tmp` | `$TMPDIR` or `/tmp` | `%TEMP%` | -| `state_home()` | `XDG_STATE_HOME` | `~/.local/state` | `~/Library/Application Support` | `%LOCALAPPDATA%` | -| `templates()` | `XDG_TEMPLATES_DIR` | `~/Templates` | `~/Templates` | `%USERPROFILE%\Templates` | -| `videos()` | `XDG_VIDEOS_DIR` | `~/Videos` | `~/Movies` | `%USERPROFILE%\Videos` | +| Method | XDG Variable | Linux Default | macOS Default | Windows Default | +|------------------|------------------------|------------------------|----------------------------------|---------------------------| +| `bin_home()` | `XDG_BIN_HOME` | `~/.local/bin` | `~/.local/bin` | `%LOCALAPPDATA%\Programs` | +| `cache_home()` | `XDG_CACHE_HOME` | `~/.cache` | `~/Library/Caches` | `%LOCALAPPDATA%` | +| `config_home()` | `XDG_CONFIG_HOME` | `~/.config` | `~/Library/Application Support` | `%APPDATA%` | +| `config_local()` | — | `~/.config`¹ | `~/Library/Application Support`¹ | `%LOCALAPPDATA%` | +| `data_home()` | `XDG_DATA_HOME` | `~/.local/share` | `~/Library/Application Support` | `%APPDATA%` | +| `data_local()` | — | `~/.local/share`¹ | `~/Library/Application Support`¹ | `%LOCALAPPDATA%` | +| `desktop()` | `XDG_DESKTOP_DIR` | `~/Desktop` | `~/Desktop` | `%USERPROFILE%\Desktop` | +| `documents()` | `XDG_DOCUMENTS_DIR` | `~/Documents` | `~/Documents` | `%USERPROFILE%\Documents` | +| `downloads()` | `XDG_DOWNLOAD_DIR` | `~/Downloads` | `~/Downloads` | `%USERPROFILE%\Downloads` | +| `fonts()` | — | `~/.local/share/fonts` | `~/Library/Fonts` | `None`² | +| `home()` | `HOME` / `USERPROFILE` | `$HOME` | `$HOME` | `%USERPROFILE%` | +| `music()` | `XDG_MUSIC_DIR` | `~/Music` | `~/Music` | `%USERPROFILE%\Music` | +| `pictures()` | `XDG_PICTURES_DIR` | `~/Pictures` | `~/Pictures` | `%USERPROFILE%\Pictures` | +| `preferences()` | — | `~/.config`¹ | `~/Library/Preferences` | `%APPDATA%`¹ | +| `publicshare()` | `XDG_PUBLICSHARE_DIR` | `~/Public` | `~/Public` | `C:\Users\Public` | +| `runtime()` | `XDG_RUNTIME_DIR` | `$TMPDIR` or `/tmp` | `$TMPDIR` or `/tmp` | `%TEMP%` | +| `state_home()` | `XDG_STATE_HOME` | `~/.local/state` | `~/Library/Application Support` | `%LOCALAPPDATA%` | +| `templates()` | `XDG_TEMPLATES_DIR` | `~/Templates` | `~/Templates` | `%USERPROFILE%\Templates` | +| `videos()` | `XDG_VIDEOS_DIR` | `~/Videos` | `~/Movies` | `%USERPROFILE%\Videos` | **Notes:** diff --git a/bin/audit b/bin/audit deleted file mode 100755 index 6c72ed9..0000000 --- a/bin/audit +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh -#MISE description="check dependencies for updates and security vulnerabilities" - -cargo audit -cargo outdated diff --git a/bin/build b/bin/build deleted file mode 100755 index ba60fa8..0000000 --- a/bin/build +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh -#MISE description="build the project" -#MISE alias=["b"] - -cargo build --all --all-features diff --git a/bin/ci b/bin/ci deleted file mode 100755 index f2243a0..0000000 --- a/bin/ci +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env sh -#MISE description="run all CI checks" - -use_color() { - [ "${FORCE_COLOR:-}" = "1" ] && return 0 - [ -t 1 ] || return 1 - [ "${TERM:-}" = "dumb" ] && return 1 - [ -n "${NO_COLOR:-}" ] && return 1 - return 0 -} - -echo_step() { - if use_color; then - printf "\n\e[38;2;72;192;189m==>\e[0m \e[38;2;255;255;255;1m%s\e[0m\n\n" "$1" - else - printf "\n==> %s\n\n" "$1" - fi -} - -echo_step "✨ Lint" && bin/lint/_default -echo_step "🧪 Test" && bin/test/coverage -echo_step "👷‍♂️ Build" && bin/build diff --git a/bin/clean b/bin/clean deleted file mode 100755 index 6bc15e7..0000000 --- a/bin/clean +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh -#MISE description="clean build artifacts" - -rm -rf coverage -cargo clean diff --git a/bin/lint/_default b/bin/lint/_default deleted file mode 100755 index 0ddbbc9..0000000 --- a/bin/lint/_default +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env sh -#MISE description="run formatting and lint checks" -#MISE alias=["l"] - -bin/lint/check/markdown && -bin/lint/check/yaml && -bin/lint/check/toml && -bin/lint/check/shell && -bin/lint/check/rust - -if command -v editorconfig-checker >/dev/null 2>&1; then - editorconfig-checker -fi diff --git a/bin/lint/check/markdown b/bin/lint/check/markdown deleted file mode 100755 index 875558c..0000000 --- a/bin/lint/check/markdown +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -#MISE description="lint markdown files" -#MISE alias=["l:c:md", "l:c:markdown", "l:check:md", "lint:c:markdown", "lint:c:md", "lint:check:md"] - -if command -v markdownlint >/dev/null 2>&1; then - markdownlint . -fi diff --git a/bin/lint/check/rust b/bin/lint/check/rust deleted file mode 100755 index 6bc0263..0000000 --- a/bin/lint/check/rust +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -#MISE description="lint rust files" -#MISE alias=["l:c:rs", "l:c:rust", "l:check:rs", "lint:c:rust", "lint:c:rs", "lint:check:rs"] - -cargo sort -w --check -cargo +nightly fmt --all --check -cargo clippy --all-targets diff --git a/bin/lint/check/shell b/bin/lint/check/shell deleted file mode 100755 index dfc9327..0000000 --- a/bin/lint/check/shell +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env sh -#MISE description="lint shell files" -#MISE alias=["l:c:sh", "l:c:shell", "l:check:sh", "lint:c:shell", "lint:c:sh", "lint:check:sh"] - -if command -v shellcheck >/dev/null 2>&1; then - find bin/ -type f -print0 | \ - xargs -0 grep -l '^#!/.*sh' 2>/dev/null | \ - while IFS= read -r file; do - shellcheck "$file" - done -fi diff --git a/bin/lint/check/toml b/bin/lint/check/toml deleted file mode 100755 index 391e63f..0000000 --- a/bin/lint/check/toml +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -#MISE description="lint yaml files" -#MISE alias=["l:c:toml", "lint:c:toml", "lint:check:toml"] - -if command -v taplo >/dev/null 2>&1; then - taplo lint -fi diff --git a/bin/lint/check/yaml b/bin/lint/check/yaml deleted file mode 100755 index f334891..0000000 --- a/bin/lint/check/yaml +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -#MISE description="lint yaml files" -#MISE alias=["l:c:yml", "l:c:yaml", "l:check:yml", "lint:c:yaml", "lint:c:yml", "lint:check:yaml"] - -if command -v yamllint >/dev/null 2>&1; then - yamllint . -fi diff --git a/bin/lint/fix/_default b/bin/lint/fix/_default deleted file mode 100755 index 9be5d02..0000000 --- a/bin/lint/fix/_default +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env sh -#MISE description="run formatting and lint checks with auto-correction" -#MISE alias=["l:f", "l:fix", "lint:f"] - -bin/lint/fix/markdown && -bin/lint/check/yaml && -bin/lint/fix/toml && -bin/lint/check/shell && -bin/lint/fix/rust - -if command -v editorconfig-checker >/dev/null 2>&1; then - editorconfig-checker -fi diff --git a/bin/lint/fix/markdown b/bin/lint/fix/markdown deleted file mode 100755 index 67a0f88..0000000 --- a/bin/lint/fix/markdown +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -#MISE description="lint markdown files with auto-correction" -#MISE alias=["l:f:md", "l:f:markdown", "l:fix:md", "lint:f:markdown", "lint:f:md", "lint:fix:md"] - -if command -v markdownlint >/dev/null 2>&1; then - markdownlint . --fix -fi diff --git a/bin/lint/fix/rust b/bin/lint/fix/rust deleted file mode 100755 index 1b3b596..0000000 --- a/bin/lint/fix/rust +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -#MISE description="lint rust files with auto-correction" -#MISE alias=["l:f:rs", "l:f:rust", "l:fix:rs", "lint:f:rust", "lint:f:rs", "lint:fix:rs"] - -cargo sort -w -cargo +nightly fmt --all -cargo clippy --all-targets --fix --allow-dirty --allow-staged diff --git a/bin/lint/fix/toml b/bin/lint/fix/toml deleted file mode 100755 index 1a97b69..0000000 --- a/bin/lint/fix/toml +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -#MISE description="lint yaml files" -#MISE alias=["l:f:toml", "lint:f:toml", "lint:fix:toml"] - -if command -v taplo >/dev/null 2>&1; then - taplo format -fi diff --git a/bin/setup b/bin/setup deleted file mode 100755 index 7c53707..0000000 --- a/bin/setup +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env sh -#MISE description="install dependencies and setup the dev environment" - -if command -v mise >/dev/null 2>&1; then - mise install -fi - -rustup component add --toolchain nightly-x86_64-unknown-linux-gnu rustfmt -rustup component add --toolchain nightly-x86_64-unknown-linux-gnu clippy -cargo install cargo-audit cargo-llvm-cov cargo-nextest cargo-outdated cargo-sort cargo-workspaces diff --git a/bin/test/_default b/bin/test/_default deleted file mode 100755 index b6781d5..0000000 --- a/bin/test/_default +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh -#MISE description="run unit tests" -#MISE alias=["t"] - -bin/test/unit diff --git a/bin/test/coverage b/bin/test/coverage deleted file mode 100755 index 6d28949..0000000 --- a/bin/test/coverage +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -#MISE description="run unit tests with test coverage" -#MISE alias=["t:c"] - -cargo llvm-cov nextest --branch --summary-only -cargo llvm-cov report --branch --html --output-dir coverage -cargo llvm-cov report --branch --lcov --output-path coverage/lcov.info diff --git a/bin/test/unit b/bin/test/unit deleted file mode 100755 index 90e6f40..0000000 --- a/bin/test/unit +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -#MISE description="run unit tests" - -cargo nextest run diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md deleted file mode 100644 index cef92f6..0000000 --- a/docs/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,132 +0,0 @@ -# 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, caste, color, 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: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -- Focusing on what is best not just for us as individuals, but for the overall - community - -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 email 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 by mentioning -[@aaronmallen](https://github.com/aaronmallen). -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.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md deleted file mode 100644 index 71e90c3..0000000 --- a/docs/CONTRIBUTING.md +++ /dev/null @@ -1,311 +0,0 @@ -# Contributing to dir_spec - -Thank you for your interest in contributing to dir_spec! This guide will help you get set up and understand our -development workflow. - -## Quick Start - -### Prerequisites - -- **Rust**: Install via [rustup](https://rustup.rs/) -- **Git**: For version control - -### Development Setup (Recommended: mise) - -We **strongly recommend** using [mise](https://mise.jdx.dev/) for tool management as it automatically handles all -dependencies and toolchain setup: - -1. **Clone the repository** - - ```bash - git clone https://github.com/aaronmallen/dir_spec.git - cd dir_spec - ``` - -2. **Install mise** - - ```bash - curl https://mise.jdx.dev/install.sh | sh - ``` - -3. **Install tools and dependencies** - - ```bash - mise install - mise run setup - ``` - -4. **Verify setup** - - ```bash - mise run test - mise run lint:check:rust - ``` - -### Alternative: Manual Setup - -If you prefer not to use mise, you can use our setup script: - -1. **Install Rust toolchain** - - ```bash - # The project will automatically use the toolchain specified in rust-toolchain.toml - rustup component add clippy rustfmt - ``` - -2. **Install development tools** - - ```bash - ./bin/setup - ``` - -3. **Verify setup** - - ```bash - ./bin/test/_default - ./bin/lint/check/rust - ``` - -## Development Workflow - -### Running Tests - -```bash -# Recommended: Use mise -mise run test - -# Without mise: Use bin scripts -./bin/test -``` - -### Code Formatting and Linting - -We use several tools to maintain code quality: - -```bash -# Recommended: Use mise -mise run lint:check:rust # Check formatting, imports, and clippy -mise run lint:fix:rust # Auto-fix formatting and imports -mise run lint:check:md # Check markdown formatting -mise run lint:fix:md # Auto-fix markdown formatting - -# Without mise: Use bin scripts -./bin/lint/check/rust # Check formatting, imports, and clippy -./bin/lint/fix/rust # Auto-fix formatting and imports -./bin/lint/check/markdown # Check markdown formatting -./bin/lint/fix/markdown # Auto-fix markdown formatting - -# Run all checks at once -./bin/lint/_default # Check everything -./bin/lint/fix/_default # Fix everything -``` - -### Running All CI Checks - -To run the same checks that CI runs: - -```bash -# Recommended: Use mise -mise run ci - -# Without mise: Use bin scripts -./bin/ci -``` - -### Other Useful Commands - -```bash -# Build the project -./bin/build - -# Check for dependency updates and security issues -./bin/audit - -# Clean build artifacts -./bin/clean -``` - -**Our linting includes:** - -- **cargo-sort**: Ensures `Cargo.toml` dependencies are sorted -- **rustfmt**: Code formatting (configured in `rustfmt.toml`) -- **clippy**: Lint checks for common mistakes and improvements -- **markdownlint**: Markdown file linting - -### Code Style - -- **Formatting**: We use `rustfmt` with custom settings (see `rustfmt.toml`) -- **Imports**: Sorted and grouped by `StdExternalCrate` -- **Line length**: 120 characters max -- **Indentation**: 2 spaces (no tabs) - -### Making Changes - -#### Create a feature branch - -```bash -git checkout -b feature/your-feature-name -``` - -#### *Make your changes - -- Add tests for new functionality -- Update documentation as needed -- Follow existing code patterns - -#### Test your changes - -```bash -# Recommended: Use mise -mise run test -mise run lint:check:rust - -# Without mise: Use bin scripts -./bin/test -./bin/lint/check/rust -``` - -#### Commit your changes - -```bash -git add . -git commit -m "feat: add support for XYZ" -``` - -#### Push and create a PR - -```bash -git push origin feature/your-feature-name -``` - -## Testing Guidelines - -### Platform Coverage - -This crate supports Linux, macOS, and Windows. When adding new features: - -- **Test on your platform** during development -- **Consider all platforms** in your implementation -- **Use `#[cfg(target_os = "...")]`** for platform-specific code -- **CI will test** on all platforms automatically - -### Test Categories - -- **Unit tests**: Test individual functions and edge cases -- **Integration tests**: Test the full API surface -- **Platform-specific tests**: Verify platform conventions are followed - -### Writing Tests - -```rust -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_config_home_xdg_override() { - // Test XDG environment variable override - } - - #[test] - #[cfg(target_os = "linux")] - fn test_config_home_linux_default() { - // Test Linux-specific defaults - } -} -``` - -## XDG Compliance - -This crate prioritizes XDG compliance across all platforms: - -### Key Principles - -1. **XDG variables take precedence** on all platforms -2. **Platform conventions** are used as fallbacks -3. **Consistent behavior** across operating systems -4. **Proper error handling** for missing directories - -### Adding New Directories - -When adding support for new directories: - -1. **Research platform conventions** - - - Linux: Follow XDG Base Directory Specification - - macOS: Use native `~/Library/*` locations - - Windows: Use appropriate `%APPDATA%` or `%LOCALAPPDATA%` paths - -2. **Implement XDG-first logic** - - ```rust - pub fn new_directory() -> Result { - if let Ok(xdg_var) = env::var("XDG_NEW_HOME") { - return Ok(PathBuf::from(xdg_var)); - } - - // Platform-specific fallbacks - #[cfg(target_os = "linux")] - { /* Linux default */ } - - #[cfg(target_os = "macos")] - { /* macOS default */ } - - #[cfg(target_os = "windows")] - { /* Windows default */ } - } - ``` - -3. **Update documentation** - -- Add to the table in README.md -- Include examples in doc comments - -## Pull Request Guidelines - -### Before Submitting - -- [ ] Code passes all tests (`mise run test` or `./bin/test`) -- [ ] Code passes all lints (`mise run lint:check:rust` or `./bin/lint/check/rust`) -- [ ] Documentation is updated if needed -- [ ] Platform-specific behavior is documented -- [ ] XDG compliance is maintained - -### PR Description - -Please include: - -- **What**: Brief description of changes -- **Why**: Motivation for the change -- **Platform impact**: Any platform-specific considerations -- **Testing**: How you tested the changes - -### Review Process - -1. **Automated checks** must pass (CI) -2. **Code review** by maintainers -3. **Testing** on multiple platforms (via CI) -4. **Merge** once approved - -## Getting Help - -- **Issues**: Open an issue for bugs or feature requests -- **Discussions**: Use GitHub Discussions for questions -- **Documentation**: Check the README.md and code comments - -## References - -Understanding these specifications will help with contributions: - -- [XDG Base Directory Specification][xdg-spec] -- [Apple File System Programming Guide][apple-guide] -- [Windows Known Folder IDs][windows-folders] - -[xdg-spec]: https://specifications.freedesktop.org/basedir-spec/latest/ -[apple-guide]: - https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/ -[windows-folders]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid - -## License - -By contributing to dir_spec, you agree that your contributions will be licensed under the MIT License. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..d21cb17 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,11 @@ +# Documentation + +| Section | Description | +|-------------------------------|-----------------------------------------------------| +| [Development][dev] | Contributing guide, code style, tasks, and testing | +| [Migration Guides][migration] | Guides for migrating from one version to another | +| [Policy][policy] | Code of conduct, AI use policy, and security policy | + +[dev]: https://github.com/aaronmallen/dir_spec/blob/main/docs/dev/README.md +[migration]: https://github.com/aaronmallen/dir_spec/tree/main/docs/migration_guides +[policy]: https://github.com/aaronmallen/dir_spec/blob/main/docs/policy/README.md diff --git a/docs/dev/README.md b/docs/dev/README.md new file mode 100644 index 0000000..2069489 --- /dev/null +++ b/docs/dev/README.md @@ -0,0 +1,46 @@ +# Contributing to dir_spec + +Thank you for your interest in contributing to dir_spec! Whether you're fixing a bug, adding a feature, improving +documentation, or just asking questions -- all contributions are welcome. + +## Before You Start + +Please read and agree to the following before contributing: + +- **[Code of Conduct][coc]** -- how we treat each other +- **[AI Use Policy][ai-policy]** -- expectations around AI-assisted contributions + +By submitting a contribution you acknowledge that you have read and agree to these policies and practices. + +## Getting Oriented + +| Resource | What it covers | +|-----------------------------|----------------------------------------------------------| +| [Security Policy][security] | Reporting vulnerabilities responsibly | +| [Code Style Guide][style] | Formatting rules and code organization conventions | +| [Tasks][tasks] | Running builds, tests, formatting, and linting with mise | +| [Testing][testing] | How to write and organize tests | +| [Commits][commits] | Conventional commit format and scoping rules | + +## Ways to Contribute + +- **Report a bug** -- [open an issue][new-issue] with steps to reproduce +- **Suggest a feature** -- start a discussion or issue describing the problem you'd like solved +- **Fix a bug or implement a feature** -- check the issue tracker for open items, or bring your own idea +- **Improve documentation** -- typos, missing context, better examples -- it all helps +- **Write or improve tests** -- more coverage makes the project more reliable for everyone +- **Review pull requests** -- thoughtful review is just as valuable as writing code +- **Report a security vulnerability** -- follow the [Security Policy][security] instead of filing a public issue + +## Questions? + +If something is unclear, open a discussion or issue -- we're happy to help. + +[ai-policy]: https://github.com/aaronmallen/dir_spec/blob/main/docs/policy/ai.md +[coc]: https://github.com/aaronmallen/dir_spec/blob/main/docs/policy/code-of-conduct.md +[commits]: https://github.com/aaronmallen/dir_spec/blob/main/docs/dev/commits.md +[new-issue]: https://github.com/aaronmallen/dir_spec/issues/new +[security]: https://github.com/aaronmallen/dir_spec/blob/main/docs/policy/security.md +[style]: https://github.com/aaronmallen/dir_spec/blob/main/docs/dev/code-style.md +[tasks]: https://github.com/aaronmallen/dir_spec/blob/main/docs/dev/tasks.md +[testing]: https://github.com/aaronmallen/dir_spec/blob/main/docs/dev/testing.md diff --git a/docs/dev/code-style.md b/docs/dev/code-style.md new file mode 100644 index 0000000..e3519fa --- /dev/null +++ b/docs/dev/code-style.md @@ -0,0 +1,90 @@ +# Code Style + +This document describes the formatting and organization conventions used in Gest. Most formatting is handled +automatically by tools -- you generally just need to run `mise run format` before committing. + +The code organization rules are the main things to keep in mind when writing new code. Project-specific rules will be +added to this document as the project matures. + +## Running Formatters and Linters + +```bash +mise run format # Format all files +mise run lint # Lint all files +``` + +## General Principles + +These principles apply across all file types in the project: + +| Principle | Convention | +|----------------------|---------------------------------------------------------------| +| Maximum line width | 120 characters | +| Indentation | 2 spaces (no tabs) | +| Trailing whitespace | None | +| Final newline | All files end with a single newline | +| Import/include order | Alphabetical, grouped by origin (stdlib, external, local) | +| Declaration ordering | Alphabetical within visibility groups (public before private) | + +These conventions are enforced by `.editorconfig` and the project's linting tools. + +## Import Style + +Prefer importing named types (structs, enums, traits) directly rather than using fully-qualified paths, unless there is +a name conflict. Functions and free-standing helpers may use the fully-qualified path. + +```rust +// Good: import the trait and type, qualify only where there's a conflict (fmt::Result vs std::Result) +use std::fmt::{self, Display, Formatter}; + +impl Display for Foo { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // ... + } +} + +// Bad: unnecessarily qualified types +impl fmt::Display for Foo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // ... + } +} + +// Fine: free functions can stay qualified +std::fs::create_dir_all(path)?; +``` + +## Code Organization + +### Module-Level Ordering + +Order items within a module by: + +1. **Constants**: All constant declarations first +2. **Type groups**: Each type definition immediately followed by its implementation blocks +3. **Free functions**: Any standalone helper functions after all type groups + +Type groups are ordered **alphabetically** by type name, with **public types before private types** (each visibility +group sorted alphabetically). + +### Enumeration Variants + +Enumeration variants should be ordered **alphabetically**. + +### Struct/Record Fields + +Struct or record fields should be ordered **alphabetically**, unless field order is semantically significant (e.g., +positional arguments in a CLI framework). + +### Implementation Block Ordering + +Order functions and methods within implementation blocks by: + +1. **Static vs Instance**: Static/associated functions first, then instance methods +2. **Visibility**: Public items first, then private items +3. **Alphabetical**: Within each group, sort alphabetically + +In test modules, fall back to purely alphabetical ordering when the static/instance/public/private structure doesn't +apply. See [testing] for test-specific conventions. + +[testing]: https://github.com/aaronmallen/dir_spec/blob/main/docs/dev/testing.md diff --git a/docs/dev/commits.md b/docs/dev/commits.md new file mode 100644 index 0000000..ad30466 --- /dev/null +++ b/docs/dev/commits.md @@ -0,0 +1,69 @@ +# Commit Conventions + +This project uses [Conventional Commits][conventional-commits] with **scoped** messages. + +## Format + +```text +(): + +[optional body] + +[optional footer(s)] +``` + +## Scopes + +If any scope can reasonably be applied to a commit, it **must** be included. Multiple scopes are comma-separated: + +```text +feat(store, cli): add task creation workflow +fix(cli): correct flag parsing for --verbose +``` + +If no scope reasonably applies, it may be omitted: + +```text +chore: update dependencies +docs: fix typo in README +``` + +## Types + +| Type | Purpose | +|------------|-----------------------------------------------------------| +| `feat` | A new feature | +| `fix` | A bug fix | +| `docs` | Documentation only changes | +| `style` | Changes that do not affect the meaning of the code | +| `refactor` | A code change that neither fixes a bug nor adds a feature | +| `perf` | A code change that improves performance | +| `test` | Adding or correcting tests | +| `build` | Changes to the build system or external dependencies | +| `ci` | Changes to CI configuration files and scripts | +| `chore` | Other changes that don't modify src or test files | +| `revert` | Reverts a previous commit | + +## Breaking Changes + +Append `!` after the type/scope to indicate a breaking change: + +```text +feat(cli)!: remove deprecated subcommands +``` + +A `BREAKING CHANGE:` footer can provide additional detail: + +```text +feat(cli)!: remove deprecated subcommands + +BREAKING CHANGE: `dir_spec foo` and `dir_spec bar` have been removed. +``` + +## Guidelines + +- Use the **imperative mood** in the description ("add feature" not "added feature") +- Keep the first line under **72 characters** +- Reference relevant issues in the footer (e.g., `Closes #42`) + +[conventional-commits]: https://www.conventionalcommits.org/ diff --git a/docs/dev/tasks.md b/docs/dev/tasks.md new file mode 100644 index 0000000..c47913f --- /dev/null +++ b/docs/dev/tasks.md @@ -0,0 +1,126 @@ +# Development Tasks + +This project uses [mise] to manage development tools and run common tasks. Mise ensures everyone uses the same tool +versions and provides convenient commands for formatting and linting. + +If you prefer not to use mise, all tasks have equivalent shell scripts in the `tasks/` directory. + +## Available Tasks + + +## `audit` + +- **Usage**: `audit` + +Audit dependencies + +## `build` + +- **Usage**: `build` +- **Aliases**: `b` + +Build the project + +## `check` + +- **Usage**: `check` +- **Aliases**: `c` + +Check for compilation errors + +## `format` + +- Depends: format:** + +- **Usage**: `format` +- **Aliases**: `fmt` + +Format all files + +## `format:markdown` + +- **Usage**: `format:markdown` +- **Aliases**: `fmt:markdown`, `fmt:md`, `format:md` + +Format markdown files + +## `format:rust` + +- **Usage**: `format:rust` +- **Aliases**: `fmt:rust`, `fmt:rs`, `format:rs` + +Format rust files + +## `format:toml` + +- **Usage**: `format:toml` +- **Aliases**: `fmt:toml` + +Format toml files + +## `format:yaml` + +- **Usage**: `format:yaml` +- **Aliases**: `fmt:yaml`, `fmt:yml`, `format:yml` + +Format yaml files + +## `lint` + +- Depends: lint:** + +- **Usage**: `lint` +- **Aliases**: `l` + +Lint all files + +## `lint:editorconfig` + +- **Usage**: `lint:editorconfig` +- **Aliases**: `l:ec`, `l:editorconfig`, `lint:ec` + +Lint files with editorconfig-checker + +## `lint:markdown` + +- **Usage**: `lint:markdown` +- **Aliases**: `l:md`, `l:markdown`, `lint:md` + +Lint markdown files + +## `lint:rust` + +- **Usage**: `lint:rust` +- **Aliases**: `l:rust`, `l:rs`, `lint:rs` + +Lint rust files + +## `lint:toml` + +- **Usage**: `lint:toml` +- **Aliases**: `l:toml` + +Lint toml files + +## `lint:yaml` + +- **Usage**: `lint:yaml` +- **Aliases**: `l:yaml`, `l:yml`, `lint:yml` + +Lint all yaml files + +## `test` + +- **Usage**: `test [--filter ]` +- **Aliases**: `t` + +Run all tests + +### Flags + +#### `--filter ` + +Filter tests by name + + +[mise]: https://mise.jdx.dev/ diff --git a/docs/dev/testing.md b/docs/dev/testing.md new file mode 100644 index 0000000..b3d2faf --- /dev/null +++ b/docs/dev/testing.md @@ -0,0 +1,62 @@ +# Testing + +This guide covers how to write and organize tests in Gest. + +The goal is to test meaningful behavior without over-testing trivial code. When in doubt, focus on logic that could +break -- transformations, edge cases, and complex calculations. + +## Core Principle + +**Tests are the source of truth.** Tests define the expected behavior of the system. If a test passes, the behavior it +describes is correct by definition. + +**Never modify existing integration tests unless the issue explicitly calls for behavioral change.** If an integration +test is failing, the implementation needs to change -- not the test. The only exception is when the issue or spec +explicitly requires a change in observable behavior. + +## Running Tests + +```bash +mise run test # Run all tests +mise run test -- --test xyz # Run tests matching "xyz" +``` + +## What to Test + +**Test:** + +- Functions and methods with logic (arithmetic, transformations, conditionals) +- Display/formatting implementations +- Custom comparison or ordering implementations +- Edge cases and boundary conditions +- Inverse operations (roundtrip tests) +- Error paths and failure modes + +**Skip:** + +- Simple constructors that just assign fields +- Trivial getters that return field values +- Thin wrappers that only delegate to another function + +Before writing a test, ask: "Does this test verify actual logic, or just that field assignment works?" + +## Test Structure + +Tests should be organized in a way that mirrors the module structure. Group tests by the function or method they +exercise, and name test functions descriptively using the pattern `it_`. + +## Conventions + +**Naming:** Test functions use the pattern `it_`. Group names match the function or method being tested. + +**Ordering:** Test groups follow [code style][code-style] ordering -- static/associated functions first +(alphabetically), then instance methods (alphabetically). + +**Test body structure:** Separate setup from assertions with a blank line. For tests with multiple assertion groups, +separate each group with a blank line. + +**Integration tests:** Integration tests validate end-to-end behavior and are the strondir_spec contract in the +codebase. They must not be modified to make a failing implementation pass. If an integration test fails after a code +change, the code change is wrong unless the issue explicitly calls for a behavioral change. + +[code-style]: https://github.com/aaronmallen/dir_spec/blob/main/docs/dev/code-style.md diff --git a/docs/migration_guides/0.4.x-0.5.0.md b/docs/migration_guides/0.4.x-0.5.0.md index bd1c797..f036a21 100644 --- a/docs/migration_guides/0.4.x-0.5.0.md +++ b/docs/migration_guides/0.4.x-0.5.0.md @@ -226,27 +226,27 @@ find . -name "*.rs" -exec perl -pi -e 's/use dir_spec::Dir;/use dir_spec::*;/g' All functions remain unchanged except for how they're called: -| v0.4.0 | v0.5.0 | -|--------|---------| -| `Dir::bin_home()` | `bin_home()` | -| `Dir::cache_home()` | `cache_home()` | -| `Dir::config_home()` | `config_home()` | +| v0.4.0 | v0.5.0 | +|-----------------------|------------------| +| `Dir::bin_home()` | `bin_home()` | +| `Dir::cache_home()` | `cache_home()` | +| `Dir::config_home()` | `config_home()` | | `Dir::config_local()` | `config_local()` | -| `Dir::data_home()` | `data_home()` | -| `Dir::data_local()` | `data_local()` | -| `Dir::desktop()` | `desktop()` | -| `Dir::documents()` | `documents()` | -| `Dir::downloads()` | `downloads()` | -| `Dir::fonts()` | `fonts()` | -| `Dir::home()` | `home()` | -| `Dir::music()` | `music()` | -| `Dir::pictures()` | `pictures()` | -| `Dir::preferences()` | `preferences()` | -| `Dir::publicshare()` | `publicshare()` | -| `Dir::runtime()` | `runtime()` | -| `Dir::state_home()` | `state_home()` | -| `Dir::templates()` | `templates()` | -| `Dir::videos()` | `videos()` | +| `Dir::data_home()` | `data_home()` | +| `Dir::data_local()` | `data_local()` | +| `Dir::desktop()` | `desktop()` | +| `Dir::documents()` | `documents()` | +| `Dir::downloads()` | `downloads()` | +| `Dir::fonts()` | `fonts()` | +| `Dir::home()` | `home()` | +| `Dir::music()` | `music()` | +| `Dir::pictures()` | `pictures()` | +| `Dir::preferences()` | `preferences()` | +| `Dir::publicshare()` | `publicshare()` | +| `Dir::runtime()` | `runtime()` | +| `Dir::state_home()` | `state_home()` | +| `Dir::templates()` | `templates()` | +| `Dir::videos()` | `videos()` | ## Benefits of the New API diff --git a/docs/policy/ai.md b/docs/policy/ai.md new file mode 100644 index 0000000..c4c7fdd --- /dev/null +++ b/docs/policy/ai.md @@ -0,0 +1,36 @@ +# AI Tool Use Policy + +Gest welcomes contributions made with AI tools, including agentic development workflows. However, we require a +**human in the loop** for all contributions. + +## Policy + +- **You are the author.** As a contributor, you are fully accountable for your contributions regardless of what tools + you used to create them. +- **Review your work.** You must carefully read and review all tool-generated code or text before submitting it for + review. +- **Be transparent.** Note when your contribution contains substantial amounts of tool-generated content. +- **Be prepared.** You should be able to answer questions about your work during review. +- **Stay in the loop.** Agents may assist with development, but a human must review and approve all actions that affect + our shared spaces -- pull requests, issues, comments, and code. + +## Agents Welcome, With Oversight + +Unlike many projects, we do not prohibit agentic development. AI agents can be powerful collaborators. What we prohibit +is **unsupervised** agent activity in our spaces. Specifically: + +- Agents **must not** take action in our repositories (opening PRs, commenting on issues, posting reviews) without + explicit human review and approval of that specific action. +- Automated review tools that publish comments without human review are not allowed. +- A human must be the one to click "submit" -- not a cron job, not a webhook, not a bot. + +## Rationale + +Processing proposals, issues, PRs, and comments takes maintainer time and energy. Unreviewed tool output shifts effort +from the contributor to the reviewer, making it an extractive contribution. The human-in-the-loop requirement ensures +that contributors take ownership of their work and that maintainer time is respected. + +## Handling Violations + +If a maintainer determines that a contribution was submitted without meaningful human oversight, they may request +changes, close the PR, or lock the conversation at their discretion. diff --git a/docs/policy/code-of-conduct.md b/docs/policy/code-of-conduct.md new file mode 100644 index 0000000..b54d0f0 --- /dev/null +++ b/docs/policy/code-of-conduct.md @@ -0,0 +1,109 @@ +# 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, caste, +color, 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: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall community + +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 email 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 by mentioning [@aaronmallen][aaronmallen]. 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.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[aaronmallen]: https://github.com/aaronmallen +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/docs/SECURITY.md b/docs/policy/security.md similarity index 100% rename from docs/SECURITY.md rename to docs/policy/security.md diff --git a/mise.toml b/mise.toml deleted file mode 100644 index a91d3f8..0000000 --- a/mise.toml +++ /dev/null @@ -1,17 +0,0 @@ -[task_config] -includes = ["bin"] - -[tools] -"cargo:cargo-audit" = "latest" -"cargo:cargo-llvm-cov" = "latest" -"cargo:cargo-nextest" = "latest" -"cargo:cargo-outdated" = "latest" -"cargo:cargo-sort" = "latest" -"cargo:taplo-cli" = "latest" -"cargo:cargo-workspace" = "latest" -"npm:editorconfig-checker" = "latest" -"npm:markdownlint-cli" = "latest" -pipx = { version = "latest", os = ["linux", "macos"] } -rust = { version = "nightly", components = "clippy,llvm-tools-preview,rustfmt,rust-src", targets = "x86_64-unknown-linux-gnu,x86_64-pc-windows-gnu,x86_64-apple-darwin,aarch64-apple-darwin" } -shellcheck = "latest" -yamllint = { version = "latest", os = ["linux", "macos"] } diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 28de78e..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,3 +0,0 @@ -[toolchain] -channel = "nightly" -components = ["clippy", "llvm-tools-preview", "rustfmt", "rust-src"] diff --git a/src/lib.rs b/src/lib.rs index e815205..006e236 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -466,7 +466,11 @@ mod tests { #[test] fn respects_xdg_cache_home() { - let test_path = if cfg!(windows) { "C:\\test\\cache" } else { "/test/cache" }; + let test_path = if cfg!(windows) { + "C:\\test\\cache" + } else { + "/test/cache" + }; with_var("XDG_CACHE_HOME", Some(test_path), || { let result = cache_home(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -514,7 +518,11 @@ mod tests { #[test] fn respects_xdg_config_home() { - let test_path = if cfg!(windows) { "C:\\test\\config" } else { "/test/config" }; + let test_path = if cfg!(windows) { + "C:\\test\\config" + } else { + "/test/config" + }; with_var("XDG_CONFIG_HOME", Some(test_path), || { let result = config_home(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -678,7 +686,11 @@ mod tests { #[test] fn respects_xdg_desktop_dir() { - let test_path = if cfg!(windows) { "C:\\test\\desktop" } else { "/test/desktop" }; + let test_path = if cfg!(windows) { + "C:\\test\\desktop" + } else { + "/test/desktop" + }; with_var("XDG_DESKTOP_DIR", Some(test_path), || { let result = desktop(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -714,7 +726,11 @@ mod tests { #[test] fn respects_xdg_documents_dir() { - let test_path = if cfg!(windows) { "C:\\test\\documents" } else { "/test/documents" }; + let test_path = if cfg!(windows) { + "C:\\test\\documents" + } else { + "/test/documents" + }; with_var("XDG_DOCUMENTS_DIR", Some(test_path), || { let result = documents(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -750,7 +766,11 @@ mod tests { #[test] fn respects_xdg_download_dir() { - let test_path = if cfg!(windows) { "C:\\test\\downloads" } else { "/test/downloads" }; + let test_path = if cfg!(windows) { + "C:\\test\\downloads" + } else { + "/test/downloads" + }; with_var("XDG_DOWNLOAD_DIR", Some(test_path), || { let result = downloads(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -850,7 +870,11 @@ mod tests { #[test] fn respects_xdg_music_dir() { - let test_path = if cfg!(windows) { "C:\\test\\music" } else { "/test/music" }; + let test_path = if cfg!(windows) { + "C:\\test\\music" + } else { + "/test/music" + }; with_var("XDG_MUSIC_DIR", Some(test_path), || { let result = music(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -886,7 +910,11 @@ mod tests { #[test] fn respects_xdg_pictures_dir() { - let test_path = if cfg!(windows) { "C:\\test\\pictures" } else { "/test/pictures" }; + let test_path = if cfg!(windows) { + "C:\\test\\pictures" + } else { + "/test/pictures" + }; with_var("XDG_PICTURES_DIR", Some(test_path), || { let result = pictures(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -961,7 +989,11 @@ mod tests { #[test] fn respects_xdg_publicshare_dir() { - let test_path = if cfg!(windows) { "C:\\test\\public" } else { "/test/public" }; + let test_path = if cfg!(windows) { + "C:\\test\\public" + } else { + "/test/public" + }; with_var("XDG_PUBLICSHARE_DIR", Some(test_path), || { let result = publicshare(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -1013,7 +1045,11 @@ mod tests { #[test] fn respects_xdg_runtime_dir() { - let test_path = if cfg!(windows) { "C:\\test\\runtime" } else { "/test/runtime" }; + let test_path = if cfg!(windows) { + "C:\\test\\runtime" + } else { + "/test/runtime" + }; with_var("XDG_RUNTIME_DIR", Some(test_path), || { let result = runtime(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -1074,7 +1110,11 @@ mod tests { #[test] fn respects_xdg_state_home() { - let test_path = if cfg!(windows) { "C:\\test\\state" } else { "/test/state" }; + let test_path = if cfg!(windows) { + "C:\\test\\state" + } else { + "/test/state" + }; with_var("XDG_STATE_HOME", Some(test_path), || { let result = state_home(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -1122,7 +1162,11 @@ mod tests { #[test] fn respects_xdg_templates_dir() { - let test_path = if cfg!(windows) { "C:\\test\\templates" } else { "/test/templates" }; + let test_path = if cfg!(windows) { + "C:\\test\\templates" + } else { + "/test/templates" + }; with_var("XDG_TEMPLATES_DIR", Some(test_path), || { let result = templates(); assert_eq!(result, Some(PathBuf::from(test_path))); @@ -1158,7 +1202,11 @@ mod tests { #[test] fn respects_xdg_videos_dir() { - let test_path = if cfg!(windows) { "C:\\test\\videos" } else { "/test/videos" }; + let test_path = if cfg!(windows) { + "C:\\test\\videos" + } else { + "/test/videos" + }; with_var("XDG_VIDEOS_DIR", Some(test_path), || { let result = videos(); assert_eq!(result, Some(PathBuf::from(test_path))); diff --git a/src/linux.rs b/src/linux.rs index 98ce1fe..f570662 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -59,8 +59,12 @@ pub fn publicshare() -> Option { } pub fn runtime() -> Option { - xdg::resolve_path(xdg::RUNTIME_DIR) - .or_else(|| env::var("TMPDIR").ok().map(PathBuf::from).or_else(|| Some(PathBuf::from("/tmp")))) + xdg::resolve_path(xdg::RUNTIME_DIR).or_else(|| { + env::var("TMPDIR") + .ok() + .map(PathBuf::from) + .or_else(|| Some(PathBuf::from("/tmp"))) + }) } pub fn state_home() -> Option { diff --git a/src/macos.rs b/src/macos.rs index db90122..4bf9b70 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -61,8 +61,12 @@ pub fn publicshare() -> Option { } pub fn runtime() -> Option { - xdg::resolve_path(xdg::RUNTIME_DIR) - .or_else(|| env::var("TMPDIR").ok().map(PathBuf::from).or_else(|| Some(PathBuf::from("/tmp")))) + xdg::resolve_path(xdg::RUNTIME_DIR).or_else(|| { + env::var("TMPDIR") + .ok() + .map(PathBuf::from) + .or_else(|| Some(PathBuf::from("/tmp"))) + }) } pub fn state_home() -> Option { diff --git a/taplo.toml b/taplo.toml deleted file mode 100644 index 6f1709f..0000000 --- a/taplo.toml +++ /dev/null @@ -1,2 +0,0 @@ -include = ["**/*.toml"] -exclude = ["target/**"] diff --git a/tasks/audit b/tasks/audit new file mode 100755 index 0000000..7f65b73 --- /dev/null +++ b/tasks/audit @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Audit dependencies" + +set -e + +cargo audit +cargo outdated diff --git a/tasks/build b/tasks/build new file mode 100755 index 0000000..1be4d87 --- /dev/null +++ b/tasks/build @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Build the project" +#MISE alias=["b"] + +set -e + +cargo build --workspace --all-features diff --git a/tasks/check b/tasks/check new file mode 100755 index 0000000..809e110 --- /dev/null +++ b/tasks/check @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Check for compilation errors" +#MISE alias=["c"] + +set -e + +cargo check --workspace --all-targets --all-features diff --git a/tasks/format/markdown b/tasks/format/markdown new file mode 100755 index 0000000..0792ca0 --- /dev/null +++ b/tasks/format/markdown @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Format markdown files" +#MISE alias=["fmt:markdown", "fmt:md", "format:md"] + +set -e + +tasks/lint/markdown --fix diff --git a/tasks/format/rust b/tasks/format/rust new file mode 100755 index 0000000..d4c47f4 --- /dev/null +++ b/tasks/format/rust @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +#MISE description="Format rust files" +#MISE tools.rust="nightly" +#MISE alias=["fmt:rust", "fmt:rs", "format:rs"] + +set -e + +cargo sort --workspace --config .config/tomlfmt.toml +cargo +nightly fmt --all -- --config-path .config/rustfmt.toml +cargo clippy --fix --allow-dirty --allow-staged diff --git a/tasks/format/toml b/tasks/format/toml new file mode 100755 index 0000000..ad6c7fa --- /dev/null +++ b/tasks/format/toml @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Format toml files" +#MISE alias=["fmt:toml"] + +set -e + +taplo format --config .config/taplo.toml diff --git a/tasks/format/yaml b/tasks/format/yaml new file mode 100755 index 0000000..ca4ad4f --- /dev/null +++ b/tasks/format/yaml @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Format yaml files" +#MISE alias=["fmt:yaml", "fmt:yml", "format:yml"] + +set -e + +yamlfmt -conf .config/.yamlfmt . diff --git a/tasks/generate/task-docs b/tasks/generate/task-docs new file mode 100755 index 0000000..8413944 --- /dev/null +++ b/tasks/generate/task-docs @@ -0,0 +1,35 @@ +#!/usr/bin/env sh +#MISE hide=true + +set -e + +target="docs/dev/tasks.md" +begin_marker="" +end_marker="" +docs=$(mise generate task-docs) + +{ + head="" + tail="" + found_begin=false + found_end=false + while IFS= read -r line; do + if [ "$line" = "$end_marker" ]; then + found_end=true + fi + if [ "$found_begin" = true ] && [ "$found_end" = false ]; then + continue + fi + if [ "$found_end" = true ]; then + tail="$tail$line +" + else + head="$head$line +" + fi + if [ "$line" = "$begin_marker" ]; then + found_begin=true + fi + done < "$target" + printf '%s%s\n%s' "$head" "$docs" "$tail" > "$target" +} diff --git a/tasks/lint/editorconfig b/tasks/lint/editorconfig new file mode 100755 index 0000000..17e1630 --- /dev/null +++ b/tasks/lint/editorconfig @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Lint files with editorconfig-checker" +#MISE alias=["l:ec", "l:editorconfig", "lint:ec"] + +set -e + +ec -config .config/editorconfig-checker.json diff --git a/tasks/lint/markdown b/tasks/lint/markdown new file mode 100755 index 0000000..f7da52c --- /dev/null +++ b/tasks/lint/markdown @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Lint markdown files" +#MISE alias=["l:md", "l:markdown", "lint:md"] + +set -e + +markdownlint-cli2 --config .config/.markdownlint.yml "*.md" "**/*.md" "#.cargo" "#.mise" "#.mise-data" "#coverage" "#.github/PULL_REQUEST_TEMPLATE.md" "#target" "#tmp" "#**/node_modules" "$@" diff --git a/tasks/lint/rust b/tasks/lint/rust new file mode 100755 index 0000000..0c71f47 --- /dev/null +++ b/tasks/lint/rust @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +#MISE description="Lint rust files" +#MISE tools.rust="nightly" +#MISE alias=["l:rust", "l:rs", "lint:rs"] + +set -e + +cargo sort --workspace --check --config .config/tomlfmt.toml +cargo +nightly fmt --all --check -- --config-path .config/rustfmt.toml +cargo clippy diff --git a/tasks/lint/toml b/tasks/lint/toml new file mode 100755 index 0000000..a2a277c --- /dev/null +++ b/tasks/lint/toml @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Lint toml files" +#MISE alias=["l:toml"] + +set -e + +taplo lint --config .config/taplo.toml diff --git a/tasks/lint/yaml b/tasks/lint/yaml new file mode 100755 index 0000000..69a3c94 --- /dev/null +++ b/tasks/lint/yaml @@ -0,0 +1,7 @@ +#!/usr/bin/env sh +#MISE description="Lint all yaml files" +#MISE alias=["l:yaml", "l:yml", "lint:yml"] + +set -e + +yamlfmt -lint -conf .config/.yamlfmt . diff --git a/tasks/test b/tasks/test new file mode 100755 index 0000000..3750b6e --- /dev/null +++ b/tasks/test @@ -0,0 +1,17 @@ +#!/usr/bin/env sh +#MISE description="Run all tests" +#MISE alias=["t"] +#USAGE flag "--filter" help="Filter tests by name" { +#USAGE arg "" +#USAGE } + +set -e + +FILTER_ARGS="" +if [ -n "${usage_filter}" ]; then + FILTER_ARGS="--no-fail-fast --filter-expr 'test(/${usage_filter}/)'" +fi + +eval cargo llvm-cov nextest --workspace --summary-only --all-features --no-tests pass "${FILTER_ARGS}" +cargo llvm-cov report --html --output-dir coverage +cargo llvm-cov report --cobertura --output-path coverage/cobertura.xml