├── .github ├── FUNDING.yml └── workflows │ ├── release-plz.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── assets ├── qdir.gif └── qdir.tape ├── cliff.toml ├── release-plz.toml ├── src ├── lib.rs ├── main.rs ├── name │ └── mod.rs └── pet │ └── mod.rs └── tests └── main_test.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: k3ii 2 | buy_me_a_coffee: jainramchurn 3 | -------------------------------------------------------------------------------- /.github/workflows/release-plz.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Deployment 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | release-plz: 10 | name: Release-plz 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | token: ${{ secrets.RELEASE_PLZ_TOKEN }} 18 | 19 | - name: Install Rust toolchain 20 | uses: dtolnay/rust-toolchain@stable 21 | 22 | - name: Run release-plz 23 | uses: MarcoIeni/release-plz-action@v0.5 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }} 26 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 27 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by cargo-dist: https://opensource.axo.dev/cargo-dist/ 2 | # 3 | # Copyright 2022-2024, axodotdev 4 | # SPDX-License-Identifier: MIT or Apache-2.0 5 | # 6 | # CI that: 7 | # 8 | # * checks for a Git Tag that looks like a release 9 | # * builds artifacts with cargo-dist (archives, installers, hashes) 10 | # * uploads those artifacts to temporary workflow zip 11 | # * on success, uploads the artifacts to a GitHub Release 12 | # 13 | # Note that the GitHub Release will be created with a generated 14 | # title/body based on your changelogs. 15 | 16 | name: Release 17 | permissions: 18 | "contents": "write" 19 | 20 | # This task will run whenever you push a git tag that looks like a version 21 | # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. 22 | # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where 23 | # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION 24 | # must be a Cargo-style SemVer Version (must have at least major.minor.patch). 25 | # 26 | # If PACKAGE_NAME is specified, then the announcement will be for that 27 | # package (erroring out if it doesn't have the given version or isn't cargo-dist-able). 28 | # 29 | # If PACKAGE_NAME isn't specified, then the announcement will be for all 30 | # (cargo-dist-able) packages in the workspace with that version (this mode is 31 | # intended for workspaces with only one dist-able package, or with all dist-able 32 | # packages versioned/released in lockstep). 33 | # 34 | # If you push multiple tags at once, separate instances of this workflow will 35 | # spin up, creating an independent announcement for each one. However, GitHub 36 | # will hard limit this to 3 tags per commit, as it will assume more tags is a 37 | # mistake. 38 | # 39 | # If there's a prerelease-style suffix to the version, then the release(s) 40 | # will be marked as a prerelease. 41 | on: 42 | pull_request: 43 | push: 44 | tags: 45 | - '**[0-9]+.[0-9]+.[0-9]+*' 46 | 47 | jobs: 48 | # Run 'cargo dist plan' (or host) to determine what tasks we need to do 49 | plan: 50 | runs-on: "ubuntu-20.04" 51 | outputs: 52 | val: ${{ steps.plan.outputs.manifest }} 53 | tag: ${{ !github.event.pull_request && github.ref_name || '' }} 54 | tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} 55 | publishing: ${{ !github.event.pull_request }} 56 | env: 57 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | steps: 59 | - uses: actions/checkout@v4 60 | with: 61 | submodules: recursive 62 | - name: Install cargo-dist 63 | # we specify bash to get pipefail; it guards against the `curl` command 64 | # failing. otherwise `sh` won't catch that `curl` returned non-0 65 | shell: bash 66 | run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.21.1/cargo-dist-installer.sh | sh" 67 | - name: Cache cargo-dist 68 | uses: actions/upload-artifact@v4 69 | with: 70 | name: cargo-dist-cache 71 | path: ~/.cargo/bin/cargo-dist 72 | # sure would be cool if github gave us proper conditionals... 73 | # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible 74 | # functionality based on whether this is a pull_request, and whether it's from a fork. 75 | # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* 76 | # but also really annoying to build CI around when it needs secrets to work right.) 77 | - id: plan 78 | run: | 79 | cargo dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json 80 | echo "cargo dist ran successfully" 81 | cat plan-dist-manifest.json 82 | echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" 83 | - name: "Upload dist-manifest.json" 84 | uses: actions/upload-artifact@v4 85 | with: 86 | name: artifacts-plan-dist-manifest 87 | path: plan-dist-manifest.json 88 | 89 | # Build and packages all the platform-specific things 90 | build-local-artifacts: 91 | name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) 92 | # Let the initial task tell us to not run (currently very blunt) 93 | needs: 94 | - plan 95 | if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} 96 | strategy: 97 | fail-fast: false 98 | # Target platforms/runners are computed by cargo-dist in create-release. 99 | # Each member of the matrix has the following arguments: 100 | # 101 | # - runner: the github runner 102 | # - dist-args: cli flags to pass to cargo dist 103 | # - install-dist: expression to run to install cargo-dist on the runner 104 | # 105 | # Typically there will be: 106 | # - 1 "global" task that builds universal installers 107 | # - N "local" tasks that build each platform's binaries and platform-specific installers 108 | matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} 109 | runs-on: ${{ matrix.runner }} 110 | env: 111 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 112 | BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json 113 | steps: 114 | - name: enable windows longpaths 115 | run: | 116 | git config --global core.longpaths true 117 | - uses: actions/checkout@v4 118 | with: 119 | submodules: recursive 120 | - name: Install cargo-dist 121 | run: ${{ matrix.install_dist }} 122 | # Get the dist-manifest 123 | - name: Fetch local artifacts 124 | uses: actions/download-artifact@v4 125 | with: 126 | pattern: artifacts-* 127 | path: target/distrib/ 128 | merge-multiple: true 129 | - name: Install dependencies 130 | run: | 131 | ${{ matrix.packages_install }} 132 | - name: Build artifacts 133 | run: | 134 | # Actually do builds and make zips and whatnot 135 | cargo dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json 136 | echo "cargo dist ran successfully" 137 | - id: cargo-dist 138 | name: Post-build 139 | # We force bash here just because github makes it really hard to get values up 140 | # to "real" actions without writing to env-vars, and writing to env-vars has 141 | # inconsistent syntax between shell and powershell. 142 | shell: bash 143 | run: | 144 | # Parse out what we just built and upload it to scratch storage 145 | echo "paths<> "$GITHUB_OUTPUT" 146 | jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" 147 | echo "EOF" >> "$GITHUB_OUTPUT" 148 | 149 | cp dist-manifest.json "$BUILD_MANIFEST_NAME" 150 | - name: "Upload artifacts" 151 | uses: actions/upload-artifact@v4 152 | with: 153 | name: artifacts-build-local-${{ join(matrix.targets, '_') }} 154 | path: | 155 | ${{ steps.cargo-dist.outputs.paths }} 156 | ${{ env.BUILD_MANIFEST_NAME }} 157 | 158 | # Build and package all the platform-agnostic(ish) things 159 | build-global-artifacts: 160 | needs: 161 | - plan 162 | - build-local-artifacts 163 | runs-on: "ubuntu-20.04" 164 | env: 165 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 166 | BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json 167 | steps: 168 | - uses: actions/checkout@v4 169 | with: 170 | submodules: recursive 171 | - name: Install cached cargo-dist 172 | uses: actions/download-artifact@v4 173 | with: 174 | name: cargo-dist-cache 175 | path: ~/.cargo/bin/ 176 | - run: chmod +x ~/.cargo/bin/cargo-dist 177 | # Get all the local artifacts for the global tasks to use (for e.g. checksums) 178 | - name: Fetch local artifacts 179 | uses: actions/download-artifact@v4 180 | with: 181 | pattern: artifacts-* 182 | path: target/distrib/ 183 | merge-multiple: true 184 | - id: cargo-dist 185 | shell: bash 186 | run: | 187 | cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json 188 | echo "cargo dist ran successfully" 189 | 190 | # Parse out what we just built and upload it to scratch storage 191 | echo "paths<> "$GITHUB_OUTPUT" 192 | jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" 193 | echo "EOF" >> "$GITHUB_OUTPUT" 194 | 195 | cp dist-manifest.json "$BUILD_MANIFEST_NAME" 196 | - name: "Upload artifacts" 197 | uses: actions/upload-artifact@v4 198 | with: 199 | name: artifacts-build-global 200 | path: | 201 | ${{ steps.cargo-dist.outputs.paths }} 202 | ${{ env.BUILD_MANIFEST_NAME }} 203 | # Determines if we should publish/announce 204 | host: 205 | needs: 206 | - plan 207 | - build-local-artifacts 208 | - build-global-artifacts 209 | # Only run if we're "publishing", and only if local and global didn't fail (skipped is fine) 210 | if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} 211 | env: 212 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 213 | runs-on: "ubuntu-20.04" 214 | outputs: 215 | val: ${{ steps.host.outputs.manifest }} 216 | steps: 217 | - uses: actions/checkout@v4 218 | with: 219 | submodules: recursive 220 | - name: Install cached cargo-dist 221 | uses: actions/download-artifact@v4 222 | with: 223 | name: cargo-dist-cache 224 | path: ~/.cargo/bin/ 225 | - run: chmod +x ~/.cargo/bin/cargo-dist 226 | # Fetch artifacts from scratch-storage 227 | - name: Fetch artifacts 228 | uses: actions/download-artifact@v4 229 | with: 230 | pattern: artifacts-* 231 | path: target/distrib/ 232 | merge-multiple: true 233 | - id: host 234 | shell: bash 235 | run: | 236 | cargo dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json 237 | echo "artifacts uploaded and released successfully" 238 | cat dist-manifest.json 239 | echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" 240 | - name: "Upload dist-manifest.json" 241 | uses: actions/upload-artifact@v4 242 | with: 243 | # Overwrite the previous copy 244 | name: artifacts-dist-manifest 245 | path: dist-manifest.json 246 | # Create a GitHub Release while uploading all files to it 247 | - name: "Download GitHub Artifacts" 248 | uses: actions/download-artifact@v4 249 | with: 250 | pattern: artifacts-* 251 | path: artifacts 252 | merge-multiple: true 253 | - name: Cleanup 254 | run: | 255 | # Remove the granular manifests 256 | rm -f artifacts/*-dist-manifest.json 257 | - name: Create GitHub Release 258 | env: 259 | PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" 260 | ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" 261 | ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" 262 | RELEASE_COMMIT: "${{ github.sha }}" 263 | run: | 264 | # Write and read notes from a file to avoid quoting breaking things 265 | echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt 266 | 267 | gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* 268 | 269 | publish-homebrew-formula: 270 | needs: 271 | - plan 272 | - host 273 | runs-on: "ubuntu-20.04" 274 | env: 275 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 276 | PLAN: ${{ needs.plan.outputs.val }} 277 | GITHUB_USER: "axo bot" 278 | GITHUB_EMAIL: "admin+bot@axo.dev" 279 | if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} 280 | steps: 281 | - uses: actions/checkout@v4 282 | with: 283 | repository: "k3ii/homebrew-tap" 284 | token: ${{ secrets.HOMEBREW_TAP_TOKEN }} 285 | # So we have access to the formula 286 | - name: Fetch homebrew formulae 287 | uses: actions/download-artifact@v4 288 | with: 289 | pattern: artifacts-* 290 | path: Formula/ 291 | merge-multiple: true 292 | # This is extra complex because you can make your Formula name not match your app name 293 | # so we need to find releases with a *.rb file, and publish with that filename. 294 | - name: Commit formula files 295 | run: | 296 | git config --global user.name "${GITHUB_USER}" 297 | git config --global user.email "${GITHUB_EMAIL}" 298 | 299 | for release in $(echo "$PLAN" | jq --compact-output '.releases[] | select([.artifacts[] | endswith(".rb")] | any)'); do 300 | filename=$(echo "$release" | jq '.artifacts[] | select(endswith(".rb"))' --raw-output) 301 | name=$(echo "$filename" | sed "s/\.rb$//") 302 | version=$(echo "$release" | jq .app_version --raw-output) 303 | 304 | git add "Formula/${filename}" 305 | git commit -m "${name} ${version}" 306 | done 307 | git push 308 | 309 | announce: 310 | needs: 311 | - plan 312 | - host 313 | - publish-homebrew-formula 314 | # use "always() && ..." to allow us to wait for all publish jobs while 315 | # still allowing individual publish jobs to skip themselves (for prereleases). 316 | # "host" however must run to completion, no skipping allowed! 317 | if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} 318 | runs-on: "ubuntu-20.04" 319 | env: 320 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 321 | steps: 322 | - uses: actions/checkout@v4 323 | with: 324 | submodules: recursive 325 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## 0.1.2 - 2024-09-02 6 | 7 | ### 🚀 Features 8 | 9 | - Create multiple directories at the same level ([9e1399b](9e1399b476a187893be1820bb354d152d3044ad2)) 10 | 11 | ### 📚 Documentation 12 | 13 | - Add contributing, acknowledgement and support ([16cb794](16cb7949f74d30aa5b8a729c3f278675a3bc0091)) 14 | 15 | 16 | ## 0.1.1 - 2024-09-01 17 | 18 | ### 📚 Documentation 19 | 20 | - Added installation section in the readme.md ([c0bed47](c0bed4799c6ef0dbbf2acc20490b1d53bcd59bd3)) 21 | 22 | 23 | ## 0.0.1-rc.2 - 2024-09-01 24 | 25 | ### ⚙️ Miscellaneous Tasks 26 | 27 | - Release ([1d7756f](1d7756f163b275235aa4514a642b1c26cac04a72)) 28 | 29 | 30 | ## 0.0.1-rc.1 - 2024-09-01 31 | 32 | ### 🚀 Features 33 | 34 | - Bootstrap qdir ([ab785e7](ab785e7f7fdedacbb8de638e0bc164fe64020200)) 35 | - Add module pet and name ([9fbcfd0](9fbcfd024ad0a79f0805a74d61a9963489062d97)) 36 | - Parse value directly via method copied() ([5aca119](5aca11984e7ec939c02f1b5ebf52c82cb98b913a)) 37 | - Update function make_dir to use tmp ([00372e4](00372e49f498d27bfe8b150d1b107acc56c1fdd0)) 38 | - Add flag tmp ([12a8365](12a83651ffd31a5355eba5b00f5632ffee00d07d)) 39 | - Add version and author to clap's command ([aca2cfb](aca2cfbc6bf7dacff580649cf95f50aa2f433d2b)) 40 | 41 | ### 🐛 Bug Fixes 42 | 43 | - Use get_flag() method ([a2a56df](a2a56df1b5abff0ef7850de9d5838ef4680cfa70)) 44 | - Use value_parser ([ae46364](ae4636451cdbfdb0c2add7a3407f850a4277659a)) 45 | - Flags name, pet, and length cannot be used together ([b42c158](b42c15804c5ba3ba29a2069a927e3be17bcc03ad)) 46 | - Set correct category slugs ([3d59325](3d59325c1e0777bfff9d16971d75e69ff78173e7)) 47 | - Flags name, pet, and length cannot be used together ([bad1d5f](bad1d5ff8484eed7b6a53ea2f1324c33a6a525f8)) 48 | 49 | ### 🚜 Refactor 50 | 51 | - Remove debugging lines ([0c2967a](0c2967aea468ea53e02d65767b8a80a67ea44a6f)) 52 | - Remove debugging lines ([af97b5d](af97b5d65b061b4c6b038eabd8ab9d07a929cfd7)) 53 | 54 | ### ⚙️ Miscellaneous Tasks 55 | 56 | - Add details to cargo.toml ([ccb617f](ccb617f52caf54f33778551b15a4ab250bb76057)) 57 | - Add cargo-dist ([7d9416c](7d9416c64278b2c3231366aaf4f795cc20f60e44)) 58 | - Add cliff.toml for changelog ([3b4435d](3b4435d6fab9d87049b0f875b504be616d4548e8)) 59 | - Add release-plz ([be6d6aa](be6d6aa5c79c5802c2fccb409c8451ae3d96739a)) 60 | - Add License ([10200a7](10200a7a9a40507483ad761bd7453f1f034af515)) 61 | 62 | 63 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anstream" 16 | version = "0.6.15" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 19 | dependencies = [ 20 | "anstyle", 21 | "anstyle-parse", 22 | "anstyle-query", 23 | "anstyle-wincon", 24 | "colorchoice", 25 | "is_terminal_polyfill", 26 | "utf8parse", 27 | ] 28 | 29 | [[package]] 30 | name = "anstyle" 31 | version = "1.0.8" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 34 | 35 | [[package]] 36 | name = "anstyle-parse" 37 | version = "0.2.5" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 40 | dependencies = [ 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-query" 46 | version = "1.1.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 49 | dependencies = [ 50 | "windows-sys 0.52.0", 51 | ] 52 | 53 | [[package]] 54 | name = "anstyle-wincon" 55 | version = "3.0.4" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 58 | dependencies = [ 59 | "anstyle", 60 | "windows-sys 0.52.0", 61 | ] 62 | 63 | [[package]] 64 | name = "assert_cmd" 65 | version = "2.0.16" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" 68 | dependencies = [ 69 | "anstyle", 70 | "bstr", 71 | "doc-comment", 72 | "libc", 73 | "predicates", 74 | "predicates-core", 75 | "predicates-tree", 76 | "wait-timeout", 77 | ] 78 | 79 | [[package]] 80 | name = "autocfg" 81 | version = "1.3.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 84 | 85 | [[package]] 86 | name = "bitflags" 87 | version = "2.6.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 90 | 91 | [[package]] 92 | name = "bstr" 93 | version = "1.10.0" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" 96 | dependencies = [ 97 | "memchr", 98 | "regex-automata", 99 | "serde", 100 | ] 101 | 102 | [[package]] 103 | name = "byteorder" 104 | version = "1.5.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 107 | 108 | [[package]] 109 | name = "cfg-if" 110 | version = "1.0.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 113 | 114 | [[package]] 115 | name = "clap" 116 | version = "4.5.16" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" 119 | dependencies = [ 120 | "clap_builder", 121 | "clap_derive", 122 | ] 123 | 124 | [[package]] 125 | name = "clap_builder" 126 | version = "4.5.15" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" 129 | dependencies = [ 130 | "anstream", 131 | "anstyle", 132 | "clap_lex", 133 | "strsim", 134 | ] 135 | 136 | [[package]] 137 | name = "clap_derive" 138 | version = "4.5.13" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" 141 | dependencies = [ 142 | "heck", 143 | "proc-macro2", 144 | "quote", 145 | "syn", 146 | ] 147 | 148 | [[package]] 149 | name = "clap_lex" 150 | version = "0.7.2" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 153 | 154 | [[package]] 155 | name = "colorchoice" 156 | version = "1.0.2" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 159 | 160 | [[package]] 161 | name = "difflib" 162 | version = "0.4.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" 165 | 166 | [[package]] 167 | name = "doc-comment" 168 | version = "0.3.3" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 171 | 172 | [[package]] 173 | name = "errno" 174 | version = "0.3.9" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 177 | dependencies = [ 178 | "libc", 179 | "windows-sys 0.52.0", 180 | ] 181 | 182 | [[package]] 183 | name = "fastrand" 184 | version = "2.1.1" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" 187 | 188 | [[package]] 189 | name = "float-cmp" 190 | version = "0.9.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" 193 | dependencies = [ 194 | "num-traits", 195 | ] 196 | 197 | [[package]] 198 | name = "getrandom" 199 | version = "0.2.15" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 202 | dependencies = [ 203 | "cfg-if", 204 | "libc", 205 | "wasi", 206 | ] 207 | 208 | [[package]] 209 | name = "heck" 210 | version = "0.5.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 213 | 214 | [[package]] 215 | name = "is_terminal_polyfill" 216 | version = "1.70.1" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 219 | 220 | [[package]] 221 | name = "libc" 222 | version = "0.2.158" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" 225 | 226 | [[package]] 227 | name = "linux-raw-sys" 228 | version = "0.4.14" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 231 | 232 | [[package]] 233 | name = "memchr" 234 | version = "2.7.4" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 237 | 238 | [[package]] 239 | name = "normalize-line-endings" 240 | version = "0.3.0" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" 243 | 244 | [[package]] 245 | name = "num-traits" 246 | version = "0.2.19" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 249 | dependencies = [ 250 | "autocfg", 251 | ] 252 | 253 | [[package]] 254 | name = "once_cell" 255 | version = "1.19.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 258 | 259 | [[package]] 260 | name = "ppv-lite86" 261 | version = "0.2.20" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 264 | dependencies = [ 265 | "zerocopy", 266 | ] 267 | 268 | [[package]] 269 | name = "predicates" 270 | version = "3.1.2" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" 273 | dependencies = [ 274 | "anstyle", 275 | "difflib", 276 | "float-cmp", 277 | "normalize-line-endings", 278 | "predicates-core", 279 | "regex", 280 | ] 281 | 282 | [[package]] 283 | name = "predicates-core" 284 | version = "1.0.8" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" 287 | 288 | [[package]] 289 | name = "predicates-tree" 290 | version = "1.0.11" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" 293 | dependencies = [ 294 | "predicates-core", 295 | "termtree", 296 | ] 297 | 298 | [[package]] 299 | name = "proc-macro2" 300 | version = "1.0.86" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 303 | dependencies = [ 304 | "unicode-ident", 305 | ] 306 | 307 | [[package]] 308 | name = "qdir" 309 | version = "0.1.2" 310 | dependencies = [ 311 | "assert_cmd", 312 | "clap", 313 | "predicates", 314 | "rand", 315 | "tempfile", 316 | ] 317 | 318 | [[package]] 319 | name = "quote" 320 | version = "1.0.37" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 323 | dependencies = [ 324 | "proc-macro2", 325 | ] 326 | 327 | [[package]] 328 | name = "rand" 329 | version = "0.8.5" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 332 | dependencies = [ 333 | "libc", 334 | "rand_chacha", 335 | "rand_core", 336 | ] 337 | 338 | [[package]] 339 | name = "rand_chacha" 340 | version = "0.3.1" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 343 | dependencies = [ 344 | "ppv-lite86", 345 | "rand_core", 346 | ] 347 | 348 | [[package]] 349 | name = "rand_core" 350 | version = "0.6.4" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 353 | dependencies = [ 354 | "getrandom", 355 | ] 356 | 357 | [[package]] 358 | name = "regex" 359 | version = "1.10.6" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" 362 | dependencies = [ 363 | "aho-corasick", 364 | "memchr", 365 | "regex-automata", 366 | "regex-syntax", 367 | ] 368 | 369 | [[package]] 370 | name = "regex-automata" 371 | version = "0.4.7" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 374 | dependencies = [ 375 | "aho-corasick", 376 | "memchr", 377 | "regex-syntax", 378 | ] 379 | 380 | [[package]] 381 | name = "regex-syntax" 382 | version = "0.8.4" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 385 | 386 | [[package]] 387 | name = "rustix" 388 | version = "0.38.35" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" 391 | dependencies = [ 392 | "bitflags", 393 | "errno", 394 | "libc", 395 | "linux-raw-sys", 396 | "windows-sys 0.52.0", 397 | ] 398 | 399 | [[package]] 400 | name = "serde" 401 | version = "1.0.209" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" 404 | dependencies = [ 405 | "serde_derive", 406 | ] 407 | 408 | [[package]] 409 | name = "serde_derive" 410 | version = "1.0.209" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" 413 | dependencies = [ 414 | "proc-macro2", 415 | "quote", 416 | "syn", 417 | ] 418 | 419 | [[package]] 420 | name = "strsim" 421 | version = "0.11.1" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 424 | 425 | [[package]] 426 | name = "syn" 427 | version = "2.0.77" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" 430 | dependencies = [ 431 | "proc-macro2", 432 | "quote", 433 | "unicode-ident", 434 | ] 435 | 436 | [[package]] 437 | name = "tempfile" 438 | version = "3.12.0" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" 441 | dependencies = [ 442 | "cfg-if", 443 | "fastrand", 444 | "once_cell", 445 | "rustix", 446 | "windows-sys 0.59.0", 447 | ] 448 | 449 | [[package]] 450 | name = "termtree" 451 | version = "0.4.1" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" 454 | 455 | [[package]] 456 | name = "unicode-ident" 457 | version = "1.0.12" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 460 | 461 | [[package]] 462 | name = "utf8parse" 463 | version = "0.2.2" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 466 | 467 | [[package]] 468 | name = "wait-timeout" 469 | version = "0.2.0" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 472 | dependencies = [ 473 | "libc", 474 | ] 475 | 476 | [[package]] 477 | name = "wasi" 478 | version = "0.11.0+wasi-snapshot-preview1" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 481 | 482 | [[package]] 483 | name = "windows-sys" 484 | version = "0.52.0" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 487 | dependencies = [ 488 | "windows-targets", 489 | ] 490 | 491 | [[package]] 492 | name = "windows-sys" 493 | version = "0.59.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 496 | dependencies = [ 497 | "windows-targets", 498 | ] 499 | 500 | [[package]] 501 | name = "windows-targets" 502 | version = "0.52.6" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 505 | dependencies = [ 506 | "windows_aarch64_gnullvm", 507 | "windows_aarch64_msvc", 508 | "windows_i686_gnu", 509 | "windows_i686_gnullvm", 510 | "windows_i686_msvc", 511 | "windows_x86_64_gnu", 512 | "windows_x86_64_gnullvm", 513 | "windows_x86_64_msvc", 514 | ] 515 | 516 | [[package]] 517 | name = "windows_aarch64_gnullvm" 518 | version = "0.52.6" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 521 | 522 | [[package]] 523 | name = "windows_aarch64_msvc" 524 | version = "0.52.6" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 527 | 528 | [[package]] 529 | name = "windows_i686_gnu" 530 | version = "0.52.6" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 533 | 534 | [[package]] 535 | name = "windows_i686_gnullvm" 536 | version = "0.52.6" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 539 | 540 | [[package]] 541 | name = "windows_i686_msvc" 542 | version = "0.52.6" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 545 | 546 | [[package]] 547 | name = "windows_x86_64_gnu" 548 | version = "0.52.6" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 551 | 552 | [[package]] 553 | name = "windows_x86_64_gnullvm" 554 | version = "0.52.6" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 557 | 558 | [[package]] 559 | name = "windows_x86_64_msvc" 560 | version = "0.52.6" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 563 | 564 | [[package]] 565 | name = "zerocopy" 566 | version = "0.7.35" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 569 | dependencies = [ 570 | "byteorder", 571 | "zerocopy-derive", 572 | ] 573 | 574 | [[package]] 575 | name = "zerocopy-derive" 576 | version = "0.7.35" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 579 | dependencies = [ 580 | "proc-macro2", 581 | "quote", 582 | "syn", 583 | ] 584 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qdir" 3 | version = "0.1.2" 4 | edition = "2021" 5 | authors = ["Jain Ramchurn"] 6 | description = "A quick directory generator" 7 | license-file = "LICENSE" 8 | repository = "https://github.com/k3ii/qdir" 9 | homepage = "https://github.com/k3ii/qdir" 10 | readme = "README.md" 11 | keywords = ["cli"] 12 | categories = ["command-line-utilities"] 13 | 14 | [dependencies] 15 | clap = { version = "4.5.16", features = ["derive", "cargo"] } 16 | rand = "0.8.5" 17 | 18 | [dev-dependencies] 19 | assert_cmd = "2.0.16" 20 | tempfile = "3.12.0" 21 | predicates = "3.1.2" 22 | 23 | 24 | # The profile that 'cargo dist' will build with 25 | [profile.dist] 26 | inherits = "release" 27 | lto = "thin" 28 | 29 | # Config for 'cargo dist' 30 | [workspace.metadata.dist] 31 | # The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax) 32 | cargo-dist-version = "0.21.1" 33 | # CI backends to support 34 | ci = "github" 35 | # The installers to generate for each app 36 | installers = ["shell", "powershell", "homebrew"] 37 | # A GitHub repo to push Homebrew formulas to 38 | tap = "k3ii/homebrew-tap" 39 | # Target platforms to build apps for (Rust target-triple syntax) 40 | targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] 41 | # Path that installers should place binaries in 42 | install-path = "CARGO_HOME" 43 | # Publish jobs to run in CI 44 | publish-jobs = ["homebrew"] 45 | # Whether to install an updater program 46 | install-updater = true 47 | # Ignore out-of-date contents 48 | allow-dirty = ["ci"] 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | "THE BEER-WARE LICENSE" (Revision 42): 2 | 3 | wrote this file. As long as you retain this notice you 4 | can do whatever you want with this stuff. If we meet some day, and you think 5 | this stuff is worth it, you can buy me a beer in return. 6 | 7 | Jain Ramchurn 8 | Dereck Lam Hon Wah 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qdir - Quick Directory Creator 2 | 3 | `qdir` is a command-line tool for creating directories with randomly generated names. It's useful when you need a quick directory without thinking about names. 4 | 5 | ![qdir demo](./assets/qdir.gif) 6 | 7 | ## Features 8 | 9 | - Create directories with random alphanumeric names 10 | - Optional naming themes: scientists/engineers or pet names 11 | - Create nested directories with controllable depth 12 | - Use system temp directory or current working directory 13 | 14 | ## Usage 15 | 16 | Basic: `qdir` 17 | 18 | With options: qdir [options] 19 | 20 | ## Options 21 | 22 | - `-d, --depth ` Set depth for nested directories 23 | - `-l, --length ` Set length for random string 24 | - `-n, --name` Use names instead of random string 25 | - `-p, --pet` Use pets instead of random string 26 | - `-t, --tmp` Use the system's temporary directory 27 | - `-h, --help` Print help 28 | - `-V, --version` Print version 29 | 30 | ## Installation 31 | 32 | You can install `qdir` using `cargo`: 33 | 34 | ```sh 35 | cargo install qdir 36 | ``` 37 | 38 | Or using `brew`: 39 | 40 | ```sh 41 | brew install k3ii/tap/qdir 42 | ``` 43 | 44 | **Check the [release page](https://github.com/k3ii/qdir/releases) to install the pre-built binaries.** 45 | 46 | ## Contributing 47 | 48 | Contributions to Qdir are welcome! Please feel free to submit a Pull Request. 49 | 50 | ## Acknowledgments 51 | 52 | * Inspired by Docker's naming generator for containers 53 | * Thanks to all the scientists and technologists whose names are used in this project 54 | 55 | ## Support 56 | If you encounter any problems or have any questions, please open an issue on the GitHub repository. 57 | -------------------------------------------------------------------------------- /assets/qdir.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k3ii/qdir/ad7d08f7782c54a5c45054eb0af32095009ff6ac/assets/qdir.gif -------------------------------------------------------------------------------- /assets/qdir.tape: -------------------------------------------------------------------------------- 1 | Set Shell zsh 2 | Type "qdir" 3 | Sleep 500ms 4 | Enter 5 | Type "qd" 6 | Sleep 500ms 7 | Type "ir -p " 8 | Sleep 500ms 9 | Type "-d4 " 10 | Sleep 500ms 11 | Enter 12 | Sleep 500ms 13 | Type "qdir -n -m " 14 | Sleep 1.5s 15 | Type "-" 16 | Sleep 500ms 17 | Backspace 18 | Type "4 " 19 | Sleep 500ms 20 | Type "-d 2" 21 | Enter 22 | Sleep 1.5s 23 | Ctrl+D 24 | 25 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # git-cliff ~ default configuration file 2 | # https://git-cliff.org/docs/configuration 3 | # 4 | # Lines starting with "#" are comments. 5 | # Configuration options are organized into tables and keys. 6 | # See documentation for more information on available options. 7 | 8 | [changelog] 9 | # template for the changelog footer 10 | header = """ 11 | # Changelog\n 12 | All notable changes to this project will be documented in this file.\n 13 | """ 14 | # template for the changelog body 15 | # https://keats.github.io/tera/docs/#introduction 16 | body = """ 17 | {% if version %}\ 18 | ## {{ version | trim_start_matches(pat="v") }} - {{ timestamp | date(format="%Y-%m-%d") }} 19 | {% else %}\ 20 | ## Unreleased 21 | {% endif %}\ 22 | {% if previous %}\ 23 | {% if previous.commit_id and commit_id %} 24 | [{{ previous.commit_id | truncate(length=7, end="") }}]({{ previous.commit_id }})...\ 25 | [{{ commit_id | truncate(length=7, end="") }}]({{ commit_id }}) 26 | {% endif %}\ 27 | {% endif %}\ 28 | {% for group, commits in commits | group_by(attribute="group") %} 29 | ### {{ group | upper_first }} 30 | {% for commit in commits %} 31 | - {{ commit.message | split(pat="\n") | first | upper_first | trim }} ([{{ commit.id | truncate(length=7, end="") }}]({{ commit.id }}))\ 32 | {% for footer in commit.footers -%} 33 | , {{ footer.token }}{{ footer.separator }}{{ footer.value }}\ 34 | {% endfor %}\ 35 | {% endfor %} 36 | {% endfor %}\n 37 | """ 38 | # template for the changelog footer 39 | footer = """ 40 | 41 | """ 42 | # remove the leading and trailing s 43 | trim = true 44 | # postprocessors 45 | postprocessors = [ 46 | # { pattern = '', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL 47 | ] 48 | 49 | [git] 50 | # parse the commits based on https://www.conventionalcommits.org 51 | conventional_commits = true 52 | # filter out the commits that are not conventional 53 | filter_unconventional = true 54 | # process each line of a commit as an individual commit 55 | split_commits = false 56 | # regex for preprocessing the commit messages 57 | commit_preprocessors = [ 58 | # Replace issue numbers 59 | #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, 60 | # Check spelling of the commit with https://github.com/crate-ci/typos 61 | # If the spelling is incorrect, it will be automatically fixed. 62 | #{ pattern = '.*', replace_command = 'typos --write-changes -' }, 63 | ] 64 | # regex for parsing and grouping commits 65 | commit_parsers = [ 66 | { message = "^feat", group = "🚀 Features" }, 67 | { message = "^fix", group = "🐛 Bug Fixes" }, 68 | { message = "^doc", group = "📚 Documentation" }, 69 | { message = "^perf", group = "⚡ Performance" }, 70 | { message = "^refactor", group = "🚜 Refactor" }, 71 | { message = "^style", group = "🎨 Styling" }, 72 | { message = "^test", group = "🧪 Testing" }, 73 | { message = "^chore\\(release\\): prepare for", skip = true }, 74 | { message = "^chore\\(deps.*\\)", skip = true }, 75 | { message = "^chore\\(pr\\)", skip = true }, 76 | { message = "^chore\\(pull\\)", skip = true }, 77 | { message = "^chore|^ci", group = "⚙️ Miscellaneous Tasks" }, 78 | { body = ".*security", group = "🛡️ Security" }, 79 | { message = "^revert", group = "◀️ Revert" }, 80 | ] 81 | # protect breaking changes from being skipped due to matching a skipping commit_parser 82 | protect_breaking_commits = false 83 | # filter out the commits that are not matched by commit parsers 84 | filter_commits = false 85 | # regex for matching git tags 86 | # tag_pattern = "v[0-9].*" 87 | # regex for skipping tags 88 | # skip_tags = "" 89 | # regex for ignoring tags 90 | # ignore_tags = "" 91 | # sort the tags topologically 92 | topo_order = false 93 | # sort the commits inside sections by oldest/newest order 94 | sort_commits = "oldest" 95 | # limit the number of commits included in the changelog. 96 | # limit_commits = 42 97 | -------------------------------------------------------------------------------- /release-plz.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | # path of the git-cliff configuration 3 | changelog_config = "cliff.toml" 4 | 5 | # enable changelog updates 6 | changelog_update = true 7 | 8 | # update dependencies with `cargo update` 9 | dependencies_update = true 10 | 11 | # create tags for the releases 12 | git_tag_enable = true 13 | 14 | # disable GitHub releases 15 | git_release_enable = false 16 | 17 | # labels for the release PR 18 | pr_labels = ["release"] 19 | 20 | # disallow updating repositories with uncommitted changes 21 | allow_dirty = false 22 | 23 | # disallow packaging with uncommitted changes 24 | publish_allow_dirty = false 25 | 26 | # disable running `cargo-semver-checks` 27 | semver_check = false 28 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use rand::distributions::Alphanumeric; 2 | use rand::{thread_rng, Rng}; 3 | use std::fs; 4 | use std::path::PathBuf; 5 | 6 | pub mod name; 7 | pub mod pet; 8 | 9 | pub use name::get_random_name; 10 | pub use pet::get_random_pet; 11 | 12 | pub fn random(length: usize) -> String { 13 | thread_rng() 14 | .sample_iter(&Alphanumeric) 15 | .take(length) 16 | .map(char::from) 17 | .collect() 18 | } 19 | pub fn make_dir(depth: u8, name_length: usize, name: bool, pet: bool, tmp: bool, multiple: usize) { 20 | let base_path = if tmp { 21 | std::env::temp_dir() 22 | } else { 23 | PathBuf::from(".") 24 | }; 25 | 26 | let depth = if depth == 0 { 1 } else { depth }; 27 | let multiple = if multiple == 0 { 1 } else { multiple }; 28 | 29 | for _ in 0..multiple { 30 | let mut path = base_path.clone(); 31 | for _ in 0..depth { 32 | let dir_name = if name { 33 | get_random_name().unwrap().to_string() 34 | } else if pet { 35 | get_random_pet().unwrap().to_string() 36 | } else { 37 | random(name_length) 38 | }; 39 | path.push(dir_name); 40 | } 41 | fs::create_dir_all(&path).expect("Failed to create directory"); 42 | println!("{}", path.display()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{command, Arg, ArgGroup}; 2 | use qdir::make_dir; 3 | 4 | fn main() { 5 | let matches = command!() 6 | .version("0.1.0") 7 | .author("Jain Ramchurn") 8 | .about("Quick Directory Generator") 9 | .arg( 10 | Arg::new("depth") 11 | .short('d') 12 | .long("depth") 13 | .value_parser(clap::value_parser!(u8)) 14 | .default_value("0"), 15 | ) 16 | .arg( 17 | Arg::new("length") 18 | .short('l') 19 | .long("length") 20 | .value_parser(clap::value_parser!(usize)) 21 | .default_value("6"), 22 | ) 23 | .arg( 24 | Arg::new("name") 25 | .short('n') 26 | .long("name") 27 | .action(clap::ArgAction::SetTrue) 28 | .help("Use names instead of random string"), 29 | ) 30 | .arg( 31 | Arg::new("pet") 32 | .short('p') 33 | .long("pet") 34 | .action(clap::ArgAction::SetTrue) 35 | .help("Use pets instead of random string"), 36 | ) 37 | .arg( 38 | Arg::new("tmp") 39 | .short('t') 40 | .long("tmp") 41 | .action(clap::ArgAction::SetTrue) 42 | .help("Use the system's temporary directory"), 43 | ) 44 | .arg( 45 | Arg::new("multiple") 46 | .short('m') 47 | .long("multiple") 48 | .value_parser(clap::value_parser!(usize)) 49 | .help("Create multiple directories at the same level") 50 | .default_value("1"), 51 | ) 52 | .group( 53 | ArgGroup::new("name_or_pet_length") 54 | .args(&["name", "pet", "length"]) 55 | .required(false), 56 | ) 57 | .get_matches(); 58 | 59 | let depth = matches 60 | .get_one::("depth") 61 | .copied() 62 | .expect("Default depth should be provided"); 63 | let length = matches 64 | .get_one::("length") 65 | .copied() 66 | .expect("Default length should be provided"); 67 | let multiple = matches 68 | .get_one::("multiple") 69 | .copied() 70 | .expect("Default multiple should be provided"); 71 | 72 | let use_name = matches.get_flag("name"); 73 | let use_pet = matches.get_flag("pet"); 74 | let use_tmp = matches.get_flag("tmp"); 75 | 76 | make_dir(depth, length, use_name, use_pet, use_tmp, multiple); 77 | } 78 | -------------------------------------------------------------------------------- /src/name/mod.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | 3 | const NAMES: &[&str] = &[ 4 | "lovelace", 5 | "turing", 6 | "hopper", 7 | "einstein", 8 | "curie", 9 | "davinci", 10 | "tesla", 11 | "feynman", 12 | "torvalds", 13 | "stallman", 14 | "ritchie", 15 | "knuth", 16 | "dijkstra", 17 | "wozniak", 18 | "bernerslee", 19 | "gosling", 20 | "matsumoto", 21 | "thompson", 22 | "moore", 23 | "bohr", 24 | "hawking", 25 | "ramajuan", 26 | "dawkins", 27 | "sagan", 28 | "darwin", 29 | "newton", 30 | "galileo", 31 | "diffie", 32 | "hellman", 33 | "oppenheimer", 34 | "morse", 35 | "neumann", 36 | "pike", 37 | "kernighan", 38 | ]; 39 | 40 | pub fn get_random_name() -> Option { 41 | let mut rng = rand::thread_rng(); 42 | NAMES.choose(&mut rng).map(|&name| name.to_string()) 43 | } 44 | -------------------------------------------------------------------------------- /src/pet/mod.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | 3 | const PETS: &[&str] = &[ 4 | "dog", 5 | "cat", 6 | "mouse", 7 | "cow", 8 | "rabbit", 9 | "hamster", 10 | "parrot", 11 | "goldfish", 12 | "turtle", 13 | "guinea pig", 14 | "horse", 15 | "donkey", 16 | "goat", 17 | "sheep", 18 | "pig", 19 | "chicken", 20 | "duck", 21 | "goose", 22 | "ferret", 23 | "gerbil", 24 | "canary", 25 | "parakeet", 26 | "iguana", 27 | "gecko", 28 | ]; 29 | 30 | pub fn get_random_pet() -> Option<&'static str> { 31 | let mut rng = rand::thread_rng(); 32 | PETS.choose(&mut rng).copied() 33 | } 34 | -------------------------------------------------------------------------------- /tests/main_test.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | use std::fs; 3 | use tempfile::tempdir; 4 | 5 | #[test] 6 | fn test_multiple_directories() { 7 | let temp_dir = tempdir().unwrap(); 8 | let temp_path = temp_dir.path().to_str().unwrap(); 9 | 10 | let mut cmd = Command::cargo_bin("qdir").unwrap(); 11 | cmd.current_dir(temp_path) 12 | .arg("-m") 13 | .arg("3") 14 | .assert() 15 | .success() 16 | .stdout(predicates::str::contains("./")); 17 | 18 | let entries: Vec<_> = fs::read_dir(temp_path).unwrap().collect(); 19 | assert_eq!(entries.len(), 3); 20 | } 21 | 22 | #[test] 23 | fn test_pet_named_directories() { 24 | let temp_dir = tempdir().unwrap(); 25 | let temp_path = temp_dir.path().to_str().unwrap(); 26 | 27 | let mut cmd = Command::cargo_bin("qdir").unwrap(); 28 | cmd.current_dir(temp_path) 29 | .arg("-p") 30 | .assert() 31 | .success() 32 | .stdout(predicates::str::contains("./")); 33 | 34 | let entries: Vec<_> = fs::read_dir(temp_path).unwrap().collect(); 35 | assert_eq!(entries.len(), 1); 36 | 37 | let dir_name = entries[0].as_ref().unwrap().file_name(); 38 | let name_str = dir_name.to_str().unwrap(); 39 | assert!(name_str.chars().all(char::is_alphabetic)); 40 | } 41 | 42 | #[test] 43 | fn test_named_directories() { 44 | let temp_dir = tempdir().unwrap(); 45 | let temp_path = temp_dir.path().to_str().unwrap(); 46 | 47 | let mut cmd = Command::cargo_bin("qdir").unwrap(); 48 | cmd.current_dir(temp_path) 49 | .arg("-n") 50 | .assert() 51 | .success() 52 | .stdout(predicates::str::contains("./")); 53 | 54 | let entries: Vec<_> = fs::read_dir(temp_path).unwrap().collect(); 55 | assert_eq!(entries.len(), 1); 56 | 57 | let dir_name = entries[0].as_ref().unwrap().file_name(); 58 | let name_str = dir_name.to_str().unwrap(); 59 | assert!(name_str.chars().all(char::is_alphabetic)); 60 | } 61 | 62 | #[test] 63 | fn test_depth_directories() { 64 | let temp_dir = tempdir().unwrap(); 65 | let temp_path = temp_dir.path().to_str().unwrap(); 66 | 67 | let mut cmd = Command::cargo_bin("qdir").unwrap(); 68 | cmd.current_dir(temp_path) 69 | .arg("-d") 70 | .arg("2") 71 | .assert() 72 | .success() 73 | .stdout(predicates::str::contains("./")); 74 | 75 | let entries: Vec<_> = fs::read_dir(temp_path).unwrap().collect(); 76 | assert_eq!(entries.len(), 1); 77 | 78 | let first_entry = &entries[0].as_ref().unwrap().path(); 79 | assert!(first_entry.is_dir()); 80 | let nested_entries: Vec<_> = fs::read_dir(first_entry).unwrap().collect(); 81 | assert_eq!(nested_entries.len(), 1); 82 | } 83 | 84 | #[test] 85 | fn test_default_directory_creation() { 86 | let temp_dir = tempdir().unwrap(); 87 | let temp_path = temp_dir.path().to_str().unwrap(); 88 | 89 | let mut cmd = Command::cargo_bin("qdir").unwrap(); 90 | cmd.current_dir(temp_path) 91 | .assert() 92 | .success() 93 | .stdout(predicates::str::contains("./")); 94 | 95 | let entries: Vec<_> = fs::read_dir(temp_path).unwrap().collect(); 96 | assert_eq!(entries.len(), 1); 97 | } 98 | --------------------------------------------------------------------------------