diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5a8705b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,68 @@ +# .gitattributes — Rust (GitHub-hosted) +# +# Sources: GitHub Docs (line endings), git-scm gitattributes (diff=rust), +# gitattributes/gitattributes Rust.gitattributes + Common.gitattributes, +# github-linguist overrides. + +# Default: detect text, normalize to LF in the repository +* text=auto + +# --- Source --- +*.rs text eol=lf diff=rust +*.toml text +Cargo.lock text +Cargo.toml text +rust-toolchain text +rust-toolchain.toml text + +# --- Scripts --- +*.bash text eol=lf +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf +*.sh text eol=lf +*.zsh text eol=lf + +# --- Docs / meta --- +*.adoc text +*.markdown text diff=markdown +*.md text diff=markdown +*.txt text +AUTHORS text +CHANGELOG text +CHANGES text +CONTRIBUTING text +COPYING text +LICENSE text +NEWS text +README text +TODO text +.gitattributes text +.gitignore text + +# --- Serialisation --- +*.json text +*.xml text +*.yaml text +*.yml text + +# --- Binary --- +*.a binary +*.dll binary +*.dylib binary +*.exe binary +*.rlib binary +*.so binary + +# --- Archives / images (common) --- +*.gif binary +*.gz binary +*.ico binary +*.jpeg binary +*.jpg binary +*.png binary +*.tar binary +*.zip binary + +# --- GitHub Linguist --- +**/target/** linguist-generated diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3c6a18c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,85 @@ +name: CI + +on: + push: + branches: + - master + - dev + - boilerplate + - idiomatic + - rc1 + - rc2 + - rc3 + pull_request: + +permissions: + contents: read + +defaults: + run: + shell: bash + +env: + CARGO_TERM_COLOR: always + +jobs: + check: + name: Stable checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - uses: Swatinem/rust-cache@v2 + + - name: cargo test (all targets) + run: cargo test --all-targets --locked + + - name: cargo test (no default features) + run: cargo test --no-default-features --locked + + - name: cargo clippy + run: cargo clippy --all-targets --locked -- -D warnings + + - name: cargo doc + run: RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --locked + + - name: Install pinned nightly rustfmt + run: rustup toolchain install nightly-2026-09-10 --profile minimal --component rustfmt + + - name: rustfmt + env: + RUSTFMT_TOOLCHAIN: nightly-2026-09-10 + RUSTUP_TOOLCHAIN: nightly-2026-09-10 + run: ./scripts/fmt --check + + - name: DOC_76 checker + run: python3 scripts/check_doc_76.py + + - name: RUST_TEST_NAMING checker + run: python3 scripts/check_test_names.py + + - name: DERIVE_LAYOUT checker + run: python3 scripts/check_derives.py + + - name: cargo build (examples) + run: cargo build --examples --locked + + - name: cargo publish (dry run) + run: cargo publish --dry-run --locked + + msrv: + name: MSRV (1.74) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@1.74.0 + + - uses: Swatinem/rust-cache@v2 + + - name: cargo check (library) + run: cargo check --lib --locked diff --git a/.gitignore b/.gitignore index 37c2727..6b5e062 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,27 @@ # directories (by name) -/_build/ +/_analyses/ + +/.idea/ + +/scratch/ /target/ # directories (by pattern) +**/__pycache__/ + +**/.mypy_cache/ +**/.pytest_cache/ +**/.ruff_cache/ + # files (by name) .DS_Store - -Cargo.lock +.ruby-version # files (by pattern) @@ -20,7 +29,45 @@ Cargo.lock **/*.rs.bk # These are backup files generated by rustfmt *~ +*.*~ +*.???_obj +*.a +*.app +*.bak +*.class +*.diff +*.dll +*.dylib +*.exe +*.gch +*.gem +*.idb +*.ilk +*.lib +*.log +*.ncb +*.o +*.obj +*.opensdf +*.opt +*.org +*.out +*.pch +*.pdb +*.pyc +*.pyo +*.res +*.sbr +*.scc +*.sdf +*.so +*.suo +*.sw[ponm] *.swp +*.test +*.tlog *.tmp +*.xcuserstate *.zip + diff --git a/.vimrc b/.vimrc index b7392f5..9546145 100644 --- a/.vimrc +++ b/.vimrc @@ -1,6 +1,70 @@ +" Synesis Rust project .vimrc — aligned with .vscode/settings.json (Rust) +set nocompatible +filetype indent plugin on +syntax enable set autoindent +set backspace=indent,eol,start +set hlsearch +set incsearch +set number + +" files.insertFinalNewline +set eol +set fixeol + +" editor.renderWhitespace: all +set list +set listchars=tab:->,trail:-,extends:>,precedes:<,nbsp:+ + +" editor.detectIndentation: false — global defaults (editor.tabSize: 4, insertSpaces: true) +set colorcolumn=76 set expandtab set shiftwidth=4 set softtabstop=4 -set tabstop=4 \ No newline at end of file +set tabstop=4 + +" colorcolumn draws a full-column tint in Vim (not a VS Code-style 1px line). +" Keep it subtle via the ColorColumn highlight group; reapply after colorscheme changes. +if has('termguicolors') + " set termguicolors +endif + +function! s:ConfigureColorColumn() abort + highlight ColorColumn ctermbg=236 guibg=#2a2a2a cterm=NONE gui=NONE +endfunction + +call s:ConfigureColorColumn() +autocmd ColorScheme * call s:ConfigureColorColumn() + +" files.trimTrailingWhitespace +autocmd BufWritePre * %s/\s\+$//e + +augroup sis_rust + autocmd! + + " [bat] + autocmd FileType bat,dosbatch setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=60,76 + + " [c] / [cpp] + autocmd FileType c,cpp setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=60,64,68,72,76 + + " [cmake] + autocmd FileType cmake setlocal noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 + + " [json] / [markdown] / [yaml] / [ruby] + autocmd FileType json,markdown,yaml,ruby setlocal expandtab tabstop=2 shiftwidth=2 softtabstop=2 + + " [python] + autocmd FileType python setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=60,76 + + " [rust] + autocmd FileType rust setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4 colorcolumn=76 + + " [shellscript] + autocmd FileType sh,bash,zsh setlocal expandtab tabstop=2 shiftwidth=2 softtabstop=2 colorcolumn=60,76 + + " [toml] + autocmd FileType toml setlocal noexpandtab tabstop=2 shiftwidth=2 softtabstop=2 +augroup END + diff --git a/.vscode/settings.json b/.vscode/settings.json index c7a30f7..6f3e9c2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,27 +1,64 @@ { "[json]": { + "editor.insertSpaces": true, "editor.tabSize": 2, }, + "[markdown]": { + "editor.insertSpaces": true, + "editor.tabSize": 2, + }, + "[python]": { + "editor.insertSpaces": true, + "editor.rulers": [ 60, 76 ], + "editor.tabSize": 4, + }, "[ruby]": { + "editor.insertSpaces": true, + "editor.rulers": [ 60, 76 ], "editor.tabSize": 2, }, "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "editor.formatOnSave": true, + "editor.insertSpaces": true, + "editor.rulers": [ 60, 76 ], "editor.tabSize": 4, + }, + "[shellscript]": { "editor.insertSpaces": true, "editor.rulers": [ 60, 76 ], + "editor.tabSize": 2, + }, + "[toml]": { + "editor.insertSpaces": false, + "editor.tabSize": 2, }, "cmake.configureOnOpen": false, + "debug.allowBreakpointsEverywhere": true, "editor.detectIndentation": false, "editor.insertSpaces": false, + "editor.renderWhitespace": "all", "editor.rulers": [ 76 ], "editor.tabSize": 2, - "editor.renderWhitespace": "all", "files.insertFinalNewline": true, "files.trimTrailingWhitespace": true, "git.mergeEditor": false, + "rust-analyzer.cargo.buildScripts.enable": true, + "rust-analyzer.cargo.features": [], "rust-analyzer.cargo.noDefaultFeatures": true, - "rust-analyzer.cargo.features": [ - ], + "rust-analyzer.check.allTargets": true, + "rust-analyzer.check.command": "clippy", + "rust-analyzer.checkOnSave": true, "rust-analyzer.completion.autoimport.enable": false, + "rust-analyzer.debug.engine": "vadimcn.vscode-lldb", + "rust-analyzer.debug.openDebugPane": true, + "rust-analyzer.procMacro.enable": true, + "rust-analyzer.rustfmt.overrideCommand": [ + "rustup", + "run", + "nightly-2026-09-10", + "rustfmt", + "--unstable-features", + ], "rust-analyzer.showUnlinkedFileNotification": false, } diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..9717d33 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,24 @@ +# recls.Rust - Changes + + +## 0.0.1 - 12th September 2026 + +* Recorded the first 0.0.1 release of the unpublished recursive-search + scaffold; +* Retained the absence of a supported public API and recursive-search + functionality; +* Retained reproducible dependency resolution, CI validation, and the + version-reporting example; + + +## 0.0.0 - 31st August 2026 + +* Recorded the existing 0.0.0 crate as an unpublished scaffold; +* Added explicit recursive-search status to **README.md** and crate-level rustdoc; +* Added reproducible pinned-nightly formatting, repository checkers, and locked CI validation; +* Updated reserved dependencies to bounded compatible releases, including **test_help-rs** 0.2.1; +* Documented the retained **Cargo.lock** policy and the absence of examples; +* Preserved the BSD-3-Clause license and copyright ownership chronology; + + + diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a571654 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,510 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base-traits" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "febda1e6bd75ec4a0680596f5d8689bbc391c3213607d286ab711f3b8b091ee1" +dependencies = [ + "bt-rs", +] + +[[package]] +name = "bt-rs" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b33a0644862185265eb4bee9b1679c1ae36f8258c000f3f482210acd95f7d70" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" + +[[package]] +name = "fastparse" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a61fd43821c8464ac7aebae8ef52dc009958ce8d93405d32576b3f779bec1ddd" + +[[package]] +name = "find-msvc-tools" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libpath" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247498207e14088ad0ebb185e537d290c14e72c22fa257613e97b741c5f0fc7" +dependencies = [ + "fastparse", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[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 = "recls" +version = "0.0.1" +dependencies = [ + "base-traits", + "criterion", + "fastparse", + "libpath", + "shwild", + "test_help-rs", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[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 3.0.5", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "shwild" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26bf6929b8c9e22cd97043d98753c0d9148c2e813772ff7ea4e95d3e92ca272e" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "test_help-rs" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbd2a2dbea40fe948ff30aab06e73ecf79ff443ec01d38dc16ed9a28e1a4847" +dependencies = [ + "base-traits", + "bt-rs", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[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", +] + +[[package]] +name = "zerocopy" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml index c4ad0cc..512b08b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,14 +7,35 @@ authors = [ "Matt Wilson ", ] -description = "recls (for Rust)" +categories = [ + "filesystem", +] +description = "Reserved scaffold for recursive filesystem search (for Rust)" +documentation = "https://docs.rs/recls" edition = "2021" +exclude = [ + ".cargo", + ".cursor", + ".github", + ".vimrc", + ".vscode", + "scripts", + "target", +] homepage = "https://github.com/synesissoftware/recls.Rust" +keywords = [ + "filesystem", + "path", + "recls", + "recursive-search", +] license = "BSD-3-Clause" name = "recls" +publish = true readme = "README.md" repository = "https://github.com/synesissoftware/recls.Rust" -version = "0.0.0" +rust-version = "1.74" +version = "0.0.1" # ########################################################## @@ -24,12 +45,19 @@ version = "0.0.0" name = "recls" path = "src/lib.rs" +[[example]] +name = "versions" +path = "examples/versions/main.rs" + # ########################################################## # Features [features] +default = [ +] + # General features: # # - "_NEVER_TO_BE_ENABLED" - this is a placeholder feature and must NEVER be specified; @@ -46,20 +74,26 @@ null-feature = [] # ########################################################## # Dependencies +[build-dependencies] + [dependencies] -base-traits = { version = "0", default-features = false, features = [ -] } -fastparse = { version = "~0.0", default-features = false, features = [ -] } -libpath = { version = "~0.0", default-features = false, features = [ -] } +base-traits = { version = "0.1", default-features = false, features = [ +]} +fastparse = { version = "0.0.3", default-features = false, features = [ +]} +libpath = { version = "0.0.3", default-features = false, features = [ +]} [dev-dependencies] -criterion = "*" -test_help-rs = { version = "0.1" } +criterion = { version = "0.8", default-features = false, features = [ +]} +shwild = { version = "0.2", default-features = false, features = [ +]} +test_help-rs = { version = "0.2", default-features = false, features = [ +]} # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 0000000..1fcc7a7 --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,8 @@ +# recls.Rust - Examples + + +| Name | Source | Summary | +| ---- | ------ | ------- | + + + diff --git a/LICENSE b/LICENSE index 40eb219..7f3a6e5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License - recls.Rust -Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems +Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems Copyright (c) 1999-2019, Matthew Wilson and Synesis Software All rights reserved. diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..2d5b2ac --- /dev/null +++ b/NEWS.md @@ -0,0 +1,10 @@ +# recls.Rust - News + + +| Date | News Item | Details | +| ------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------- | +| 12th September 2026 | [recls.Rust 0.0.1](https://github.com/synesissoftware/recls.Rust/releases/tag/0.0.1) released | Repository, CI, packaging, and tooling updates | +| 31st August 2026 | [recls.Rust 0.0.0](https://github.com/synesissoftware/recls.Rust/releases/tag/0.0.0) released | Initial unpublished recursive-search scaffold | + + + diff --git a/README.md b/README.md index 740c45e..10e842d 100644 --- a/README.md +++ b/README.md @@ -1,119 +1,148 @@ -# recls +# recls.Rust **re**cursive **ls**, for **Rust**. +![Language](https://img.shields.io/badge/Rust-000000?style=flat&logo=rust&logoColor=white) +[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) +![MSRV](https://img.shields.io/badge/MSRV-1.74-lightgrey) +[![CI](https://github.com/synesissoftware/recls.Rust/actions/workflows/ci.yml/badge.svg)](https://github.com/synesissoftware/recls.Rust/actions/workflows/ci.yml) + ## Table of Contents - [Introduction](#introduction) - [Installation](#installation) - [Components](#components) - - [Constants](#constants) - - [Enumerations](#enumerations) - - [Features](#features) - - [Functions](#functions) - - [Macros](#macros) - - [Structures](#structures) - - [Traits](#traits) + - [Supported API](#supported-api) + - [Features](#features) - [Examples](#examples) - [Project Information](#project-information) - - [Where to get help](#where-to-get-help) - - [Contribution guidelines](#contribution-guidelines) - - [Dependencies](#dependencies) - - [Dev Dependencies](#dev-dependencies) - - [Related projects](#related-projects) - - [License](#license) + - [Where to get help](#where-to-get-help) + - [Contribution guidelines](#contribution-guidelines) + - [Minimum Supported Rust Version (MSRV)](#minimum-supported-rust-version-msrv) + - [Dependencies](#dependencies) + - [Efferent (fan-out)](#efferent-fan-out) + - [Runtime Dependencies](#runtime-dependencies) + - [Build Dependencies](#build-dependencies) + - [Development Dependencies](#development-dependencies) + - [Afferent (fan-in)](#afferent-fan-in) + - [Related projects](#related-projects) + - [License](#license) ## Introduction -T.B.C. +**recls.Rust** is currently an unpublished scaffold. Recursive filesystem +search is not implemented, and no supported public API currently exists. The +repository reserves the Rust identity for a future recursive-search library. ## Installation -T.B.C. +The package is intentionally unpublished with `publish = false`. It is not +currently available as a supported crates.io dependency, and no installation +procedure should be inferred from this scaffold. + +This library repository retains **Cargo.lock** so local and CI validation can +use reproducible dependency resolution. +The existing formatting configuration retains nightly-only options, so +**scripts/fmt** selects the pinned `nightly-2026-09-10` formatter. ## Components -### Constants +### Supported API -No public constants are defined at this time. +No supported public API is currently defined. Path traversal, filesystem +search, filtering, and result types remain future implementation work. -### Enumerations +### Features -No public enumerations are defined at this time. +No supported public features are currently defined. -### Features +## Examples -No features are defined at this time. +No examples are currently applicable because there is no supported public API. +An **EXAMPLES.md** file will be added when a genuine API example exists. -### Functions +## Project Information -No public functions are defined at this time. +### Where to get help +Use the [recls.Rust issue tracker](https://github.com/synesissoftware/recls.Rust/issues) +for questions about the scaffold and future project work. -### Macros -No public macros are defined at this time. +### Contribution guidelines +Contributions should remain limited to scaffold, documentation, and packaging -### Structures -No public structures are defined at this time. +### Minimum Supported Rust Version (MSRV) +The declared Minimum Supported Rust Version (MSRV) for **recls.Rust** is **1.74**. -### Traits +This MSRV guarantee applies to the library crate itself, its runtime dependencies (`[dependencies]`), and its build dependencies (`[build-dependencies]`). Downstream consumers compiling this crate as a dependency are guaranteed that it builds cleanly on the declared MSRV toolchain. -No public traits are defined at this time. +Development dependencies (`[dev-dependencies]`, such as benchmarking frameworks like **criterion**) may require newer Rust toolchains for local development or performance testing. These dev-dependencies are never fetched or compiled by downstream consumers and do not affect the library's MSRV guarantee. -## Examples +work until a separate implementation task establishes a supported recursive- +search API. Do not add placeholder traversal, filtering, result types, +behavioural tests, or examples as part of boilerplate work. -T.B.C. +### Dependencies -## Project Information +#### Efferent (fan-out) -### Where to get help +The manifest retains the intended dependency direction for future work: -[GitHub Page](https://github.com/synesissoftware/recls.Rust "GitHub Page") +##### Runtime Dependencies +* [**base-traits**](https://github.com/synesissoftware/base-traits) is reserved + for future shared trait support; +* [**fastparse**](https://github.com/synesissoftware/FastParse.Rust) is reserved + for future parsing support; +* [**libpath**](https://github.com/synesissoftware/libpath.Rust) is reserved + for future path handling support. -### Contribution guidelines +These dependencies are not used by a supported API because none currently +exists. -Defect reports, feature requests, and pull requests are welcome on https://github.com/synesissoftware/recls.Rust. +##### Build Dependencies -### Dependencies +None. -Crates upon which **recls.Rust** depend: -* [**base-traits**](https://github.com/synesissoftware/base-traits); -* [**fastparse**](https://github.com/synesissoftware/fastparse); -* [**libpath**](https://github.com/synesissoftware/libpath); +##### Development Dependencies +* [**criterion**](https://github.com/criterion-rs/criterion.rs) is reserved + for future performance work; +* [**test_help-rs**](https://github.com/synesissoftware/test_help-rs) is reserved + for future test support. -##### Dev Dependencies -Crates upon which **recls.Rust** depend: +#### Afferent (fan-in) -* [**criterion**](https://github.com/bheisler/criterion.rs); -* [**test_help-rs**](https://github.com/synesissoftware/test_help-rs); +No downstream consumers are currently recorded. ### Related projects -T.B.C. +The future path-search implementation is expected to build on the related +**fastparse** and **libpath.Rust** projects. No supported Rust consumer is +currently recorded. ### License -**recls.Rust** is released under the 3-clause BSD license. See [LICENSE](./LICENSE) for details. +**recls.Rust** is released under the 3-clause BSD license. See +[LICENSE](./LICENSE) for details. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..ba9509c --- /dev/null +++ b/TODO.md @@ -0,0 +1,35 @@ +# recls.Rust - TODO + + +## Table of Contents + +- [Functional improvements](#functional-improvements) +- [Performance improvements](#performance-improvements) +- [Packaging improvements](#packaging-improvements) + + +## Functional improvements + +* [ ] Design and implement a supported recursive-search API in a separate + implementation task; +* [ ] Implement path traversal, filtering, and result types only as part of + that reviewed API work; +* [ ] Add behavioural tests only after supported public behaviour exists; +* [ ] Add maintained examples and **EXAMPLES.md** only after the API exists; + + +## Performance improvements + +* \ + + +## Packaging improvements + +* [ ] Reassess `publish = false` after a supported public API and release + policy have been established; +* [ ] Review the retained **Cargo.lock** policy before publication; +* [ ] Revalidate the reserved **base-traits**, **fastparse**, and **libpath** + relationships when implementation work begins; + + + diff --git a/examples/versions/main.rs b/examples/versions/main.rs new file mode 100644 index 0000000..9886976 --- /dev/null +++ b/examples/versions/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("{} v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"),); +} diff --git a/.rustfmt.toml b/rustfmt.toml similarity index 74% rename from .rustfmt.toml rename to rustfmt.toml index d3e3a99..6ff4572 100644 --- a/.rustfmt.toml +++ b/rustfmt.toml @@ -1,7 +1,16 @@ - -# rustfmt.toml for recls.Rust +# rustfmt.toml +# +# Synesis Information Systems — gold rustfmt configuration for SIS Rust +# crates. +# +# Requires pinned nightly rustfmt with unstable features. Prefer: +# +# ./scripts/fmt +# +# Default pin (see scripts/fmt): nightly-2026-09-10 # -# configured for cargo-fmt 1.84.0-nightly +# Layout: full option inventory, lexicographic by option name; deprecated +# or unused options remain present but commented. # array_width=60 # deprecated # attr_fn_like_width=70 # deprecated @@ -12,30 +21,29 @@ brace_style="SameLineWhere" # chain_width=60 # deprecated color="Auto" combine_control_expr=false -comment_width=100 +comment_width=76 condense_wildcard_suffixes=false control_brace_style="AlwaysSameLine" disable_all_formatting=false -edition="2018" +edition="2021" empty_item_single_line=false enum_discrim_align_threshold=0 error_on_line_overflow=false error_on_unformatted=false -# fn_args_layout="Vertical" +# fn_args_layout="Vertical" # deprecated alias of fn_params_layout # fn_call_width=60 # deprecated fn_params_layout="Vertical" fn_single_line=false +# force_code_in_doc_comments=true force_explicit_abi=true force_multiline_blocks=true -# force_code_in_doc_comments=true format_macro_bodies=true format_macro_matchers=true format_strings=false group_imports="Preserve" hard_tabs=false -hide_parse_errors=false +# hide_parse_errors=false ignore=[ - ] imports_granularity="Crate" imports_indent="Block" @@ -61,7 +69,7 @@ reorder_modules=true # report_todo="Never" # required_version="????" short_array_element_width_threshold=1 -# show_parse_errors=true +show_parse_errors=true # single_line_if_else_max_width=0 # deprecated skip_children=false space_after_colon=true @@ -69,17 +77,19 @@ space_before_colon=true spaces_around_ranges=false struct_field_align_threshold=20 struct_lit_single_line=false +# struct_lit_trailing_comma="Vertical" # struct_lit_width=0 # deprecated +# struct_trailing_comma="Vertical" # struct_variant_width=0 # deprecated -tab_spaces=4 # Q: do we want to move to 2?? +# style_edition="2021" +tab_spaces=4 trailing_comma="Vertical" trailing_semicolon=true type_punctuation_density="Wide" -# unstable_features=false +unstable_features=true use_field_init_shorthand=true use_small_heuristics="Default" use_try_shorthand=true # version= where_single_line=false wrap_comments=false - diff --git a/scripts/check_derives.py b/scripts/check_derives.py new file mode 100755 index 0000000..5c595ec --- /dev/null +++ b/scripts/check_derives.py @@ -0,0 +1,129 @@ +#! /usr/bin/env python3 +""" +Verify DERIVE_LAYOUT: multi-trait `#[derive(...)]` macros must be split +into separate single-trait lines, ordered alphabetically by trait name, +except tightly coupled groups (Eq/PartialEq, Ord/PartialOrd). +""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path + + +COUPLED_TRAIT_GROUPS = [ + ["Eq", "PartialEq"], + ["Ord", "PartialOrd"], +] + +PASS = "\N{WHITE HEAVY CHECK MARK}" # ✅ +FAIL = "\N{CROSS MARK}" # ❌ + + +def lint_file(filepath: Path) -> list[str]: + errors: list[str] = [] + + lines = filepath.read_text(encoding="utf-8").splitlines() + i = 0 + + while i < len(lines): + line = lines[i] + + if not re.match(r"^\s*#\[derive\(", line): + i += 1 + continue + + derive_block: list[tuple[int, str]] = [] + start_line_num = i + 1 + + while i < len(lines) and re.match(r"^\s*#\[derive\(", lines[i]): + derive_block.append((i + 1, lines[i])) + i += 1 + + parsed_lines: list[tuple[int, str, str]] = [] + block_has_error = False + + for line_num, line_str in derive_block: + match = re.search(r"#\[derive\((.*?)\)\]", line_str) + + if not match: + continue + + traits = [ + t.strip() + for t in match.group(1).split(",") + if t.strip() + ] + + if len(traits) > 1: + if traits not in COUPLED_TRAIT_GROUPS: + block_has_error = True + allowed = ", ".join( + f"'{', '.join(group)}'" + for group in COUPLED_TRAIT_GROUPS + ) + errors.append( + f"{filepath}:{line_num}: multi-trait derive " + f"'{line_str.strip()}' is not allowed " + f"(except coupled groups: {allowed})", + ) + elif len(traits) == 0: + block_has_error = True + errors.append( + f"{filepath}:{line_num}: empty derive attribute " + f"'{line_str.strip()}'", + ) + + sort_key = traits[0] if traits else "" + parsed_lines.append((line_num, line_str, sort_key)) + + if not block_has_error and len(parsed_lines) > 1: + sort_keys = [item[2] for item in parsed_lines] + + if sort_keys != sorted(sort_keys): + actual = [item[1].strip() for item in parsed_lines] + expected = [ + item[1].strip() + for item in sorted(parsed_lines, key=lambda x: x[2]) + ] + errors.append( + f"{filepath}:{start_line_num}: derive attributes not " + f"sorted alphabetically\n" + f" actual: {actual}\n" + f" expected: {expected}", + ) + + return errors + + +def main() -> int: + root = Path(__file__).resolve().parents[1] + errors: list[str] = [] + + for directory in ("src", "examples", "benches", "test"): + base = root / directory + + if not base.is_dir(): + continue + + for path in sorted(base.rglob("*.rs")): + if "target" in path.parts: + continue + + errors.extend(lint_file(path)) + + if errors: + print( + f"{FAIL} DERIVE_LAYOUT violations:", + file=sys.stderr, + ) + print("\n".join(f" {FAIL} {error}" for error in errors), file=sys.stderr) + return 1 + + print(f"{PASS} DERIVE_LAYOUT: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/check_doc_76.py b/scripts/check_doc_76.py new file mode 100755 index 0000000..2a10562 --- /dev/null +++ b/scripts/check_doc_76.py @@ -0,0 +1,66 @@ +#! /usr/bin/env python3 +""" +Verify DOC_76: public documentation comment lines are at most 76 characters. + +Code blocks inside doc comments (``` ... ```) are exempt, matching Synesis +Information Systems' internal project standards. +""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path + + +DOC_LINE = re.compile(r"^\s*(//!|///)") + +PASS = "\N{WHITE HEAVY CHECK MARK}" # ✅ +FAIL = "\N{CROSS MARK}" # ❌ + + +def iter_doc_violations(path: Path) -> list[str]: + violations: list[str] = [] + in_codeblock = False + + for line_no, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + stripped = line.rstrip() + + if DOC_LINE.match(stripped) and re.search(r"\s*```\s*$", stripped): + in_codeblock = not in_codeblock + continue + + if in_codeblock or not DOC_LINE.match(stripped): + continue + + if len(stripped) > 76: + violations.append( + f"{path}:{line_no} ({len(stripped)} chars): {stripped}" + ) + + return violations + + +def main() -> int: + root = Path(__file__).resolve().parents[1] + errors: list[str] = [] + + for path in sorted(root.rglob("*.rs")): + if "target" in path.parts: + continue + errors.extend(iter_doc_violations(path)) + + if errors: + print( + f"{FAIL} DOC_76 violations (doc comment lines must be <= 76 characters):", + file=sys.stderr, + ) + print("\n".join(f" {FAIL} {error}" for error in errors), file=sys.stderr) + return 1 + + print(f"{PASS} DOC_76: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/check_test_names.py b/scripts/check_test_names.py new file mode 100755 index 0000000..1909373 --- /dev/null +++ b/scripts/check_test_names.py @@ -0,0 +1,243 @@ +#! /usr/bin/env python3 +""" +Verify RUST_TEST_NAMING: test functions and test modules use TEST_ prefix +and SHOUTING_SNAKE_CASE, except words that name a specific Rust construct +(type, function, macro, field, etc.) which must preserve exact case. + +When a construct name is embedded as a SHOUTING_SNAKE_CASE constant (or +PascalCase construct), it may be delimited with an extra underscore on +each side — e.g. HAVING__IGNORE_CASE__1. +""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path + + +TEST_ATTR = re.compile(r"^\s*#\[(\w+::)?test(\(\))?\]") +FN_DEF = re.compile(r"^\s*fn\s+(\w+)") +MOD_DEF = re.compile(r"^\s*mod\s+(\w+)") +SNAKE_PART = re.compile(r"^[a-z][a-z0-9]*$") + +PASS = "\N{WHITE HEAVY CHECK MARK}" # ✅ +FAIL = "\N{CROSS MARK}" # ❌ + + +def is_pascal_case_atom(atom: str) -> bool: + return ( + atom[0].isupper() + and any(c.islower() for c in atom) + and atom.isalnum() + ) + + +def atom_violation(atom: str) -> str | None: + if atom.isupper() or atom.isdigit(): + return None + + if atom[0].islower() and all(SNAKE_PART.match(part) for part in atom.split("_")): + return None + + if is_pascal_case_atom(atom): + return None + + return ( + f"segment '{atom}' must be SHOUTING_SNAKE_CASE, a PascalCase construct " + "name, or a Rust snake_case identifier" + ) + + +def parse_padded_construct( + segments: list[str], start: int +) -> tuple[str | None, int, list[str]]: + """Parse __CONSTRUCT__ padding around a shouting or PascalCase atom.""" + violations: list[str] = [] + i = start + + while i < len(segments) and not segments[i]: + i += 1 + if i >= len(segments): + return None, i, ["empty segment padding without construct"] + + seg = segments[i] + atom: str | None = None + + if seg.isupper() or seg.isdigit(): + parts = [seg] + i += 1 + while i < len(segments) and segments[i] and ( + segments[i].isupper() or segments[i].isdigit() + ): + parts.append(segments[i]) + i += 1 + atom = "_".join(parts) + reason = atom_violation(atom) + if reason: + violations.append(reason) + elif seg[0].isupper() and is_pascal_case_atom(seg): + atom = seg + reason = atom_violation(seg) + if reason: + violations.append(reason) + i += 1 + else: + return None, start, ["empty segment padding without construct"] + + while i < len(segments) and not segments[i]: + i += 1 + + return atom, i, violations + + +def parse_name_atoms(rest: str) -> tuple[list[str], list[str]]: + """Split a test name body into atoms; return (atoms, violations).""" + atoms: list[str] = [] + violations: list[str] = [] + segments = rest.split("_") + i = 0 + + while i < len(segments): + seg = segments[i] + if not seg: + start = i + atom, i, viols = parse_padded_construct(segments, i) + violations.extend(viols) + if atom: + atoms.append(atom) + elif not viols: + violations.append(f"empty segment in '{rest}'") + if i == start: + i += 1 + continue + + if seg.isupper() or seg.isdigit(): + reason = atom_violation(seg) + if reason: + violations.append(reason) + else: + atoms.append(seg) + i += 1 + continue + + if seg[0].isupper(): + reason = atom_violation(seg) + if reason: + violations.append(reason) + else: + atoms.append(seg) + i += 1 + continue + + if SNAKE_PART.match(seg): + parts = [seg] + i += 1 + while i < len(segments) and SNAKE_PART.match(segments[i]): + parts.append(segments[i]) + i += 1 + atom = "_".join(parts) + reason = atom_violation(atom) + if reason: + violations.append(reason) + else: + atoms.append(atom) + continue + + violations.append( + f"segment '{seg}' must be SHOUTING_SNAKE_CASE, a PascalCase construct " + "name, or a Rust snake_case identifier" + ) + i += 1 + + return atoms, violations + + +def iter_name_violations(name: str) -> list[str]: + if not name.startswith("TEST_"): + return ["must start with 'TEST_'"] + + rest = name[len("TEST_") :] + if not rest: + return ["must have a name after 'TEST_'"] + + _, violations = parse_name_atoms(rest) + return violations + + +def iter_test_results(path: Path, root: Path) -> list[tuple[bool, str]]: + results: list[tuple[bool, str]] = [] + pending_test = False + display = path.relative_to(root) + + for line_no, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + stripped = line.rstrip() + + if TEST_ATTR.match(stripped): + pending_test = True + continue + + if pending_test and stripped.startswith("#["): + continue + + fn_match = FN_DEF.match(stripped) + if fn_match: + name = fn_match.group(1) + if pending_test: + pending_test = False + reasons = iter_name_violations(name) + label = f"{display}:{line_no}: test function '{name}'" + if reasons: + for reason in reasons: + results.append((False, f"{label}: {reason}")) + else: + results.append((True, label)) + continue + + mod_match = MOD_DEF.match(stripped) + if mod_match: + pending_test = False + name = mod_match.group(1) + if name.startswith("TEST_"): + reasons = iter_name_violations(name) + label = f"{display}:{line_no}: test module '{name}'" + if reasons: + for reason in reasons: + results.append((False, f"{label}: {reason}")) + else: + results.append((True, label)) + continue + + if stripped and not stripped.startswith("#") and stripped.endswith("{"): + pending_test = False + + return results + + +def main() -> int: + root = Path(__file__).resolve().parents[1] + results: list[tuple[bool, str]] = [] + + for path in sorted(root.rglob("*.rs")): + if "target" in path.parts: + continue + results.extend(iter_test_results(path, root)) + + failures = [line for ok, line in results if not ok] + if failures: + print( + f"{FAIL} RUST_TEST_NAMING violations " + "(test functions and modules must use TEST_ + SHOUTING_SNAKE_CASE):", + file=sys.stderr, + ) + for ok, line in results: + mark = PASS if ok else FAIL + print(f" {mark} {line}", file=sys.stderr) + return 1 + + print(f"{PASS} RUST_TEST_NAMING: ok") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/fmt b/scripts/fmt new file mode 100755 index 0000000..932cc02 --- /dev/null +++ b/scripts/fmt @@ -0,0 +1,17 @@ +#! /usr/bin/env bash +# Synesis Information Systems — gold rustfmt driver for SIS Rust crates. +# +# Uses a dated nightly only for this process (see RUSTFMT_TOOLCHAIN). Does +# not rely on rust-toolchain.toml. +set -euo pipefail + +RUSTFMT_TOOLCHAIN="${RUSTFMT_TOOLCHAIN:-nightly-2026-09-10}" +RUSTFMT="$(rustup which --toolchain "${RUSTFMT_TOOLCHAIN}" rustfmt 2>/dev/null || true)" +if [[ -z "${RUSTFMT}" ]]; then + echo "error: ${RUSTFMT_TOOLCHAIN} rustfmt is required (see rustfmt.toml)" >&2 + echo " rustup toolchain install ${RUSTFMT_TOOLCHAIN} --component rustfmt" >&2 + exit 1 +fi + +export RUSTFMT +exec env RUSTUP_TOOLCHAIN="${RUSTFMT_TOOLCHAIN}" cargo fmt --all -- --unstable-features "$@" diff --git a/src/lib.rs b/src/lib.rs index 8190b2d..a66b7a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,9 @@ -// src/lib.rs : Definition of the recls Rust package - -// /////////////////////////////////////////////// -// crate-level feature definitions - - -// /////////////////////////////////////////////// -// crate-level imports - - - -#[cfg(test)] -mod tests { - #![allow(non_snake_case)] - -} +//! **recls.Rust** is currently an unpublished scaffold for recursive +//! filesystem search in Rust. +//! +//! Recursive search is not implemented in this scaffold. No supported +//! public API currently exists; path traversal, filtering, result types, +//! tests, and examples are intentionally absent. /* ///////////////////////////// end of file //////////////////////////// */ -