├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── release.yml │ └── web.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── SECURITY.md ├── book ├── book.toml └── src │ ├── README.md │ ├── SUMMARY.md │ ├── contributing │ ├── README.md │ ├── adding-messages.md │ ├── adding-moods.md │ ├── adding-variables.md │ ├── concepts.md │ ├── docs.md │ └── releasing.md │ ├── customize │ ├── README.md │ ├── moods.md │ ├── never-nsfw.md │ ├── not-just-cargo.md │ ├── nsfw.md │ ├── roles-and-pronouns.md │ └── true-roles.md │ └── guides │ ├── README.md │ ├── bash.md │ ├── ci.md │ └── powershell.md ├── build.rs ├── favicon.ico ├── oranda.json ├── responses.json └── src └── main.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | schedule: 9 | - cron: '11 7 * * 1,4' 10 | 11 | env: 12 | RUSTFLAGS: -Dwarnings 13 | 14 | jobs: 15 | check: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: dtolnay/rust-toolchain@stable 20 | - name: Run cargo check 21 | run: | 22 | cargo check --all --tests 23 | fmt: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | - uses: dtolnay/rust-toolchain@stable 28 | with: 29 | components: rustfmt 30 | - name: Run cargo fmt 31 | run: | 32 | cargo fmt --all -- --check 33 | clippy: 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v3 37 | - uses: dtolnay/rust-toolchain@stable 38 | with: 39 | components: clippy 40 | - name: Run cargo clippy 41 | run: | 42 | cargo clippy --tests --examples 43 | docs: 44 | runs-on: ubuntu-latest 45 | env: 46 | RUSTDOCFLAGS: -Dwarnings 47 | steps: 48 | - uses: actions/checkout@v3 49 | - uses: dtolnay/rust-toolchain@stable 50 | - name: Run rustdoc 51 | run: | 52 | cargo doc --no-deps 53 | feature-check: 54 | needs: check 55 | runs-on: ubuntu-latest 56 | steps: 57 | - uses: actions/checkout@v3 58 | - uses: dtolnay/rust-toolchain@stable 59 | - uses: taiki-e/install-action@cargo-hack 60 | - name: Run cargo hack powerset 61 | run: | 62 | cargo hack check --feature-powerset --no-dev-deps 63 | os-test: 64 | runs-on: ${{ matrix.os }} 65 | strategy: 66 | matrix: 67 | os: [ubuntu-latest, windows-latest, macOS-latest] 68 | # note that bare "--no-default-features" isn't supported, you must pick a backend! 69 | feature-flags: ["--no-default-features", "", "--all-features"] 70 | steps: 71 | - uses: actions/checkout@v3 72 | - uses: dtolnay/rust-toolchain@stable 73 | - name: Run cargo test 74 | run: | 75 | cargo test ${{ matrix.feature-flags }} -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022-2023, axodotdev 2 | # SPDX-License-Identifier: MIT or Apache-2.0 3 | # 4 | # CI that: 5 | # 6 | # * checks for a Git Tag that looks like a release 7 | # * builds artifacts with cargo-dist (archives, installers, hashes) 8 | # * uploads those artifacts to temporary workflow zip 9 | # * on success, uploads the artifacts to a Github Release™ 10 | # 11 | # Note that the Github Release™ will be created with a generated 12 | # title/body based on your changelogs. 13 | name: Release 14 | 15 | permissions: 16 | contents: write 17 | 18 | # This task will run whenever you push a git tag that looks like a version 19 | # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. 20 | # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where 21 | # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION 22 | # must be a Cargo-style SemVer Version (must have at least major.minor.patch). 23 | # 24 | # If PACKAGE_NAME is specified, then the release will be for that 25 | # package (erroring out if it doesn't have the given version or isn't cargo-dist-able). 26 | # 27 | # If PACKAGE_NAME isn't specified, then the release will be for all 28 | # (cargo-dist-able) packages in the workspace with that version (this mode is 29 | # intended for workspaces with only one dist-able package, or with all dist-able 30 | # packages versioned/released in lockstep). 31 | # 32 | # If you push multiple tags at once, separate instances of this workflow will 33 | # spin up, creating an independent Github Release™ for each one. However Github 34 | # will hard limit this to 3 tags per commit, as it will assume more tags is a 35 | # mistake. 36 | # 37 | # If there's a prerelease-style suffix to the version, then the Github Release™ 38 | # will be marked as a prerelease. 39 | on: 40 | push: 41 | tags: 42 | - '**[0-9]+.[0-9]+.[0-9]+*' 43 | pull_request: 44 | 45 | jobs: 46 | # Run 'cargo dist plan' to determine what tasks we need to do 47 | plan: 48 | runs-on: ubuntu-latest 49 | outputs: 50 | val: ${{ steps.plan.outputs.manifest }} 51 | tag: ${{ !github.event.pull_request && github.ref_name || '' }} 52 | tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} 53 | publishing: ${{ !github.event.pull_request }} 54 | env: 55 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | steps: 57 | - uses: actions/checkout@v4 58 | with: 59 | submodules: recursive 60 | - name: Install cargo-dist 61 | run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.4.3/cargo-dist-installer.sh | sh" 62 | - id: plan 63 | run: | 64 | cargo dist plan ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} --output-format=json > dist-manifest.json 65 | echo "cargo dist plan ran successfully" 66 | cat dist-manifest.json 67 | echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" 68 | - name: "Upload dist-manifest.json" 69 | uses: actions/upload-artifact@v3 70 | with: 71 | name: artifacts 72 | path: dist-manifest.json 73 | 74 | # Build and packages all the platform-specific things 75 | upload-local-artifacts: 76 | # Let the initial task tell us to not run (currently very blunt) 77 | needs: plan 78 | if: ${{ fromJson(needs.plan.outputs.val).releases != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} 79 | strategy: 80 | fail-fast: false 81 | # Target platforms/runners are computed by cargo-dist in create-release. 82 | # Each member of the matrix has the following arguments: 83 | # 84 | # - runner: the github runner 85 | # - dist-args: cli flags to pass to cargo dist 86 | # - install-dist: expression to run to install cargo-dist on the runner 87 | # 88 | # Typically there will be: 89 | # - 1 "global" task that builds universal installers 90 | # - N "local" tasks that build each platform's binaries and platform-specific installers 91 | matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} 92 | runs-on: ${{ matrix.runner }} 93 | env: 94 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 95 | BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json 96 | steps: 97 | - uses: actions/checkout@v4 98 | with: 99 | submodules: recursive 100 | - uses: swatinem/rust-cache@v2 101 | - name: Install cargo-dist 102 | run: ${{ matrix.install_dist }} 103 | - name: Install dependencies 104 | run: | 105 | ${{ matrix.packages_install }} 106 | - name: Build artifacts 107 | run: | 108 | # Actually do builds and make zips and whatnot 109 | cargo dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json 110 | echo "cargo dist ran successfully" 111 | - id: cargo-dist 112 | name: Post-build 113 | # We force bash here just because github makes it really hard to get values up 114 | # to "real" actions without writing to env-vars, and writing to env-vars has 115 | # inconsistent syntax between shell and powershell. 116 | shell: bash 117 | run: | 118 | # Parse out what we just built and upload it to the Github Release™ 119 | echo "paths<> "$GITHUB_OUTPUT" 120 | jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT" 121 | echo "EOF" >> "$GITHUB_OUTPUT" 122 | 123 | cp dist-manifest.json "$BUILD_MANIFEST_NAME" 124 | - name: "Upload artifacts" 125 | uses: actions/upload-artifact@v3 126 | with: 127 | name: artifacts 128 | path: | 129 | ${{ steps.cargo-dist.outputs.paths }} 130 | ${{ env.BUILD_MANIFEST_NAME }} 131 | 132 | # Build and package all the platform-agnostic(ish) things 133 | upload-global-artifacts: 134 | needs: [plan, upload-local-artifacts] 135 | runs-on: "ubuntu-20.04" 136 | env: 137 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 138 | steps: 139 | - uses: actions/checkout@v4 140 | with: 141 | submodules: recursive 142 | - name: Install cargo-dist 143 | run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.4.3/cargo-dist-installer.sh | sh" 144 | # Get all the local artifacts for the global tasks to use (for e.g. checksums) 145 | - name: Fetch local artifacts 146 | uses: actions/download-artifact@v3 147 | with: 148 | name: artifacts 149 | path: target/distrib/ 150 | - id: cargo-dist 151 | shell: bash 152 | run: | 153 | cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json 154 | echo "cargo dist ran successfully" 155 | 156 | # Parse out what we just built and upload it to the Github Release™ 157 | echo "paths<> "$GITHUB_OUTPUT" 158 | jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT" 159 | echo "EOF" >> "$GITHUB_OUTPUT" 160 | - name: "Upload artifacts" 161 | uses: actions/upload-artifact@v3 162 | with: 163 | name: artifacts 164 | path: ${{ steps.cargo-dist.outputs.paths }} 165 | 166 | should-publish: 167 | needs: 168 | - plan 169 | - upload-local-artifacts 170 | - upload-global-artifacts 171 | if: ${{ needs.plan.outputs.publishing == 'true' }} 172 | runs-on: ubuntu-latest 173 | steps: 174 | - name: print tag 175 | run: echo "ok we're publishing!" 176 | 177 | # Create a Github Release with all the results once everything is done 178 | publish-release: 179 | needs: [plan, should-publish] 180 | runs-on: ubuntu-latest 181 | env: 182 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 183 | steps: 184 | - uses: actions/checkout@v4 185 | with: 186 | submodules: recursive 187 | - name: "Download artifacts" 188 | uses: actions/download-artifact@v3 189 | with: 190 | name: artifacts 191 | path: artifacts 192 | - name: Cleanup 193 | run: | 194 | # Remove the granular manifests 195 | rm artifacts/*-dist-manifest.json 196 | - name: Create Release 197 | uses: ncipollo/release-action@v1 198 | with: 199 | tag: ${{ needs.plan.outputs.tag }} 200 | name: ${{ fromJson(needs.plan.outputs.val).announcement_title }} 201 | body: ${{ fromJson(needs.plan.outputs.val).announcement_github_body }} 202 | prerelease: ${{ fromJson(needs.plan.outputs.val).announcement_is_prerelease }} 203 | artifacts: "artifacts/*" 204 | -------------------------------------------------------------------------------- /.github/workflows/web.yml: -------------------------------------------------------------------------------- 1 | # Workflow to build your docs with oranda (and mdbook) 2 | # and deploy them to Github Pages 3 | name: Web 4 | 5 | # We're going to push to the gh-pages branch, so we need that permission 6 | permissions: 7 | contents: write 8 | 9 | # What situations do we want to build docs in? 10 | # All of these work independently and can be removed / commented out 11 | # if you don't want oranda/mdbook running in that situation 12 | on: 13 | # Check that a PR didn't break docs! 14 | # 15 | # Note that the "Deploy to Github Pages" step won't run in this mode, 16 | # so this won't have any side-effects. But it will tell you if a PR 17 | # completely broke oranda/mdbook. Sadly we don't provide previews (yet)! 18 | pull_request: 19 | 20 | # Whenever something gets pushed to main, update the docs! 21 | # This is great for getting docs changes live without cutting a full release. 22 | # 23 | # Note that if you're using cargo-dist, this will "race" the Release workflow 24 | # that actually builds the Github Release that oranda tries to read (and 25 | # this will almost certainly complete first). As a result you will publish 26 | # docs for the latest commit but the oranda landing page won't know about 27 | # the latest release. The workflow_run trigger below will properly wait for 28 | # cargo-dist, and so this half-published state will only last for ~10 minutes. 29 | # 30 | # If you only want docs to update with releases, disable this, or change it to 31 | # a "release" branch. You can, of course, also manually trigger a workflow run 32 | # when you want the docs to update. 33 | push: 34 | branches: 35 | - main 36 | 37 | # Whenever a workflow called "Release" completes, update the docs! 38 | # 39 | # If you're using cargo-dist, this is recommended, as it will ensure that 40 | # oranda always sees the latest release right when it's available. Note 41 | # however that Github's UI is wonky when you use workflow_run, and won't 42 | # show this workflow as part of any commit. You have to go to the "actions" 43 | # tab for your repo to see this one running (the gh-pages deploy will also 44 | # only show up there). 45 | workflow_run: 46 | workflows: [ "Release" ] 47 | types: 48 | - completed 49 | 50 | # Alright, let's do it! 51 | jobs: 52 | web: 53 | name: Build and deploy site and docs 54 | runs-on: ubuntu-latest 55 | steps: 56 | # Setup 57 | - uses: actions/checkout@v3 58 | with: 59 | fetch-depth: 0 60 | - uses: dtolnay/rust-toolchain@stable 61 | - uses: swatinem/rust-cache@v2 62 | 63 | # If you use any mdbook plugins, here's the place to install them! 64 | 65 | # Install and run oranda (and mdbook) 66 | # This will write all output to ./public/ (including copying mdbook's output to there) 67 | - name: Install and run oranda 68 | run: | 69 | curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/oranda/releases/latest/download/oranda-installer.sh | sh 70 | oranda build 71 | 72 | # Deploy to our gh-pages branch (creating it if it doesn't exist) 73 | # the "public" dir that oranda made above will become the root dir 74 | # of this branch. 75 | # 76 | # Note that once the gh-pages branch exists, you must 77 | # go into repo's settings > pages and set "deploy from branch: gh-pages" 78 | # the other defaults work fine. 79 | - name: Deploy to Github Pages 80 | uses: JamesIves/github-pages-deploy-action@v4.4.1 81 | # ONLY if we're on main (so no PRs or feature branches allowed!) 82 | if: ${{ github.ref == 'refs/heads/main' }} 83 | with: 84 | branch: gh-pages 85 | # Gotta tell the action where to find oranda's output 86 | folder: public 87 | token: ${{ secrets.GITHUB_TOKEN }} 88 | single-commit: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | # Generated by `oranda generate ci` 3 | public/ 4 | # Generated by mdbook 5 | book/book/ 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Unreleased 2 | 3 | Nothing Yet! 4 | 5 | 6 | # Version 0.3.1 (2023-11-20) 7 | 8 | * [mommy now has a book~](https://faultlore.com/cargo-mommy/book/) 9 | * [mommy is easier to run with other things than cargo~](https://faultlore.com/cargo-mommy/book/customize/not-just-cargo.html) 10 | 11 | 12 | # Version 0.3.0 (2023-11-18) 13 | 14 | mommy has lots of improvements from lots of good little helpers~ 15 | 16 | * mommy is now totally data-driven, new moods and templates can be added with only the responses.json~ 17 | 18 | * 'cargo mommy i mean daddy' will turn cargo-mommy into cargo-daddy (it's not just daddy that works)~ 19 | 20 | * mommy now reflects on her true nature, so if you invoke her as cargo-daddy, she'll default to calling herself "daddy" and read config from `CARGO_DADDYS_*` (note the extra S at the end!)~ 21 | 22 | * mommy now handles colors better when piped to a file~ 23 | 24 | * mommy has lots of type fixes and new things to say to you~ 25 | 26 | * mommy can now be in an "ominous" mood~ 27 | 28 | * mommy has more docs~ 29 | 30 | * mommy now exit codes positively when you make a mistake~ 31 | 32 | * mommy now notices when she's run too many times recursively~ 33 | 34 | * mommy now has proper ci and release engineering~ 35 | 36 | * [mommy now has prebuilt binaries and curl|sh installers from cargo-dist~](https://github.com/Gankra/cargo-mommy/releases) 37 | 38 | * [mommy now has a website from oranda where you can get the prebuilt binaries~](https://faultlore.com/cargo-mommy/) 39 | 40 | 41 | 42 | # Version 0.2.1 43 | 44 | * mommy made lots of typos and they're fixed now~ 45 | 46 | * mommy has LICENSE files for pedants~ 47 | 48 | * mommy's kinks can be statically compiled out with cargo install --no-default-features (and individual non-chill moods can be turned back on with features of the same name~) 49 | 50 | * mommy's code has been cleaned up to make it easier to add new moods or templates~ 51 | 52 | 53 | 54 | # Version 0.2.0 55 | 56 | * mommy now works with flags like +nightly~ 57 | 58 | * rand replaced with fastrand, because mommy must go fast~ 59 | 60 | * mommy now respects flags like -q~ 61 | 62 | * mommy now has a build.rs so she can be EFFECIENT and parse her templates at compile time~ 63 | 64 | * mommy fixed some typos~ 65 | 66 | * mommy's emotes can be customized~ 💞 67 | 68 | * mommy can be kinky~ https://github.com/Gankra/cargo-mommy#configuration-kink 69 | 70 | 71 | 72 | # Version 0.1.1 73 | 74 | mommy forgets what she did~ 75 | 76 | 77 | # Version 0.1.0 78 | 79 | mommy's here for you~ -------------------------------------------------------------------------------- /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.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "cargo-mommy" 16 | version = "0.3.1" 17 | dependencies = [ 18 | "fastrand", 19 | "regex", 20 | "serde", 21 | "serde-tuple-vec-map", 22 | "serde_json", 23 | ] 24 | 25 | [[package]] 26 | name = "cfg-if" 27 | version = "1.0.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 30 | 31 | [[package]] 32 | name = "fastrand" 33 | version = "1.9.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 36 | dependencies = [ 37 | "instant", 38 | ] 39 | 40 | [[package]] 41 | name = "instant" 42 | version = "0.1.12" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 45 | dependencies = [ 46 | "cfg-if", 47 | ] 48 | 49 | [[package]] 50 | name = "itoa" 51 | version = "1.0.9" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 54 | 55 | [[package]] 56 | name = "memchr" 57 | version = "2.6.4" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" 60 | 61 | [[package]] 62 | name = "proc-macro2" 63 | version = "1.0.69" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" 66 | dependencies = [ 67 | "unicode-ident", 68 | ] 69 | 70 | [[package]] 71 | name = "quote" 72 | version = "1.0.33" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 75 | dependencies = [ 76 | "proc-macro2", 77 | ] 78 | 79 | [[package]] 80 | name = "regex" 81 | version = "1.10.2" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" 84 | dependencies = [ 85 | "aho-corasick", 86 | "memchr", 87 | "regex-automata", 88 | "regex-syntax", 89 | ] 90 | 91 | [[package]] 92 | name = "regex-automata" 93 | version = "0.4.3" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" 96 | dependencies = [ 97 | "aho-corasick", 98 | "memchr", 99 | "regex-syntax", 100 | ] 101 | 102 | [[package]] 103 | name = "regex-syntax" 104 | version = "0.8.2" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 107 | 108 | [[package]] 109 | name = "ryu" 110 | version = "1.0.15" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 113 | 114 | [[package]] 115 | name = "serde" 116 | version = "1.0.192" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" 119 | dependencies = [ 120 | "serde_derive", 121 | ] 122 | 123 | [[package]] 124 | name = "serde-tuple-vec-map" 125 | version = "1.0.1" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "a04d0ebe0de77d7d445bb729a895dcb0a288854b267ca85f030ce51cdc578c82" 128 | dependencies = [ 129 | "serde", 130 | ] 131 | 132 | [[package]] 133 | name = "serde_derive" 134 | version = "1.0.192" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" 137 | dependencies = [ 138 | "proc-macro2", 139 | "quote", 140 | "syn", 141 | ] 142 | 143 | [[package]] 144 | name = "serde_json" 145 | version = "1.0.108" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" 148 | dependencies = [ 149 | "itoa", 150 | "ryu", 151 | "serde", 152 | ] 153 | 154 | [[package]] 155 | name = "syn" 156 | version = "2.0.39" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" 159 | dependencies = [ 160 | "proc-macro2", 161 | "quote", 162 | "unicode-ident", 163 | ] 164 | 165 | [[package]] 166 | name = "unicode-ident" 167 | version = "1.0.12" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 170 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | description = "Mommy's here to support you when running cargo~" 3 | repository = "https://github.com/Gankra/cargo-mommy" 4 | license = "MIT OR Apache-2.0" 5 | name = "cargo-mommy" 6 | version = "0.3.1" 7 | edition = "2021" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | [features] 11 | # This affects if a given response is *supported* in the built binary, 12 | # not if it's enabled by default 13 | default = ["thirsty", "yikes"] 14 | 15 | thirsty = [] 16 | yikes = [] 17 | 18 | [dependencies] 19 | fastrand = "1.8.0" 20 | 21 | [build-dependencies] 22 | serde = { version = "1.0.151", features = ["derive"] } 23 | serde_json = "1.0.91" 24 | serde-tuple-vec-map = "1.0.1" 25 | regex = "1.10.2" 26 | 27 | [workspace.metadata.release] 28 | shared-version = true 29 | tag-name = "v{{version}}" 30 | pre-release-commit-message = "release: {{version}}" 31 | 32 | # Config for 'cargo dist' 33 | [workspace.metadata.dist] 34 | # The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax) 35 | cargo-dist-version = "0.4.3" 36 | # CI backends to support 37 | ci = ["github"] 38 | # The installers to generate for each app 39 | installers = ["shell", "powershell"] 40 | # Target platforms to build apps for (Rust target-triple syntax) 41 | targets = ["x86_64-unknown-linux-gnu", "aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"] 42 | # Publish jobs to run in CI 43 | pr-run-mode = "plan" 44 | 45 | # The profile that 'cargo dist' will build with 46 | [profile.dist] 47 | inherits = "release" 48 | lto = "thin" 49 | 50 | [package.metadata.release] 51 | pre-release-replacements = [ 52 | {file="CHANGELOG.md", search="# Unreleased", replace="# Unreleased\n\nNothing Yet!\n\n\n# Version {{version}} ({{date}})", exactly=1}, 53 | ] 54 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # `cargo-mommy` 4 | 5 |
6 | 7 | [![crates.io](https://img.shields.io/crates/v/cargo-mommy.svg)](https://crates.io/crates/cargo-mommy) 8 | [![Rust CI](https://github.com/Gankra/cargo-mommy/workflows/Rust/badge.svg?branch=main)](https://github.com/Gankra/cargo-mommy/actions/workflows/ci.yml) 9 | 10 | 11 | 12 | Mommy's here to support you when running cargo~ ❤️ 13 | 14 | # Installation 15 | 16 | You can `cargo install cargo-mommy`, [see the website for more options](https://faultlore.com/cargo-mommy/) 17 | 18 | 19 | # Usage 20 | 21 | Run whatever cargo command you would normally but add mommy after cargo~ 22 | 23 | ``` 24 | cargo mommy check 25 | 26 | Checking bappy-script v0.1.3 27 | error: expected one of `!` or `::`, found `passes` 28 | --> src\main.rs:20:6 29 | | 30 | 20 | mods passes; 31 | | ^^^^^^ expected one of `!` or `::` 32 | 33 | error: could not compile `bappy-script` (bin "bappy-script") due to previous error 34 | mommy knows her little girl can do better~ 💞 35 | ``` 36 | 37 | [See the docs for more options](https://faultlore.com/cargo-mommy/book/) 38 | 39 | 40 | # Configuration 41 | 42 | Mommy will read the following environment variables to make her messages better for you~ ❤️ 43 | 44 | * `CARGO_MOMMYS_LITTLE` - what to call you~ (default: "girl") 45 | * `CARGO_MOMMYS_PRONOUNS` - what pronouns mommy will use for themself~ (default: "her") 46 | * `CARGO_MOMMYS_ROLES` - what role mommy will have~ (default "mommy") 47 | * `CARGO_MOMMYS_EMOTES` - what emotes mommy will have~ (default "❤️/💖/💗/💓/💞") 48 | * `CARGO_MOMMYS_MOODS` - picks the set of possible responses~ (default: "chill", possible values "chill", "ominous") 49 | 50 | All of these options can take a `/` separated list. Mommy will randomly select one of them whenever she talks to you~ 51 | 52 | For example, the phrase "mommy loves her little girl~ 💞" is "CARGO_MOMMYS_ROLE loves CARGO_MOMMYS_PRONOUNS little CARGO_MOMMYS_LITTLE~" 53 | 54 | So if you set `CARGO_MOMMYS_ROLES="daddy"`, `CARGO_MOMMYS_PRONOUNS="his/their"`, and `CARGO_MOMMYS_LITTLE="boy/pet/baby"` then you might get any of 55 | 56 | * daddy loves their little boy~ ❤️ 57 | * daddy loves his little pet~ 58 | * daddy loves their little baby~ 💗 59 | 60 | And so on~ 💓 61 | 62 | 63 | 64 | 65 | 66 | # Licensing 67 | mommy likes freedom~ ❤️, and is dual-licensed under [MIT](LICENSE-MIT) and [Apache 2.0](LICENSE-APACHE). 68 | 69 | Use either at your choice. 70 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | Mommy will always keep you safe and secure~ ❤️ 2 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Aria Desires"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "cargo-mommy" 7 | -------------------------------------------------------------------------------- /book/src/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Mommy's here to support you when running cargo~ ❤️ 4 | 5 | [See the homepage for installation instructions~](https://faultlore.com/cargo-mommy/) 6 | 7 | Run whatever cargo command you would normally but add mommy after cargo~ 8 | 9 | ``` 10 | cargo mommy check 11 | 12 | Checking bappy-script v0.1.3 13 | error: expected one of `!` or `::`, found `passes` 14 | --> src\main.rs:20:6 15 | | 16 | 20 | mods passes; 17 | | ^^^^^^ expected one of `!` or `::` 18 | 19 | error: could not compile `bappy-script` (bin "bappy-script") due to previous error 20 | mommy knows her little girl can do better~ 💞 21 | ``` 22 | 23 | By default mommy is kind to her little girl, but she can become anything you want~ 24 | 25 | * [I want mommy to call myself or herself something else](./customize/roles-and-pronouns.md) 26 | * [I want `cargo mommy` to be `cargo daddy` (or anything else)](./customize/true-roles.md) 27 | * [I want mommy to work with our plurality](./customize/true-roles.md) 28 | * [I want mommy to have a totally different mood](./customize/moods.md) 29 | * [I want mommy to invoke mommy with more than cargo](./customize/not-just-cargo.md) 30 | * [I want mommy to sdsdfdsfdfsd me (enabling nsfw features)](./customize/nsfw.md) 31 | * [I never want mommy to sdsdfdsfdfsd me (compiling out all nsfw features)](./customize/never-nsfw.md) 32 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](./README.md) 4 | - [Customizing](./customize/README.md) 5 | - [Roles And Pronouns](./customize/roles-and-pronouns.md) 6 | - [Moods](./customize/moods.md) 7 | - [True Roles](./customize/true-roles.md) 8 | - [Not Just Cargo](./customize/not-just-cargo.md) 9 | - [NSFW Features](./customize/nsfw.md) 10 | - [Removing NSFW Features](./customize/never-nsfw.md) 11 | - [Guides](./guides/README.md) 12 | - [Powershell (Windows)](./guides/powershell.md) 13 | - [Bash](./guides/bash.md) 14 | - [CI](./guides/ci.md) 15 | - [Contributing](./contributing/README.md) 16 | - [Concepts](./contributing/concepts.md) 17 | - [Adding New Messages](./contributing/adding-messages.md) 18 | - [Adding A New Variable](./contributing/adding-variables.md) 19 | - [Adding A New Mood](./contributing/adding-moods.md) 20 | - [Locally Building The Website](./contributing/docs.md) 21 | - [Releasing](./contributing/releasing.md) 22 | 23 | -------------------------------------------------------------------------------- /book/src/contributing/README.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | These are the technical rules of mommy club~ 4 | 5 | * mommy as a project is always going to *default* to being mommy, and *default* to loving her little girl, even if she also does everything she can to be customizable for people who don't want that~ 💖 6 | * there is no such thing as "being too extra" about mommy, mommy is so very overengineered and that is wonderful~ 💖 7 | * mommy is sfw and kind first and foremost, keep the hard stuff in [its corner](../customize/nsfw.md)~ 🖤 8 | * `--no-default-features` *has* to [make mommy forget how to be nsfw](../customize/never-nsfw.md)~ 9 | * if you add a feature, make sure it respects mommy's [True Roles](../customize/true-roles.md)~ 10 | * mommy should try her best to make sure the underlying commands she invokes work correctly~ 11 | * if mommy's functionality *can* be driven by [responses.json](https://github.com/Gankra/cargo-mommy/blob/main/responses.json), it should be~ 12 | * commit messages and comments should ideally be in mommy's voice but sometimes that's exhausting to sustain for complex technical details so this is not enforced, just encouraged~ 💕 13 | 14 | These are the social rules of mommy club~ 15 | 16 | * mommy loves you~ ❤️ 17 | * you should be kind to others~ 💖 18 | * Gankra is the arbiter of the previous detail until further notice~ 💙 19 | 20 | For most contributions, you probably just need to [edit responses.json](https://github.com/Gankra/cargo-mommy/blob/main/responses.json)~ 21 | 22 | See the sub-pages for details~ 23 | 24 | 25 | -------------------------------------------------------------------------------- /book/src/contributing/adding-messages.md: -------------------------------------------------------------------------------- 1 | # Adding New Messages 2 | 3 | If you want to add a new message to an existing mood, all you need to do is edit [responses.json](https://github.com/Gankra/cargo-mommy/blob/main/responses.json) and add the string! See [the concepts docs for details on the format](./concepts.md#messages)~ 4 | 5 | That's it, no code or docs needs to be changed~ 💕 6 | -------------------------------------------------------------------------------- /book/src/contributing/adding-moods.md: -------------------------------------------------------------------------------- 1 | # Adding A New Mood 2 | 3 | You shouldn't need any code to add a mood, just update [responses.json](https://github.com/Gankra/cargo-mommy/blob/main/responses.json) and the docs~ 4 | 5 | ## Editing responses.json 6 | 7 | * Add [the new mood to responses.json's "moods" object](./concepts.md#responsesjson)~ 8 | * Fill in as many messages as you can think of for [the message pools](./concepts.md#message-pools)~ 9 | * If NSFW, be sure to set "spiciness"~ 10 | 11 | For instance, here's a minimal example (SFW) "sleepy" mood~ 💤 12 | 13 | ```json 14 | { 15 | "moods": { 16 | "sleepy": { 17 | "positive": [ 18 | "that almost makes {role} want to get out of bed...", 19 | "*yawns*\ngood work~" 20 | ], 21 | "negative": [ 22 | "{role} thinks {pronoun} little {affectionate_term} might also be too tired~", 23 | "let's just take a nap, ok~?" 24 | ], 25 | "overflow": [ 26 | "{role} did too much and is going to bed..." 27 | ] 28 | }, 29 | } 30 | } 31 | ``` 32 | 33 | That's it, no code needs to be changed! 34 | 35 | 36 | ## Updating The Docs 37 | 38 | Add the mood and some example outputs to the [SFW mood docs](../customize/moods.md) or the [NSFW mood docs](../customize/nsfw.md#moods). -------------------------------------------------------------------------------- /book/src/contributing/adding-variables.md: -------------------------------------------------------------------------------- 1 | # Adding A New Variable 2 | 3 | You shouldn't need any code to add a new variable, just update [responses.json](https://github.com/Gankra/cargo-mommy/blob/main/responses.json) and the docs~ 4 | 5 | 6 | ## Editing responses.json 7 | 8 | * Add [the new variable to responses.json's "vars" object](./concepts.md#responsesjson)~ 9 | * Fill in [the default values](./concepts.md#variables)~ 10 | * If NSFW, be sure to set "spiciness"~ 11 | * Use the variable in [existing or new messages](./concepts.md#variables)~ 12 | 13 | For instance, here's a minimal example "furniture" variable that is set with `CARGO_MOMMYS_FURNITURES`~ 🪑 14 | 15 | ```json 16 | { 17 | "vars": { 18 | "furniture": { 19 | "defaults": ["chair", "desk"] 20 | }, 21 | } 22 | } 23 | ``` 24 | 25 | and here's how you might use it~ 26 | 27 | ```json 28 | { 29 | "moods": { 30 | "chill": { 31 | "positive": [ 32 | "thanks for helping build {role}'s {furniture}~" 33 | ], 34 | "negative": [ 35 | "ouch! {role} stubbed {pronoun} toe on the {furniture}!" 36 | ] 37 | } 38 | } 39 | } 40 | ``` 41 | 42 | That's it, no code needs to be changed~ 💕 43 | 44 | 45 | ## Updating The Docs 46 | 47 | Add the variable to the [SFW variable docs](../customize/roles-and-pronouns.md.md) or the [NSFW variable docs](../customize/nsfw.md#variables). -------------------------------------------------------------------------------- /book/src/contributing/concepts.md: -------------------------------------------------------------------------------- 1 | # Concepts 2 | 3 | At her heart, mommy exists to invoke cargo, check if the result was a success or failure, and print an appropriate message~ 4 | Printing that message involves reading a bunch of configuration from env-vars to define pools of values to randomly select from, then: 5 | 6 | 1. randomly [decide what mood the response should have](#moods) 7 | 2. use the success/failure state to select which [pool of messages](#message-pools) from that mood to use 8 | 3. randomly select [a message](#messages) from that pool 9 | 4. randomly format the message's template with any [variables](#variables) it uses (plus the implicit emote) 10 | 11 | cargo-mommy has 3 major components~ 12 | 13 | 1. [responses.json, where data lives](https://github.com/Gankra/cargo-mommy/blob/main/responses.json) 14 | 2. [build.rs, that digests that data and converts it to rust code](https://github.com/Gankra/cargo-mommy/blob/main/build.rs) 15 | 3. [main.rs, that handles execution](https://github.com/Gankra/cargo-mommy/blob/main/src/main.rs) 16 | 17 | The build.rs is extremely extra, but it's actually kind of useful for making it tolerable to [disable nsfw features](../customize/never-nsfw.md). In theory it also makes cargo-mommy very efficient and fast, which, again, is very extra. 18 | 19 | 20 | 21 | ## Responses.json 22 | 23 | The ""schema"" of [responses.json](https://github.com/Gankra/cargo-mommy/blob/main/responses.json) is currently the following (`$SOME_VALUE` here is an ~arbitrary string): 24 | 25 | ```text 26 | { 27 | "moods": { 28 | "$MOOD": { 29 | "positive": ["$GOOD_MESSAGE", ...] 30 | "negative": ["$BAD_MESSAGE" ...] 31 | "overflow": ["$E_TOO_MANY_MOMMY_MESSAGE", ...] 32 | "spiciness": "chill" | "thirsty" | "yikes" | , 33 | } 34 | } 35 | "vars": { 36 | "$VAR": { 37 | "defaults": ["$VALUE", ...] 38 | "env_key": "$KEY" | 39 | "spiciness": "chill" | "thirsty" | "yikes" | , 40 | } 41 | } 42 | } 43 | ``` 44 | 45 | 46 | 47 | 48 | ### Moods 49 | 50 | Moods contain [pools of messages](#message-pools) with a particular feeling/intensity. This was originally introduced to allow the user to opt into [nsfw functionality](../customize/nsfw.md). Its functionality is more general than that, and we'd be happy to have more sfw moods like "furry" or whatever. 51 | 52 | The "chill" mood is the default mood, and is assumed to exist. 53 | 54 | Spiciness is used to determine whether a mood's contents should be considered "nsfw" content, either "chill", "thirsty", or "yikes". Everything spicier than "chill" is considered "nsfw". There's no hard and fast rules here, just vibes. Spicier moods also gain access to spicier [variables](#variables). 55 | 56 | There are 2 moods that have the same names as their equivalent spiciness level, which may help determine how nsfw a new mood is. 57 | 58 | 59 | 60 | 61 | ### Message Pools 62 | 63 | Each mood must specify 3 pools of [messages](#messages) to select from: 64 | 65 | * "positive" messages appear when the cargo command mommy invoked was a success 66 | * "negative" messages appear when the cargo command mommy invoked was a failure 67 | * "overflow" messages appear when it was recursively invoked too many times (to break out of infinite loops from misconfiguration) 68 | 69 | 70 | 71 | ### Messages 72 | 73 | Messages are strings that optionally contain [`{variables}`](#variables) that need to be substituted (emotes don't appear in the message, they're just auto-applied to the end of every message). 74 | 75 | Some examples: 76 | 77 | "{role} thinks {pronoun} little {affectionate_term} earned a big hug~" 78 | 79 | Becomes something like: 80 | 81 | "Mommy thinks her little girl earned a big hug~ ❤️" 82 | 83 | Messages have the following conventions: 84 | 85 | * `{role} ends {pronoun} messages with tildes~` 86 | * `*{role} performs actions with asterisks*` 87 | * `*{role} combines both*\nwith newlines~` 88 | 89 | 90 | 91 | 92 | 93 | ### Variables 94 | 95 | Variables are a pool of values that will be randomly selected from, and substituted into the templates. 96 | 97 | The variables "mood", "emote", "pronoun", and "role" are explicitly named in cargo_mommy's main.rs, and are assumed to appear at the start of `vars` in that exact order. 98 | 99 | Spiciness is used to determine whether a variable's contents should be considered "nsfw" -- either "chill", "thirsty", or "yikes". Everything spicier than "chill" is considered "nsfw". There's no hard and fast rules here, just vibes. Spicier variables should only ever be used by spicier [moods](#moods). 100 | 101 | env_key is used to define a SCREAMING_CASE env-var `CARGO_{TRUE_ROLE}S_{ENV_KEY}S` (note the two extra S's!). For instance the "mood" key would be `CARGO_MOMMYS_MOODS`. 102 | 103 | If a variable's env-var isn't set, it will use its defaults as the pool. ("role" has no default value in responses.json because it has special logic to default to the [True Role](../customize/true-roles.md) when no custom roles are set.) 104 | 105 | If a variable's env-var *is* set, mommy will parse it as [ISO Standard Pronoun Syntax](../customize/roles-and-pronouns.md), which is to say a slash-delimited list like "she/he/they", "girl/boy", or "daddy", and use those values as the pool. 106 | 107 | Each time mommy encounters a variable that needs to be substituted in a message, it will randomly select a new value from the pool. 108 | 109 | At the end of a message, mommy will randomly decide whether to include an emote. 110 | 111 | -------------------------------------------------------------------------------- /book/src/contributing/docs.md: -------------------------------------------------------------------------------- 1 | # Locally Building The Website 2 | 3 | The [website](https://faultlore.com/cargo-mommy/) (and book it hosts) is automatically built for every PR, and automatically deployed for every push to main. See the [releasing](./releasing.md) page for details. 4 | 5 | If you want to run and test it locally, [install oranda](https://opensource.axo.dev/oranda/) and run: 6 | 7 | ``` 8 | oranda dev 9 | ``` 10 | 11 | This will put oranda in a "watch" mode that live-updates the website as you edit files, while serving the result on a localhost url (usually http://127.0.0.1:7979/cargo-mommy). 12 | 13 | Oranda internally uses [mdBook](https://rust-lang.github.io/mdBook/) as a library, with several patches to make mdbook have the same theme as the oranda one. If you're familiar with mdbook and just want to stick to that, you *should* be able to just [go to the book/ dir](https://github.com/Gankra/cargo-mommy/blob/main/book/) and run: 14 | 15 | ``` 16 | mdbook serve 17 | ``` 18 | 19 | It just won't have the same theming as the production site. 20 | -------------------------------------------------------------------------------- /book/src/contributing/releasing.md: -------------------------------------------------------------------------------- 1 | # Releasing 2 | 3 | Mommy's release process is simple but does a lot~ 4 | 5 | 6 | ## Cutting a Release 7 | 8 | 1. Checkout main 9 | 2. pull 10 | 3. Update the "Unreleased" section of [CHANGELOG.md](https://github.com/Gankra/cargo-mommy/blob/main/CHANGELOG.md) (DO NOT CHANGE THE HEADING!) 11 | 4. Commit 12 | 5. Run `cargo release {version}` (e.g. `cargo release 1.0.0`) 13 | 14 | This will trigger this cascade of events: 15 | 16 | 1. [cargo-release](https://github.com/crate-ci/cargo-release) will get the ball rolling: 17 | 1. [the changelog heading will be updated to "Version {version} ({date})"](https://github.com/Gankra/cargo-mommy/blob/0d96506db241003166e32deb22ad0ab0fc52c16c/Cargo.toml#L50-L53) 18 | 2. the version in the Cargo.toml will be update to "{version}" 19 | 3. [a commit will be made with "release: {version}"](https://github.com/Gankra/cargo-mommy/blob/0d96506db241003166e32deb22ad0ab0fc52c16c/Cargo.toml#L30) 20 | 4. [a "v{version}" git tag will be created](https://github.com/Gankra/cargo-mommy/blob/0d96506db241003166e32deb22ad0ab0fc52c16c/Cargo.toml#L29) 21 | 5. `cargo publish` will be run 22 | 6. A more robust equivalent of `git push && git push --tags` will run 23 | 2. [cargo-dist](https://opensource.axo.dev/cargo-dist/) will see the tagged commit and build binaries/archives/installers/announcements: 24 | 1. [release.yml](https://github.com/Gankra/cargo-mommy/blob/main/.github/workflows/release.yml) will spawn 25 | 2. binaries and archives (zips/tarballs) will get built for [all supported targets](https://github.com/Gankra/cargo-mommy/blob/0d96506db241003166e32deb22ad0ab0fc52c16c/Cargo.toml#L41) 26 | 3. [installers will be built](https://github.com/Gankra/cargo-mommy/blob/0d96506db241003166e32deb22ad0ab0fc52c16c/Cargo.toml#L39) that can fetch the archives 27 | 4. A [Github Release](https://github.com/Gankra/cargo-mommy/releases) will be made, with the release notes and installers/archives 28 | 3. [oranda](https://opensource.axo.dev/oranda/) will see a workflow called "Release" completed, and build and deploy docs 29 | 1. [web.yml](https://github.com/Gankra/cargo-mommy/blob/main/.github/workflows/web.yml) will spawn 30 | 2. [oranda will autodetect, retheme, and build](https://opensource.axo.dev/oranda/book/configuration/mdbook.html) the [cargo-mommy mdbook](https://github.com/Gankra/cargo-mommy/tree/main/src) 31 | 3. oranda will generate [a website with releases, the book, and a platform-autodetecting install widget for the latest release](https://faultlore.com/cargo-mommy/) 32 | 4. oranda will push the website to [the gh-pages branch](https://github.com/Gankra/cargo-mommy/tree/gh-pages) 33 | 4. Github Pages will trigger 34 | 1. [pages-build-deployment](https://github.com/Gankra/cargo-mommy/actions/workflows/pages/pages-build-deployment) will spawn 35 | 2. [the live website will update](https://faultlore.com/cargo-mommy/) 36 | 37 | Note that steps 3 and 4 trigger on all pushes to main, so updating docs doesn't require a new release~ 38 | 39 | 40 | 41 | 42 | ## Updating The Release Process 43 | 44 | The cargo-release step was created by hand-copying some config values that Gankra likes to use in her projects. There shouldn't be any need to mess with it. 45 | 46 | The cargo-dist step was created by locally running `cargo dist init`, selecting some targets/installers in the interactive prompts, and committing the results. It can be updated by running `cargo dist init` again. 47 | 48 | The oranda step was created by running `oranda generate ci`, selecting some of the options, and committing the results. A [custom oranda.json](https://github.com/Gankra/cargo-mommy/blob/main/oranda.json) was then added to fix path-prefix with github-pages, and to pick a non-default theme. The CI is setup to always use the "latest" oranda, so it's self-updating and may never need to be touched. Any changes that are desired will probably be changes to oranda.json. 49 | 50 | The Github Pages step was done in the Github Settings UI for the cargo-mommy repo. The website it deploys to is Gankra's. 51 | 52 | 53 | 54 | ## Ensuring The Release Process Works 55 | 56 | All pull requests run the `cargo dist plan` [part of the releases ci](https://github.com/Gankra/cargo-mommy/blob/main/.github/workflows/release.yml) to check that the release process still works. 57 | 58 | All pull requests run the `oranda build` [part of the website ci](https://github.com/Gankra/cargo-mommy/blob/main/.github/workflows/web.yml) to check that the website and mdbook still works. 59 | 60 | (This is just the default behaviour of the CI scripts that cargo-dist and oranda generate!) 61 | 62 | 63 | 64 | ## Historical Releases 65 | 66 | This process was setup for 0.3.0, everything before that was Gankra being intentionally sloppy because all of the above is her actual job and sometimes you just don't want to think about work and just want to mash `cargo publish` and nothing more on your shitpost that everyone took way too seriously. 67 | -------------------------------------------------------------------------------- /book/src/customize/README.md: -------------------------------------------------------------------------------- 1 | # Customizing 2 | 3 | By default mommy is kind to her little girl, but she can become anything you want~ 4 | 5 | * [I want mommy to call myself or herself something else](./roles-and-pronouns.md) 6 | * [I want `cargo mommy` to be `cargo daddy` (or anything else)](./true-roles.md) 7 | * [I want mommy to work with our plurality](./true-roles.md) 8 | * [I want mommy to have a totally different mood](./moods.md) 9 | * [I want mommy to invoke mommy with more than cargo](./not-just-cargo.md) 10 | * [I want mommy to sdsdfdsfdfsd me (enabling nsfw features)](./nsfw.md) 11 | * [I never want mommy to sdsdfdsfdfsd me (compiling out all nsfw features)](./never-nsfw.md) 12 | -------------------------------------------------------------------------------- /book/src/customize/moods.md: -------------------------------------------------------------------------------- 1 | # Moods 2 | 3 | Moods are a system for changing the flavour/vibe of mommy's messages~ 💕 4 | 5 | Here's the moods mommy can have~ 6 | 7 | * chill: encouragement and support from mommy~ 💕 8 | * ominous: mommy thinks you should prepare for the coming prophecy~ 💜 9 | 10 | See [the NSFW docs for additional spicier moods](./nsfw.md#moods)~ 11 | 12 | By default only the "chill" mood is enabled. The moods mommy can select from are chosen by the `CARGO_MOMMYS_MOODS` environment variable, using [ISO Standard Pronoun Syntax](./roles-and-pronouns.md) (e.g. `CARGO_MOMMYS_MOODS="chill/ominous"` turns both of those moods on). 13 | 14 | 15 | 16 | ## Examples: Chill 17 | 18 |
19 | Here's some examples of mommy being chill~ ❤️ 20 | 21 | ``` 22 | *pets your head* 23 | ``` 24 | 25 | ``` 26 | aww, what a good girl~ 27 | mommy knew you could do it~ ❤️ 28 | ``` 29 | 30 | ``` 31 | that's mommy's clever little girl~ 💞 32 | ``` 33 | 34 | ``` 35 | oops~! mommy loves you anyways~ 💞 36 | ``` 37 | 38 | ``` 39 | do you need mommy's help~? 💓 40 | ``` 41 | 42 |
43 | 44 | 45 | ## Examples: Ominous 46 | 47 |
48 | Here's some examples of mommy being ominous~ 💜 49 | 50 | ``` 51 | What you have set in motion today will be remembered for aeons to come! 💗 52 | ``` 53 | 54 | ``` 55 | mommy will see to it that her little girl's name is feared~ 💞 56 | ``` 57 | 58 | ``` 59 | Ah, failure? mommy will make sure the stars are right next time 60 | ``` 61 | 62 | ``` 63 | Does mommy's little girl need more time for worship~? 64 | ``` 65 | 66 |
-------------------------------------------------------------------------------- /book/src/customize/never-nsfw.md: -------------------------------------------------------------------------------- 1 | # Removing NSFW Features 2 | 3 | By default mommy operates in a totally safe-for-work mode, where she just provides positive encouragement, but she has support for more intense experiences, and if that concerns you, you can statically compile them out with feature flags~ 4 | 5 | To get a totally safe binary, all you need to do is disable mommy's default features~ 6 | 7 | ``` 8 | cargo install cargo-mommy --no-default-features 9 | ``` 10 | 11 | All nsfw strings, moods, and variables will be eliminated from the binary, leaving only encouragement~ ❤️ 12 | 13 | ...and the "ominous" mood too~ 🎭 -------------------------------------------------------------------------------- /book/src/customize/not-just-cargo.md: -------------------------------------------------------------------------------- 1 | # Not Just Cargo 2 | 3 | Although Mommy is primarily intended to be invoked as `cargo mommy`, you *can* invoke her binary directly, or setup aliases to it. 4 | 5 | If you try to make it into an alias, you should prefer pointing it to cargo-mommy directly, as this tends to play nicer with the rustup toolchain picker~ mommy will also respect CARGO to execute the right cargo for you~ 6 | 7 | 8 | ## REALLY Not Just Cargo 9 | 10 | Now, this is a Developing Feature that's first shipping in 0.3.1, but... 11 | 12 | If you want to use cargo-mommy for not-cargo programs, just set the CARGO_MOMMYS_ACTUAL environment variable to it, for example on linux you can do this: 13 | 14 | ``` 15 | CARGO_MOMMYS_ACTUAL=date cargo-mommy 16 | Sun Nov 19 05:33:34 PM CET 2023 17 | what a good girl you are~ ❤️ 18 | ``` 19 | 20 | Enough people have been asking about this that we might end up just making a second dedicated "mommy" binary that supports this usecase more directly. We'll see~ 21 | -------------------------------------------------------------------------------- /book/src/customize/nsfw.md: -------------------------------------------------------------------------------- 1 | # NSFW Features 2 | 3 | Good pet~ ❤️ 4 | 5 | All of mommy's NSFW content is hidden behind extra moods, see below~ 6 | 7 | 8 | ## I DONT WANT TO BE HERE 9 | 10 | Then [disable the nsfw features completely](./never-nsfw.md) 11 | 12 | 13 | 14 | ## Moods 15 | 16 | In addition to [the SFW moods](./moods.md), `CARGO_MOMMYS_MOODS` also takes these NSFW values~ 17 | 18 | * thirsty: teasing mommy, pretty horny~ 💕 19 | * yikes: dommy mommy step on me, explicit sex and bdsm~ 🖤 20 | 21 | You can enable "true mommy chaos mode" by setting `CARGO_MOMMYS_MOODS="chill/ominous/thirsty/yikes"`, making mommy oscillate wildly between light positive affirmation, screaming about the end of days, and trying to break you in half~ 💕 22 | 23 | 24 | ## Variables 25 | 26 | The "yikes" mood also has access to the following variables~ 27 | 28 | * `CARGO_MOMMYS_PARTS` - what part of mommy you should crave~ (default: "milk") 29 | * `CARGO_MOMMYS_FUCKING` - what to call mommy's pet~ (default: "slut/toy/pet/pervert/whore") 30 | 31 | 32 | 33 | ## Examples: Thirsty 34 | 35 |
36 | Here's some examples of mommy being thirsty~ ❤️ 37 | 38 | ``` 39 | *tugs your leash* 40 | that's a VERY good girl~ 💞 41 | ``` 42 | 43 | ``` 44 | *smooches your forehead* 45 | good job~ 46 | ``` 47 | 48 | ``` 49 | are you just keysmashing now~? 50 | cute~ 💖 51 | ``` 52 | 53 | ``` 54 | if you don't learn how to code better, mommy is going to put you in time-out~ 💓 55 | ``` 56 |
57 | 58 | ## Examples: Yikes 59 | 60 |
61 | Here's some examples of mommy being yikes~ 🖤 62 | 63 | ``` 64 | good slut~ 65 | you've earned five minutes with the buzzy wand~ 💗 66 | ``` 67 | 68 | ``` 69 | *slides her finger in your mouth* 70 | that's a good little toy~ ❤️ 71 | ``` 72 | 73 | ``` 74 | get on your knees and beg mommy for forgiveness you pervert~ 75 | ``` 76 | 77 | ``` 78 | mommy is starting to wonder if you should just give up and become her breeding stock~ 💗 79 | ``` 80 |
81 | 82 | 83 | ## Buttplug.io Support 84 | 85 | [Buttplug.io](https://buttplug.io/) is an open-source standards and software project for controlling intimate hardware, including sex toys, fucking machines, and more. 86 | 87 | Mommy doesn't integrate it, but she is compatible with [cargo-vibe](https://github.com/Shadlock0133/cargo-vibe) which does~ 💕 88 | 89 | ``` 90 | cargo mommy vibe check 91 | ``` 92 | 93 | Should always Just Work~ 🖤 94 | -------------------------------------------------------------------------------- /book/src/customize/roles-and-pronouns.md: -------------------------------------------------------------------------------- 1 | # Roles And Pronouns 2 | 3 | Mommy knows you might not be a "girl", and might not want her to be a "mommy" or a "her"~ 4 | 5 | Mommy will read the following environment variables to make her messages better for you~ ❤️ 6 | 7 | * `CARGO_MOMMYS_LITTLE` - what to call you~ (default: "girl") 8 | * `CARGO_MOMMYS_PRONOUNS` - what pronouns mommy will use for themself~ (default: "her") 9 | * `CARGO_MOMMYS_ROLES` - what role mommy will have~ (default "mommy") 10 | * `CARGO_MOMMYS_EMOTES` - what emotes mommy will have~ (default "❤️/💖/💗/💓/💞") 11 | 12 | See [the NSFW docs for additional spicier variables](./nsfw.md#variables)~ 13 | 14 | All of these options take ISO Standard Pronoun Syntax, which is to say a slash-delimited list like "she/he/they", "girl/boy", or "daddy". Mommy will randomly select one of them whenever she talks to you~ 15 | 16 | For example, the phrase "mommy loves her little girl~ 💞" is "CARGO_MOMMYS_ROLE loves CARGO_MOMMYS_PRONOUNS little CARGO_MOMMYS_LITTLE~" 17 | 18 | So if you set `CARGO_MOMMYS_ROLES="daddy"`, `CARGO_MOMMYS_PRONOUNS="his/their"`, and `CARGO_MOMMYS_LITTLE="boy/pet/baby"` then you might get any of 19 | 20 | * daddy loves their little boy~ ❤️ 21 | * daddy loves his little pet~ 22 | * daddy loves their little baby~ 💗 23 | 24 | And so on~ 💓 25 | -------------------------------------------------------------------------------- /book/src/customize/true-roles.md: -------------------------------------------------------------------------------- 1 | # True Roles 2 | 3 | While [Variables like Roles And Pronouns](./roles-and-pronouns.md) change *output*, the True Role exists to change *input*~ 4 | 5 | > TL;DR 6 | > 7 | > ``` 8 | > cargo mommy i mean daddy 9 | > mommy is now daddy~ 10 | > cargo daddy check 11 | > daddy loves you~ 12 | > ``` 13 | 14 | This feature is useful for: 15 | 16 | * folks who *really* don't want to say/see "mommy" ever again 17 | * folks who want to maintain several independent sets of configuration (perhaps for plural reasons) 18 | 19 | Specifically if you change the True Role from "mommy" to e.g. "daddy", the following changes will occur: 20 | 21 | * You will be able to invoke it as `cargo daddy` 22 | * Instead of reading env vars like `CARGO_MOMMYS_MOODS` for config, it will read `CARGO_DADDYS_MOODS` (note the extra "S"!) 23 | * If `CARGO_{TRUE_ROLE}S_ROLE` isn't set, it will default to the True Role 24 | 25 | The value "daddy" is arbitrary here, you can pick any value. Make yourself a `cargo burger-chimes` if you want! 26 | 27 | All you have to do to change the True Role is to rename the cargo-mommy(.exe) binary to cargo-daddy(.exe). 28 | 29 | 30 | ## I Mean... 31 | 32 | As a convenience, `cargo mommy i mean daddy` finds the current binary and makes a *copy* (not a symlink or move) with the new name for you. Execution is halted immediately after seeing such an incantation, so all other input is ignored. 33 | 34 | `cargo mommy i mean mommy`, or any other "idempotent i mean" is treated as sugar for "cargo mommy mommy" (which has always worked and produces 2 messages). e.g. 35 | 36 | ``` 37 | cargo mommy i mean mommy i mean mommy i mean mommy check 38 | Finished dev [unoptimized + debuginfo] target(s) in 0.01s 39 | that's mommy's clever little girl~ 💓 40 | *wraps you in a big hug* 💗 41 | you did it~! ❤️ 42 | good girl~ 43 | mommy's so proud of you~ 44 | ``` -------------------------------------------------------------------------------- /book/src/guides/README.md: -------------------------------------------------------------------------------- 1 | # Guides 2 | 3 | These guides are intended to help folks setup cargo-mommy in situations like these ones~ 4 | 5 | * [persistent bash config](./bash.md) 6 | * [persistent powershell config](./powershell.md) 7 | * [ci scripting](./ci.md) 8 | * the rust build system (x.py) 9 | * using cargo-mommy for other non-cargo-mommy commands (CARGO_MOMMYS_ACTUAL) 10 | * fish 11 | * zsh 12 | * nix 13 | * ...? 14 | 15 | Please contribute any knowledge you have about these setups so everyone can enjoy~ 16 | -------------------------------------------------------------------------------- /book/src/guides/bash.md: -------------------------------------------------------------------------------- 1 | # Bash 2 | 3 | TODO: tips on setting up persistent config for mommy in `~/.bashrc` or `~/.profile` using `export` and `alias` 4 | 5 | https://github.com/Gankra/cargo-mommy/pull/41 6 | -------------------------------------------------------------------------------- /book/src/guides/ci.md: -------------------------------------------------------------------------------- 1 | # Using cargo-mommy in CI 2 | 3 | While you can of course `cargo install cargo-mommy` in CI, this can significantly slow down your CI runs~ 4 | 5 | To help with this, every cargo-mommy release since 0.3.0 comes with prebuilt binaries and shell/powershell installers, allowing mommy to be setup in CI quickly and easily~ 💕 6 | 7 | See [the install page](https://faultlore.com/cargo-mommy/artifacts/) for `curl | sh` and `irm | iex` expressions~ 8 | 9 | Note that if you use a Github CI Matrix and it covers Windows and Not Windows, `run` expressions will implicitly be a shell/powershell polyglot. If so, we recommend putting the "install cargo-mommy" expression as an argument to the Matrix (powershell on windows, shell everywhere else)~ 10 | -------------------------------------------------------------------------------- /book/src/guides/powershell.md: -------------------------------------------------------------------------------- 1 | # Powershell (Windows) 2 | 3 | TODO: tips on setting up persistent config for mommy when using powershell (Persistently setting values in the registry? using `$env:CARGO_MOMMYS_LITTLE="boy/girl"` syntax?) 4 | 5 | https://github.com/Gankra/cargo-mommy/pull/41 6 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | //! Code that statically parses responses.json and adds it to the codebase~ 2 | //! 3 | //! This allows the binary to only include what it needs, both perf-wise 4 | //! and oh-god-i-found-these-strings-in-this-binary-wise. 5 | //! 6 | //! How to add a new mood, simply add a new entry to the `moods` object in 7 | //! `responses.json`. Make sure to add `vars` entries for any new variables you 8 | //! introduce. 9 | //! 10 | //! If your new mood or variable include... "spicy" terms, make sure to set an 11 | //! explicit `spiciness`. 12 | 13 | use std::collections::BTreeMap; 14 | use std::env; 15 | use std::fmt::Write; 16 | use std::fs; 17 | use std::ops::Range; 18 | use std::path::Path; 19 | 20 | use regex::Regex; 21 | 22 | const RESPONSES: &str = include_str!("./responses.json"); 23 | 24 | #[derive(PartialEq, Eq, PartialOrd, Ord, serde::Deserialize)] 25 | #[serde(rename_all = "snake_case")] 26 | enum Spiciness { 27 | Chill, 28 | Thirsty, 29 | Yikes, 30 | } 31 | 32 | impl Spiciness { 33 | const CONFIGURED: Spiciness = if cfg!(feature = "yikes") { 34 | Self::Yikes 35 | } else if cfg!(feature = "thirsty") { 36 | Self::Thirsty 37 | } else { 38 | Self::Chill 39 | }; 40 | } 41 | 42 | impl Default for Spiciness { 43 | fn default() -> Self { 44 | Self::Chill 45 | } 46 | } 47 | 48 | #[derive(serde::Deserialize)] 49 | struct Mood { 50 | positive: Vec, 51 | negative: Vec, 52 | overflow: Vec, 53 | 54 | #[serde(default)] 55 | spiciness: Spiciness, 56 | } 57 | 58 | #[derive(serde::Deserialize)] 59 | struct Var { 60 | defaults: Vec, 61 | #[serde(default)] 62 | env_key: Option, 63 | 64 | #[serde(default)] 65 | spiciness: Spiciness, 66 | 67 | // Mommy needs a way to reference variables by index when doing template 68 | // substitution. This type is the value of an ordered map, so we can just 69 | // stick an index in after parsing~ 70 | #[serde(skip)] 71 | index: usize, 72 | } 73 | 74 | #[derive(serde::Deserialize)] 75 | struct Config { 76 | vars: BTreeMap, 77 | moods: BTreeMap, 78 | } 79 | 80 | fn main() { 81 | let out_dir = &env::var("OUT_DIR").unwrap(); 82 | let dest_path = Path::new(out_dir).join("responses.rs"); 83 | 84 | let mut config: Config = serde_json::from_str(RESPONSES).unwrap(); 85 | let mut i = 0; 86 | let mut vars = String::new(); 87 | for (name, var) in config.vars.iter_mut() { 88 | if var.spiciness > Spiciness::CONFIGURED { 89 | continue; 90 | } 91 | var.index = i; 92 | i += 1; 93 | 94 | let env_key = var 95 | .env_key 96 | .clone() 97 | .unwrap_or_else(|| format!("{}S", name.to_uppercase())); 98 | let defaults = &var.defaults; 99 | let _ = write!( 100 | vars, 101 | r#"Var {{ env_key: "{env_key}", defaults: &{defaults:?} }},"# 102 | ); 103 | } 104 | 105 | let pattern = Regex::new(r"\{\w+\}").unwrap(); 106 | let mut responses = String::new(); 107 | for (name, mood) in &config.moods { 108 | if mood.spiciness > Spiciness::CONFIGURED { 109 | continue; 110 | } 111 | 112 | let parse_response = |text: &str, out: &mut String| { 113 | let _ = write!(out, "&["); 114 | 115 | // Mommy has to the template on matches for `pattern`, and generate 116 | // an array of alternating Chunk::Text and Chunk::Var values. 117 | let mut prev = 0; 118 | for var in pattern.find_iter(text) { 119 | let var_name = &var.as_str()[1..var.len() - 1]; 120 | let var_idx = match config.vars.get(var_name) { 121 | Some(var) => { 122 | assert!( 123 | var.spiciness <= Spiciness::CONFIGURED, 124 | "{{{var_name}}} is too spicy!" 125 | ); 126 | var.index 127 | } 128 | None => panic!("undeclared variable {{{var_name}}}"), 129 | }; 130 | 131 | let Range { start, end } = var.range(); 132 | let prev_chunk = &text[prev..start]; 133 | prev = end; 134 | 135 | let _ = write!(out, "Chunk::Text({prev_chunk:?}), Chunk::Var({var_idx}), "); 136 | } 137 | 138 | let _ = write!(out, "Chunk::Text({:?})],", &text[prev..],); 139 | }; 140 | 141 | let _ = write!(responses, "Mood {{ name: {name:?}, positive: &["); 142 | for response in &mood.positive { 143 | parse_response(response, &mut responses) 144 | } 145 | let _ = write!(responses, "], negative: &["); 146 | for response in &mood.negative { 147 | parse_response(response, &mut responses) 148 | } 149 | let _ = write!(responses, "], overflow: &["); 150 | for response in &mood.overflow { 151 | parse_response(response, &mut responses) 152 | } 153 | let _ = write!(responses, "] }},"); 154 | } 155 | 156 | // Mommy needs some hard-coded vars at a specific location~ 157 | let mood_idx = config.vars["mood"].index; 158 | let emote_idx = config.vars["emote"].index; 159 | let pronoun_idx = config.vars["pronoun"].index; 160 | let role_idx = config.vars["role"].index; 161 | 162 | fs::write( 163 | dest_path, 164 | format!( 165 | r" 166 | static CONFIG: Config<'static> = Config {{ 167 | vars: &[{vars}], 168 | moods: &[{responses}], 169 | }}; 170 | static MOOD: &Var<'static> = &CONFIG.vars[{mood_idx}]; 171 | static EMOTE: &Var<'static> = &CONFIG.vars[{emote_idx}]; 172 | static PRONOUN: &Var<'static> = &CONFIG.vars[{pronoun_idx}]; 173 | static ROLE: &Var<'static> = &CONFIG.vars[{role_idx}]; 174 | " 175 | ), 176 | ) 177 | .unwrap(); 178 | 179 | println!("cargo:rerun-if-changed=responses.json"); 180 | } 181 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gankra/cargo-mommy/7cbe9903e1b5f31c36169ec4d84bac2d5dfd2642/favicon.ico -------------------------------------------------------------------------------- /oranda.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://github.com/axodotdev/oranda/releases/latest/download/oranda-config-schema.json", 3 | "styles": { 4 | "theme": "axolight", 5 | "favicon": "favicon.ico" 6 | }, 7 | "build": { 8 | "path_prefix": "cargo-mommy" 9 | }, 10 | "components": { 11 | "artifacts": { 12 | "package_managers": { 13 | "preferred": { 14 | "cargo": "cargo install cargo-mommy" 15 | }, 16 | "additional": { 17 | "cargo (clean strings)": "cargo install cargo-mommy --no-default-features" 18 | } 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /responses.json: -------------------------------------------------------------------------------- 1 | { 2 | "moods": { 3 | "chill": { 4 | "positive": [ 5 | "*pets your head*", 6 | "*gives you scritches*", 7 | "you're such a smart cookie~", 8 | "that's a good {affectionate_term}~", 9 | "{role} thinks {pronoun} little {affectionate_term} earned a big hug~", 10 | "good {affectionate_term}~\n{role}'s so proud of you~", 11 | "aww, what a good {affectionate_term}~\n{role} knew you could do it~", 12 | "you did it~!", 13 | "{role} loves you~", 14 | "*gives you a sticker*", 15 | "*boops your nose*", 16 | "*wraps you in a big hug*", 17 | "well done~!\n{role} is so happy for you~", 18 | "what a good {affectionate_term} you are~", 19 | "that's {role}'s clever little {affectionate_term}~", 20 | "you're doing so well~!", 21 | "you're making {role} so happy~", 22 | "{role} loves {pronoun} cute little {affectionate_term}~" 23 | ], 24 | "negative": [ 25 | "{role} believes in you~", 26 | "don't forget to hydrate~", 27 | "aww, you'll get it next time~", 28 | "do you need {role}'s help~?", 29 | "everything's gonna be ok~", 30 | "{role} still loves you no matter what~", 31 | "oh no did {role}'s little {affectionate_term} make a big mess~?", 32 | "{role} knows {pronoun} little {affectionate_term} can do better~", 33 | "{role} still loves you~", 34 | "{role} thinks {pronoun} little {affectionate_term} is getting close~", 35 | "it's ok, {role}'s here for you~", 36 | "oh, darling, you're almost there~", 37 | "does {role}'s little {affectionate_term} need a bit of a break~?", 38 | "oops~! {role} loves you anyways~", 39 | "try again for {role}, {affectionate_term}~", 40 | "don't worry, {role} knows you can do it~" 41 | ], 42 | "overflow": [ 43 | "{role} has executed too many times and needs to take a nap~" 44 | ] 45 | }, 46 | "ominous": { 47 | "positive": [ 48 | "What you have set in motion today will be remembered for aeons to come!", 49 | "{role} will see to it that {pronoun} little {affectionate_term}'s name is feared~", 50 | "{role} is proud of the evil seed {pronoun} {affectionate_term} has planted into this accursed world" 51 | ], 52 | "negative": [ 53 | "Ah, failure? {role} will make sure the stars are right next time", 54 | "Does {role}'s little {affectionate_term} need more time for worship~?", 55 | "May the mark of the beast stain your flesh forever, {role} will haunt your soul forevermore" 56 | ], 57 | "overflow": [ 58 | "THOU HAST DRUNK TOO DEEPLY OF THE FONT" 59 | ] 60 | }, 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | "thirsty": { 75 | "spiciness": "thirsty", 76 | "positive": [ 77 | "*tugs your leash*\nthat's a VERY good {affectionate_term}~", 78 | "*runs {pronoun} fingers through your hair* good {affectionate_term}~ keep going~", 79 | "*smooches your forehead*\ngood job~", 80 | "*nibbles on your ear*\nthat's right~\nkeep going~", 81 | "*pats your butt*\nthat's a good {affectionate_term}~", 82 | "*drags {pronoun} nail along your cheek*\nsuch a good {affectionate_term}~", 83 | "*bites {pronoun} lip*\nmhmm~", 84 | "give {role} a kiss~", 85 | "*heavy breathing against your neck*" 86 | ], 87 | "negative": [ 88 | "you're so cute when you're flustered~", 89 | "do you think you're going to get a reward from {role} like that~?", 90 | "*grabs your hair and pulls your head back*\nyou can do better than that for {role} can't you~?", 91 | "if you don't learn how to code better, {role} is going to put you in time-out~", 92 | "does {role} need to give {pronoun} little {affectionate_term} some special lessons~?", 93 | "you need to work harder to please {role}~", 94 | "gosh you must be flustered~", 95 | "are you just keysmashing now~?\ncute~", 96 | "is {role}'s little {affectionate_term} having trouble reaching the keyboard~?" 97 | ], 98 | "overflow": [ 99 | "you've been a bad little {affectionate_term} and worn out {role}~" 100 | ] 101 | }, 102 | 103 | "yikes": { 104 | "spiciness": "yikes", 105 | "positive": [ 106 | "keep it up and {role} might let you cum you little {denigrating_term}~", 107 | "good {denigrating_term}~\nyou've earned five minutes with the buzzy wand~", 108 | "mmm~ come taste {role}'s {part}~", 109 | "*slides {pronoun} finger in your mouth*\nthat's a good little {denigrating_term}~", 110 | "you're so good with your fingers~\n{role} knows where {pronoun} {denigrating_term} should put them next~", 111 | "{role} is getting hot~", 112 | "that's a good {denigrating_term}~", 113 | "yes~\nyes~~\nyes~~~", 114 | "{role}'s going to keep {pronoun} good little {denigrating_term}~", 115 | "open wide {denigrating_term}.\nyou've earned {role}'s {part}~", 116 | "do you want {role}'s {part}?\nkeep this up and you'll earn it~", 117 | "oooh~ what a good {denigrating_term} you are~" 118 | ], 119 | "negative": [ 120 | "you filthy {denigrating_term}~\nyou made a mess, now clean it up~\nwith your tongue~", 121 | "*picks you up by the throat*\npathetic~", 122 | "*drags {pronoun} claws down your back*\ndo it again~", 123 | "*brandishes {pronoun} paddle*\ndon't make me use this~", 124 | "{denigrating_term}.\n{denigrating_term}~\n{denigrating_term}~~", 125 | "get on your knees and beg {role} for forgiveness you {denigrating_term}~", 126 | "{role} doesn't think {pronoun} little {denigrating_term} should have permission to wear clothes anymore~", 127 | "never forget you belong to {role}~", 128 | "does {role} need to put you in the {denigrating_term} wiggler~?", 129 | "{role} is starting to wonder if you should just give up and become {pronoun} breeding stock~", 130 | "on your knees {denigrating_term}~", 131 | "oh dear. {role} is not pleased", 132 | "one spank per error sounds appropriate, don't you think {denigrating_term}?", 133 | "no more {part} for you {denigrating_term}" 134 | ], 135 | "overflow": [ 136 | "brats like you don't get to talk to {role}" 137 | ] 138 | } 139 | }, 140 | 141 | "vars": { 142 | "mood": { 143 | "defaults": ["chill"] 144 | }, 145 | "emote": { 146 | "defaults": ["❤️", "💖", "💗", "💓", "💞"] 147 | }, 148 | "pronoun": { 149 | "defaults": ["her"] 150 | }, 151 | "role": { 152 | "defaults": [] 153 | }, 154 | 155 | "affectionate_term": { 156 | "defaults": ["girl"], 157 | "env_key": "LITTLE" 158 | }, 159 | 160 | "denigrating_term": { 161 | "spiciness": "yikes", 162 | "defaults": ["slut", "toy", "pet", "pervert", "whore"], 163 | "env_key": "FUCKING" 164 | }, 165 | "part": { 166 | "spiciness": "yikes", 167 | "defaults": ["milk"] 168 | } 169 | } 170 | } 171 | 172 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::let_and_return)] 2 | 3 | use fastrand::Rng; 4 | use std::env; 5 | use std::io::IsTerminal; 6 | 7 | enum ResponseType { 8 | Positive, 9 | Negative, 10 | Overflow, 11 | } 12 | 13 | /// Mommy intentionally lets her little ones call her recursively, since they might want to hear more from her~ 14 | /// 15 | /// If they call her a thousand times in a row, though, something has probably gone wrong 😏 16 | const RECURSION_LIMIT: u8 = 100; 17 | /// This name is intentionally not user-configurable. Mommy can't let the little ones make *too* 18 | /// much of a mess~ 19 | const RECURSION_LIMIT_VAR: &str = "CARGO_MOMMY_RECURSION_LIMIT"; 20 | 21 | fn main() { 22 | // Ideally mommy would use ExitCode but that's pretty new and mommy wants 23 | // to support more little ones~ 24 | let code = real_main().unwrap_or_else(|e| { 25 | eprintln!("Error: {e:?}"); 26 | 1 27 | }); 28 | std::process::exit(code) 29 | } 30 | 31 | fn real_main() -> Result> { 32 | let rng = Rng::new(); 33 | 34 | let mut arg_iter = env::args().peekable(); 35 | 36 | // Understand who mommy really is~ 37 | // 38 | // *INHALES* 39 | // 40 | // Ok so first argument is this binary, should look like /some/path/to/cargo-mommy(.exe) 41 | // but we want to let it instead be cargo-daddy, and for everything to rekey itself to: 42 | // * make the default role be "daddy" 43 | // * make all the read env-vars be "CARGO_DADDYS_*" 44 | 45 | // Interpret the argument as a path so we can manipulate it~ 46 | let first_arg = arg_iter.next(); 47 | let bin_path = env::current_exe() 48 | .unwrap_or_else(|_| std::path::PathBuf::from(first_arg.unwrap_or_default())); 49 | // Get the extensionless-file name, and parse it case-insensitively~ 50 | let bin_name = bin_path 51 | .file_stem() 52 | .unwrap_or_default() 53 | .to_string_lossy() 54 | .to_lowercase(); 55 | let true_role = if let Some((_path, role)) = bin_name.rsplit_once("cargo-") { 56 | role.to_owned() 57 | } else { 58 | // If something messed up is going on "mommy" will always take care of it~ 59 | "mommy".to_owned() 60 | }; 61 | 62 | let cargo = env::var(format!("CARGO_{}S_ACTUAL", true_role.to_uppercase())) 63 | .or_else(|_| env::var("CARGO")) 64 | .unwrap_or_else(|_| "cargo".to_owned()); 65 | 66 | // Check if someone has told mommy to keep calling herself~ 67 | // Mommy loves you, darlings, but she can't keep running forever~ 68 | let mut new_limit = 1; 69 | if let Ok(limit) = env::var(RECURSION_LIMIT_VAR) { 70 | if let Ok(n) = limit.parse::() { 71 | if n > RECURSION_LIMIT { 72 | let mut response = select_response(&true_role, &rng, ResponseType::Overflow); 73 | match &mut response { 74 | Ok(s) | Err(s) => { 75 | *s += "\nyou didn't set CARGO to something naughty, did you?\n" 76 | } 77 | } 78 | pretty_print(response); 79 | return Ok(2); 80 | } else { 81 | new_limit = n + 1; 82 | } 83 | } 84 | } 85 | 86 | // *GASPS FOR BREATH* 87 | // 88 | // *INHALES DESPERATELY* 89 | // 90 | // cargo subcommands when run as "cargo blah" get the "blah" argument passed to themselves 91 | // as the *second* argument. However if we are invoked directly as "cargo-blah" we won't 92 | // have that extra argument! So if there's a second argument that is "mommy" (or whatever) 93 | // we pop off that redundant copy before forwarding the rest of the args back to "cargo ...". 94 | // if we don't do this, we'll infinitely recurse into ourselves by re-calling "cargo mommy"! 95 | // (note that it *is* supported to do `cargo mommy mommy` and get two messages, although I 96 | // believe we do this, `cargo-mommy mommy` will still only get you one message). 97 | 98 | if arg_iter.peek().map_or(false, |arg| arg == &true_role) { 99 | let _ = arg_iter.next(); 100 | } 101 | 102 | // *WHEEZES* 103 | // 104 | // *PANTS FOR A MINUTE* 105 | // 106 | // Ok so now we want to detect if the invocation looked like "cargo mommy i mean daddy" 107 | // if it *does* we want to copy ourselves to rename cargo-mommy to cargo-daddy. To make this 108 | // simpler, collect the args into a vec so we can peek more than one element. 109 | // 110 | // ... 111 | // 112 | // ~ 113 | let mut args: Vec<_> = arg_iter.collect(); 114 | { 115 | // We speculate the "i mean" part so that can easily discard it 116 | // in the case of "cargo mommy i mean mommy", making the execution 117 | // equivalent to "cargo mommy mommy". Not popping off the extra 118 | // "mommy" let "cargo mommy i mean mommy i mean mommy" work right~ 119 | let new_role = args.get(2); 120 | let mean = args.get(1) == Some(&"mean".to_owned()); 121 | let i = args.first() == Some(&"i".to_owned()); 122 | if i && mean { 123 | if let Some(new_role) = new_role.cloned() { 124 | // Ok at this point we're confident we got "i mean " 125 | // so definitely consume those arguments~ 126 | args.drain(..2); 127 | 128 | // If the new role is the same as before, they typed something like 129 | // "cargo mommy i mean mommy test" so we don't need to do anything~ 130 | if new_role != true_role { 131 | if let Some(parent) = bin_path.parent() { 132 | let new_bin_name = format!("cargo-{new_role}"); 133 | let mut new_bin_path = parent.join(new_bin_name); 134 | if let Some(ext) = bin_path.extension() { 135 | new_bin_path.set_extension(ext); 136 | } 137 | if let Err(e) = std::fs::copy(bin_path, new_bin_path) { 138 | Err(format!( 139 | "{role} couldn't copy {pronoun}self...\n{e:?}", 140 | role = ROLE.load(&true_role, &rng)?, 141 | pronoun = PRONOUN.load(&true_role, &rng)?, 142 | ))? 143 | } else { 144 | // Just exit immediately on success, don't try to get too clever here~ 145 | eprintln!("{true_role} is now {new_role}~"); 146 | return Ok(0); 147 | } 148 | } else { 149 | Err(format!( 150 | "{role} couldn't copy {pronoun}self...\n(couldn't find own parent dir)", 151 | role = ROLE.load(&true_role, &rng)?, 152 | pronoun = PRONOUN.load(&true_role, &rng)?, 153 | ))?; 154 | } 155 | } 156 | } 157 | } 158 | } 159 | 160 | // Time for mommy to call cargo~ 161 | let mut cmd = std::process::Command::new(&cargo); 162 | cmd.args(args) 163 | .env(RECURSION_LIMIT_VAR, new_limit.to_string()); 164 | let status: std::process::ExitStatus = cmd.status().map_err(|err: std::io::Error| { 165 | format!("{true_role} tried looking everywhere, but did not find `{cargo}`: {err}",) 166 | })?; 167 | let code = status.code().unwrap_or(1); 168 | if is_quiet_mode_enabled(cmd.get_args()) { 169 | return Ok(code); 170 | } 171 | 172 | // Time for mommy to tell you how you did~ 173 | let response = if status.success() { 174 | select_response(&true_role, &rng, ResponseType::Positive) 175 | } else { 176 | select_response(&true_role, &rng, ResponseType::Negative) 177 | }; 178 | pretty_print(response); 179 | 180 | Ok(code) 181 | } 182 | 183 | fn pretty_print(response: Result) { 184 | let stylize = std::io::stderr().is_terminal(); 185 | match (response, stylize) { 186 | (Ok(resp), true) => eprintln!("\x1b[1m{resp}\x1b[0m"), 187 | (Err(resp), true) => eprintln!("\x1b[31m{resp}\x1b[0m"), 188 | (Ok(resp) | Err(resp), false) => eprintln!("{resp}"), 189 | } 190 | } 191 | 192 | fn is_quiet_mode_enabled(args: std::process::CommandArgs) -> bool { 193 | for arg in args.filter_map(std::ffi::OsStr::to_str) { 194 | match arg.as_bytes() { 195 | b"--" => break, 196 | b"--quiet" => return true, 197 | [b'-', b'-', ..] => {} 198 | [b'-', args @ ..] if args.contains(&b'q') => return true, 199 | _ => {} 200 | } 201 | } 202 | 203 | false 204 | } 205 | 206 | fn select_response( 207 | true_role: &str, 208 | rng: &Rng, 209 | response_type: ResponseType, 210 | ) -> Result { 211 | // Choose what mood mommy is in~ 212 | let mood = MOOD.load(true_role, rng)?; 213 | 214 | let Some(group) = &CONFIG.moods.iter().find(|group| group.name == mood) else { 215 | let supported_moods_str = CONFIG 216 | .moods 217 | .iter() 218 | .map(|group| group.name) 219 | .collect::>() 220 | .join(", "); 221 | return Err(format!( 222 | "{role} doesn't know how to feel {mood}... {pronoun} moods are {supported_moods_str}", 223 | role = ROLE.load(true_role, rng)?, 224 | pronoun = PRONOUN.load(true_role, rng)?, 225 | )); 226 | }; 227 | 228 | // Choose what mommy will say~ 229 | let responses = match response_type { 230 | ResponseType::Positive => group.positive, 231 | ResponseType::Negative => group.negative, 232 | ResponseType::Overflow => group.overflow, 233 | }; 234 | let response = &responses[rng.usize(..responses.len())]; 235 | 236 | // Apply options to the message template~ 237 | let mut response = CONFIG.apply_template(true_role, response, rng)?; 238 | 239 | // Let mommy show a little emote~ 240 | let should_emote = rng.bool(); 241 | if should_emote { 242 | if let Ok(emote) = EMOTE.load(true_role, rng) { 243 | response.push(' '); 244 | response.push_str(&emote); 245 | } 246 | } 247 | 248 | // Done~! 249 | Ok(response) 250 | } 251 | 252 | // Mommy generates CONFIG and other global constants in build.rs~ 253 | include!(concat!(env!("OUT_DIR"), "/responses.rs")); 254 | 255 | struct Config<'a> { 256 | vars: &'a [Var<'a>], 257 | moods: &'a [Mood<'a>], 258 | } 259 | 260 | impl Config<'_> { 261 | /// Applies a template by resolving `Chunk::Var`s against `self.vars`. 262 | fn apply_template( 263 | &self, 264 | true_role: &str, 265 | chunks: &[Chunk], 266 | rng: &Rng, 267 | ) -> Result { 268 | let mut out = String::new(); 269 | for chunk in chunks { 270 | match chunk { 271 | Chunk::Text(text) => out.push_str(text), 272 | Chunk::Var(i) => out.push_str(&self.vars[*i].load(true_role, rng)?), 273 | } 274 | } 275 | Ok(out) 276 | } 277 | } 278 | 279 | struct Mood<'a> { 280 | name: &'a str, 281 | // Each of mommy's response templates is an alternating sequence of 282 | // Text and Var chunks; Text is literal text that should be printed as-is; 283 | // Var is an index into mommy's CONFIG.vars table~ 284 | positive: &'a [&'a [Chunk<'a>]], 285 | negative: &'a [&'a [Chunk<'a>]], 286 | overflow: &'a [&'a [Chunk<'a>]], 287 | } 288 | 289 | enum Chunk<'a> { 290 | Text(&'a str), 291 | Var(usize), 292 | } 293 | 294 | struct Var<'a> { 295 | env_key: &'a str, 296 | defaults: &'a [&'a str], 297 | } 298 | 299 | impl Var<'_> { 300 | /// Loads this variable and selects one of the possible values for it; 301 | /// produces an in-character error message on failure. 302 | fn load(&self, true_role: &str, rng: &Rng) -> Result { 303 | // try to load custom settings from env vars~ 304 | let var = env::var(self.env(true_role)); 305 | let split; 306 | 307 | // parse the custom settings or use the builtins~ 308 | let choices = match var.as_deref() { 309 | Ok("") => &[], 310 | Ok(value) => { 311 | split = value.split('/').collect::>(); 312 | split.as_slice() 313 | } 314 | Err(_) => self.defaults, 315 | }; 316 | 317 | if choices.is_empty() { 318 | // If there's no ROLES set, default to mommy's true nature~ 319 | if self.env_key == "ROLES" { 320 | return Ok(true_role.to_owned()); 321 | } 322 | 323 | // Otherwise, report an error~ 324 | let role = ROLE.load(true_role, rng)?; 325 | return Err(format!( 326 | "{role} needs at least one value for {}~", 327 | self.env_key 328 | )); 329 | } 330 | 331 | // now select a choice from the options~ 332 | Ok(choices[rng.usize(..choices.len())].to_owned()) 333 | } 334 | 335 | /// Gets the name of the env var to load~ 336 | fn env(&self, true_role: &str) -> String { 337 | // Normally we'd load from CARGO_MOMMYS_* 338 | // but if cargo-mommy is cargo-daddy, we should load CARGO_DADDYS_* instead~ 339 | // If we have multiple words in our role, we must also be careful with spaces~ 340 | let screaming_role = true_role.to_ascii_uppercase().replace(' ', "_"); 341 | format!("CARGO_{screaming_role}S_{}", self.env_key) 342 | } 343 | } 344 | 345 | #[cfg(test)] 346 | #[test] 347 | fn test() { 348 | // Uncomment if you want a failing test 349 | // panic!("oops!!"); 350 | } 351 | --------------------------------------------------------------------------------