├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── pipeline.yml │ ├── release.yml │ └── wiki.yml ├── .gitignore ├── .gitmodules ├── .vim └── coc-settings.json ├── .vimspector.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── SECURITY.md ├── docker └── Dockerfile ├── docs ├── README.adoc ├── todos.py └── wiki │ ├── .gitignore │ ├── archetypes │ └── default.adoc │ ├── config.toml │ ├── content │ ├── _index.adoc │ ├── docs │ │ ├── command reference │ │ │ ├── _index.adoc │ │ │ └── collect.adoc │ │ ├── installation.adoc │ │ ├── status.adoc │ │ └── todos.adoc │ └── parts │ │ └── status-table.adoc.part │ └── resources │ └── .gitignore ├── pkg ├── appimage │ ├── .gitignore │ └── senile.AppDir │ │ ├── AppRun │ │ ├── icon.png │ │ ├── senile.desktop │ │ └── usr │ │ └── bin │ │ └── .gitkeep └── aur │ ├── .gitignore │ └── PKGBUILD ├── rustfmt.toml ├── src ├── args.rs ├── commands.rs ├── crawler.rs ├── error.rs ├── lib.rs ├── main.rs ├── parser.rs └── types.rs └── tasks.py /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: replicadse 4 | patreon: replicadse 5 | -------------------------------------------------------------------------------- /.github/workflows/pipeline.yml: -------------------------------------------------------------------------------- 1 | name: pipeline 2 | on: push 3 | 4 | jobs: 5 | build: 6 | name: check (${{ matrix.name }}) 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | include: 11 | - name: "default" 12 | features: "" 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: install toolchain 16 | run: rustup default stable 17 | - name: install tools 18 | run: sudo apt-get install build-essential 19 | - name: execute unit tests 20 | run: cargo test ${{ matrix.features }} 21 | - name: build program 22 | run: cargo build --release --locked ${{ matrix.features }} 23 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | version: 6 | description: 'version number' 7 | required: true 8 | 9 | env: 10 | RELEASE_FILE: RELEASE.txt 11 | 12 | jobs: 13 | check: 14 | name: check (${{ matrix.name }}) 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | include: 19 | - name: "default" 20 | features: "" 21 | steps: 22 | - uses: actions/checkout@v1 23 | - name: install toolchain 24 | run: rustup default stable 25 | - name: install tools 26 | run: sudo apt-get install build-essential 27 | - name: execute tests 28 | run: cargo test ${{ matrix.features }} 29 | - name: build program 30 | run: cargo build ${{ matrix.features }} 31 | 32 | tag: 33 | if: github.ref == 'refs/heads/master' 34 | needs: check 35 | name: tag and release 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v1 39 | - name: create release 40 | id: create_release 41 | uses: actions/create-release@latest 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | with: 45 | tag_name: ${{ github.event.inputs.version }} 46 | release_name: ${{ github.event.inputs.version }} 47 | body: | 48 | Release ${{ github.event.inputs.version }}. 49 | draft: false 50 | prerelease: false 51 | - run: printf ${{ steps.create_release.outputs.upload_url }} > ${{ env.RELEASE_FILE }} 52 | - name: upload release data 53 | uses: actions/upload-artifact@v1.0.0 54 | with: 55 | name: RELEASE 56 | path: ${{ env.RELEASE_FILE }} 57 | 58 | publish-cratesio: 59 | needs: tag 60 | name: crates.io 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v1 64 | - name: get version 65 | id: get_version 66 | run: echo ::set-output name=VERSION::$(git tag --points-at HEAD --sort -version:refname | head -1) 67 | - name: install tools 68 | run: | 69 | sudo apt-get install build-essential 70 | rustup default stable 71 | pip install invoke 72 | - name: publish 73 | id: publish 74 | run: | 75 | invoke ci.update-version --version=${{ steps.get_version.outputs.VERSION }} 76 | cargo login ${{ secrets.CRATES_IO_TOKEN }} 77 | cargo publish --allow-dirty 78 | 79 | publish-dockerhub: 80 | needs: publish-cratesio 81 | name: dockerhub 82 | runs-on: ubuntu-latest 83 | steps: 84 | - uses: actions/checkout@v1 85 | - name: get version 86 | id: get_version 87 | run: echo ::set-output name=VERSION::$(git tag --points-at HEAD --sort -version:refname | head -1) 88 | - name: publish 89 | id: publish 90 | run: | 91 | printf ${{ secrets.DOCKERHUB }} | docker login --username replicadse --password-stdin 92 | docker build -t replicadse/senile:latest -f docker/Dockerfile --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} . 93 | docker push replicadse/senile:latest 94 | docker image tag replicadse/senile:latest replicadse/senile:${{ steps.get_version.outputs.VERSION }} 95 | docker push replicadse/senile:${{ steps.get_version.outputs.VERSION }} 96 | 97 | publish-archlinux-aur: 98 | needs: publish-cratesio 99 | name: archlinux aur 100 | runs-on: ubuntu-latest 101 | steps: 102 | - uses: actions/checkout@v1 103 | - name: get version 104 | id: get_version 105 | run: echo ::set-output name=VERSION::$(git tag --points-at HEAD --sort -version:refname | head -1) 106 | - uses: actions/setup-python@v2 107 | with: 108 | python-version: '3.7' 109 | - name: install tools 110 | run: | 111 | rustup default stable 112 | sudo apt-get install build-essential 113 | pip install invoke 114 | - name: prepare 115 | id: prepare 116 | run: | 117 | invoke ci.update-version --version=${{ steps.get_version.outputs.VERSION }} 118 | - name: publish 119 | uses: KSXGitHub/github-actions-deploy-aur@v2.2.5 120 | with: 121 | pkgname: senile 122 | pkgbuild: ./pkg/aur/PKGBUILD 123 | commit_username: replicadse 124 | commit_email: aw@voidpointergroup.com 125 | ssh_private_key: ${{ secrets.AUR_SSH_PK }} 126 | commit_message: update AUR package 127 | 128 | publish-appimage: 129 | needs: tag 130 | name: publish appimage for ${{ matrix.os }} 131 | runs-on: ${{ matrix.os }} 132 | strategy: 133 | matrix: 134 | include: 135 | - os: ubuntu-latest 136 | target: x86_64-unknown-linux-gnu 137 | arch: x86_64 138 | install: sudo apt-get install build-essential 139 | steps: 140 | - uses: actions/checkout@v1 141 | - name: get version 142 | id: get_version 143 | run: echo ::set-output name=VERSION::$(git tag --points-at HEAD --sort -version:refname | head -1) 144 | - name: download release id 145 | uses: actions/download-artifact@v1.0.0 146 | with: 147 | name: RELEASE 148 | - name: get release data 149 | id: get_release_data 150 | run: echo ::set-output name=upload_url::$(cat RELEASE/${{ env.RELEASE_FILE }}) 151 | - uses: actions/setup-python@v2 152 | with: 153 | python-version: '3.7' 154 | - name: install tools 155 | run: | 156 | rustup default stable 157 | ${{ matrix.install }} 158 | rustup target install ${{ matrix.target }} 159 | pip install invoke 160 | wget -O appimagetool.AppImage https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage 161 | chmod +x ./appimagetool.AppImage 162 | - name: build-${{ matrix.target }} 163 | run: | 164 | invoke ci.update-version --version=${{ steps.get_version.outputs.VERSION }} 165 | cargo build --release --target ${{ matrix.target }} 166 | cp ./target/${{ matrix.target }}/release/senile ./pkg/appimage/senile.AppDir/usr/bin/senile 167 | cd ./pkg/appimage && ARCH=${{ matrix.arch }} ../../appimagetool.AppImage ./senile.AppDir && cd ../../ 168 | - name: zip 169 | run: cd ./pkg/appimage && tar -zcvf AppImage.${{ matrix.arch }}.tar.gz senile-${{ matrix.arch }}.AppImage 170 | - name: upload asset 171 | uses: svenstaro/upload-release-action@v1-release 172 | with: 173 | repo_token: ${{ secrets.GITHUB_TOKEN }} 174 | file: ./pkg/appimage/AppImage.${{ matrix.arch }}.tar.gz 175 | asset_name: AppImage.${{ matrix.arch }}.tar.gz 176 | tag: ${{ steps.get_version.outputs.VERSION }} 177 | overwrite: true 178 | 179 | publish: 180 | needs: tag 181 | name: publish for ${{ matrix.os }} 182 | runs-on: ${{ matrix.os }} 183 | strategy: 184 | matrix: 185 | include: 186 | - os: macos-latest 187 | target: x86_64-apple-darwin 188 | install: printf ok 189 | - os: ubuntu-latest 190 | target: x86_64-unknown-linux-gnu 191 | install: sudo apt-get install build-essential 192 | steps: 193 | - uses: actions/checkout@v1 194 | - name: get version 195 | id: get_version 196 | run: echo ::set-output name=VERSION::$(git tag --points-at HEAD --sort -version:refname | head -1) 197 | - name: download release id 198 | uses: actions/download-artifact@v1.0.0 199 | with: 200 | name: RELEASE 201 | - name: get release data 202 | id: get_release_data 203 | run: echo ::set-output name=upload_url::$(cat RELEASE/${{ env.RELEASE_FILE }}) 204 | - uses: actions/setup-python@v2 205 | with: 206 | python-version: '3.7' 207 | - name: install tools 208 | run: | 209 | rustup default stable 210 | ${{ matrix.install }} 211 | rustup target install ${{ matrix.target }} 212 | pip install invoke 213 | - name: build-${{ matrix.target }} 214 | run: | 215 | invoke ci.update-version --version=${{ steps.get_version.outputs.VERSION }} 216 | cargo build --release --target ${{ matrix.target }} 217 | - name: zip 218 | run: cd ./target/${{ matrix.target }}/release && tar -zcvf ${{ matrix.target }}.tar.gz senile 219 | - name: upload asset 220 | uses: svenstaro/upload-release-action@v1-release 221 | with: 222 | repo_token: ${{ secrets.GITHUB_TOKEN }} 223 | file: ./target/${{ matrix.target }}/release/${{ matrix.target }}.tar.gz 224 | asset_name: ${{ matrix.target }}.tar.gz 225 | tag: ${{ steps.get_version.outputs.VERSION }} 226 | overwrite: true 227 | -------------------------------------------------------------------------------- /.github/workflows/wiki.yml: -------------------------------------------------------------------------------- 1 | name: wiki 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - docs/** 9 | - .github/** 10 | 11 | jobs: 12 | website: 13 | name: website 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@master 17 | with: 18 | submodules: true 19 | - name: install 20 | run: | 21 | cargo +stable install senile 22 | sudo apt install hugo 23 | npm i -g asciidoctor 24 | - name: prepare 25 | run: | 26 | senile collect -f="(\.rs|\.py)$" --format="// TODO!(,):" --format="# TODO!(,):" -p . | ./docs/todos.py >> ./docs/wiki/content/docs/todos.adoc 27 | cd docs/wiki 28 | hugo 29 | - name: deploy 30 | uses: peaceiris/actions-gh-pages@v3 31 | with: 32 | github_token: ${{ secrets.GITHUB_TOKEN }} 33 | publish_dir: docs/wiki/public 34 | publish_branch: wiki 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs/wiki/themes/hugo-theme-techdoc"] 2 | path = docs/wiki/themes/hugo-theme-techdoc 3 | url = https://github.com/thingsym/hugo-theme-techdoc.git 4 | [submodule "docs/wiki/themes/book"] 5 | path = docs/wiki/themes/book 6 | url = https://github.com/alex-shpak/hugo-book 7 | -------------------------------------------------------------------------------- /.vim/coc-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.pylintEnabled": true, 3 | "python.linting.enabled": true 4 | } 5 | -------------------------------------------------------------------------------- /.vimspector.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": { 3 | "launch": { 4 | "adapter": "CodeLLDB", 5 | "configuration": { 6 | "request": "launch", 7 | "program": "${workspaceRoot}/target/debug/senile", 8 | "args": ["collect"] 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /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 = "0.7.15" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.11.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 19 | dependencies = [ 20 | "winapi", 21 | ] 22 | 23 | [[package]] 24 | name = "aquamarine" 25 | version = "0.1.10" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "796971668ab6bfd8970bb1688ba30dd1b59c223bd342b74a3336e641a7b8f0a9" 28 | dependencies = [ 29 | "itertools", 30 | "proc-macro-error", 31 | "proc-macro2", 32 | "quote", 33 | "syn", 34 | ] 35 | 36 | [[package]] 37 | name = "atty" 38 | version = "0.2.14" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 41 | dependencies = [ 42 | "hermit-abi", 43 | "libc", 44 | "winapi", 45 | ] 46 | 47 | [[package]] 48 | name = "autocfg" 49 | version = "1.0.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 52 | 53 | [[package]] 54 | name = "bit-set" 55 | version = "0.5.2" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" 58 | dependencies = [ 59 | "bit-vec", 60 | ] 61 | 62 | [[package]] 63 | name = "bit-vec" 64 | version = "0.6.3" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 67 | 68 | [[package]] 69 | name = "bitflags" 70 | version = "1.2.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 73 | 74 | [[package]] 75 | name = "cfg-if" 76 | version = "1.0.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 79 | 80 | [[package]] 81 | name = "clap" 82 | version = "2.33.3" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 85 | dependencies = [ 86 | "ansi_term", 87 | "atty", 88 | "bitflags", 89 | "strsim", 90 | "textwrap", 91 | "unicode-width", 92 | "vec_map", 93 | ] 94 | 95 | [[package]] 96 | name = "crossbeam" 97 | version = "0.8.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "fd01a6eb3daaafa260f6fc94c3a6c36390abc2080e38e3e34ced87393fb77d80" 100 | dependencies = [ 101 | "cfg-if", 102 | "crossbeam-channel", 103 | "crossbeam-deque", 104 | "crossbeam-epoch", 105 | "crossbeam-queue", 106 | "crossbeam-utils", 107 | ] 108 | 109 | [[package]] 110 | name = "crossbeam-channel" 111 | version = "0.5.0" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" 114 | dependencies = [ 115 | "cfg-if", 116 | "crossbeam-utils", 117 | ] 118 | 119 | [[package]] 120 | name = "crossbeam-deque" 121 | version = "0.8.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" 124 | dependencies = [ 125 | "cfg-if", 126 | "crossbeam-epoch", 127 | "crossbeam-utils", 128 | ] 129 | 130 | [[package]] 131 | name = "crossbeam-epoch" 132 | version = "0.9.3" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" 135 | dependencies = [ 136 | "cfg-if", 137 | "crossbeam-utils", 138 | "lazy_static", 139 | "memoffset", 140 | "scopeguard", 141 | ] 142 | 143 | [[package]] 144 | name = "crossbeam-queue" 145 | version = "0.3.1" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "0f6cb3c7f5b8e51bc3ebb73a2327ad4abdbd119dc13223f14f961d2f38486756" 148 | dependencies = [ 149 | "cfg-if", 150 | "crossbeam-utils", 151 | ] 152 | 153 | [[package]] 154 | name = "crossbeam-utils" 155 | version = "0.8.3" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" 158 | dependencies = [ 159 | "autocfg", 160 | "cfg-if", 161 | "lazy_static", 162 | ] 163 | 164 | [[package]] 165 | name = "either" 166 | version = "1.6.1" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 169 | 170 | [[package]] 171 | name = "fancy-regex" 172 | version = "0.6.0" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "3a3371ac125221b609ce0cf587a53fbb624fb56267bbe85cf1929350043bf360" 175 | dependencies = [ 176 | "bit-set", 177 | "regex", 178 | ] 179 | 180 | [[package]] 181 | name = "hermit-abi" 182 | version = "0.1.18" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 185 | dependencies = [ 186 | "libc", 187 | ] 188 | 189 | [[package]] 190 | name = "itertools" 191 | version = "0.9.0" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" 194 | dependencies = [ 195 | "either", 196 | ] 197 | 198 | [[package]] 199 | name = "itoa" 200 | version = "0.4.7" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 203 | 204 | [[package]] 205 | name = "lazy_static" 206 | version = "1.4.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 209 | 210 | [[package]] 211 | name = "libc" 212 | version = "0.2.87" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "265d751d31d6780a3f956bb5b8022feba2d94eeee5a84ba64f4212eedca42213" 215 | 216 | [[package]] 217 | name = "memchr" 218 | version = "2.3.4" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 221 | 222 | [[package]] 223 | name = "memoffset" 224 | version = "0.6.1" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" 227 | dependencies = [ 228 | "autocfg", 229 | ] 230 | 231 | [[package]] 232 | name = "num_cpus" 233 | version = "1.13.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 236 | dependencies = [ 237 | "hermit-abi", 238 | "libc", 239 | ] 240 | 241 | [[package]] 242 | name = "once_cell" 243 | version = "1.7.2" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" 246 | 247 | [[package]] 248 | name = "proc-macro-error" 249 | version = "1.0.4" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 252 | dependencies = [ 253 | "proc-macro-error-attr", 254 | "proc-macro2", 255 | "quote", 256 | "syn", 257 | "version_check", 258 | ] 259 | 260 | [[package]] 261 | name = "proc-macro-error-attr" 262 | version = "1.0.4" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 265 | dependencies = [ 266 | "proc-macro2", 267 | "quote", 268 | "version_check", 269 | ] 270 | 271 | [[package]] 272 | name = "proc-macro2" 273 | version = "1.0.24" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 276 | dependencies = [ 277 | "unicode-xid", 278 | ] 279 | 280 | [[package]] 281 | name = "quote" 282 | version = "1.0.9" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 285 | dependencies = [ 286 | "proc-macro2", 287 | ] 288 | 289 | [[package]] 290 | name = "regex" 291 | version = "1.4.3" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" 294 | dependencies = [ 295 | "aho-corasick", 296 | "memchr", 297 | "regex-syntax", 298 | "thread_local", 299 | ] 300 | 301 | [[package]] 302 | name = "regex-syntax" 303 | version = "0.6.22" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" 306 | 307 | [[package]] 308 | name = "ryu" 309 | version = "1.0.5" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 312 | 313 | [[package]] 314 | name = "same-file" 315 | version = "1.0.6" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 318 | dependencies = [ 319 | "winapi-util", 320 | ] 321 | 322 | [[package]] 323 | name = "scopeguard" 324 | version = "1.1.0" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 327 | 328 | [[package]] 329 | name = "senile" 330 | version = "0.0.0" 331 | dependencies = [ 332 | "aquamarine", 333 | "clap", 334 | "crossbeam", 335 | "fancy-regex", 336 | "serde", 337 | "serde_json", 338 | "threadpool", 339 | "walkdir", 340 | ] 341 | 342 | [[package]] 343 | name = "serde" 344 | version = "1.0.123" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" 347 | dependencies = [ 348 | "serde_derive", 349 | ] 350 | 351 | [[package]] 352 | name = "serde_derive" 353 | version = "1.0.123" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" 356 | dependencies = [ 357 | "proc-macro2", 358 | "quote", 359 | "syn", 360 | ] 361 | 362 | [[package]] 363 | name = "serde_json" 364 | version = "1.0.64" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" 367 | dependencies = [ 368 | "itoa", 369 | "ryu", 370 | "serde", 371 | ] 372 | 373 | [[package]] 374 | name = "strsim" 375 | version = "0.8.0" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 378 | 379 | [[package]] 380 | name = "syn" 381 | version = "1.0.60" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" 384 | dependencies = [ 385 | "proc-macro2", 386 | "quote", 387 | "unicode-xid", 388 | ] 389 | 390 | [[package]] 391 | name = "textwrap" 392 | version = "0.11.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 395 | dependencies = [ 396 | "unicode-width", 397 | ] 398 | 399 | [[package]] 400 | name = "thread_local" 401 | version = "1.1.3" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" 404 | dependencies = [ 405 | "once_cell", 406 | ] 407 | 408 | [[package]] 409 | name = "threadpool" 410 | version = "1.8.1" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" 413 | dependencies = [ 414 | "num_cpus", 415 | ] 416 | 417 | [[package]] 418 | name = "unicode-width" 419 | version = "0.1.8" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 422 | 423 | [[package]] 424 | name = "unicode-xid" 425 | version = "0.2.1" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 428 | 429 | [[package]] 430 | name = "vec_map" 431 | version = "0.8.2" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 434 | 435 | [[package]] 436 | name = "version_check" 437 | version = "0.9.2" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 440 | 441 | [[package]] 442 | name = "walkdir" 443 | version = "2.3.1" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" 446 | dependencies = [ 447 | "same-file", 448 | "winapi", 449 | "winapi-util", 450 | ] 451 | 452 | [[package]] 453 | name = "winapi" 454 | version = "0.3.9" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 457 | dependencies = [ 458 | "winapi-i686-pc-windows-gnu", 459 | "winapi-x86_64-pc-windows-gnu", 460 | ] 461 | 462 | [[package]] 463 | name = "winapi-i686-pc-windows-gnu" 464 | version = "0.4.0" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 467 | 468 | [[package]] 469 | name = "winapi-util" 470 | version = "0.1.5" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 473 | dependencies = [ 474 | "winapi", 475 | ] 476 | 477 | [[package]] 478 | name = "winapi-x86_64-pc-windows-gnu" 479 | version = "0.4.0" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 482 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "senile" 3 | version = "0.0.0" 4 | authors = ["Alexander Weber "] 5 | edition = "2018" 6 | license = "MIT" 7 | description = "senile" 8 | readme = "docs/README.md" 9 | homepage = "https://replicadse.github.io/senile" 10 | repository = "https://github.com/replicadse/senile" 11 | keywords = ["todo"] 12 | categories = ["command-line-utilities"] 13 | 14 | [features] 15 | default = [] 16 | 17 | [profile] 18 | [dev] 19 | opt-level = 0 20 | [release] 21 | opt-level = 3 22 | 23 | [dependencies] 24 | clap = "^2.33.0" 25 | walkdir = "^2.3.1" 26 | serde = { version = "^1.0.104", features = ["derive"] } 27 | serde_json = "^1.0.45" 28 | fancy-regex = "^0.6.0" 29 | crossbeam = "^0.8.0" 30 | threadpool = "^1.8.1" 31 | aquamarine = "^0.1.9" 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | -------- | ------------------ | 7 | | >= 0.0.0 | ✅ | 8 | | < 0.0.0 | ❌ | 9 | 10 | ## Vulnerability disclosure policy 11 | IF a version is supported and will receive updates is ONLY determined on the state of this document on the main repo's master branch.\ 12 | Report to: [aw@voidpointergroup.com](mailto:aw@voidpointergroup.com)\ 13 | Make sure to encrypt your message! [*Click me for PGP public key*.](https://raw.githubusercontent.com/replicadse/replicadse/master/aw%40voidpointergroup.com.pub)\ 14 | **Please respect the responsible disclosure principles.**\ 15 | Do **not** simply post a vulnerability publicly as issue. These will be deleted.\ 16 | **All** vulnerabilities will be posted and made public at some point _after_ the fix has been propagated. The exact time depends on the type of vulnerability and it's potential consequences. 17 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.13 as build-env 2 | ENV LANG en_US.UTF-8 3 | 4 | ARG VERSION 5 | 6 | USER root 7 | RUN apk update && apk upgrade 8 | 9 | RUN apk add \ 10 | bash \ 11 | coreutils \ 12 | make \ 13 | git \ 14 | curl \ 15 | ca-certificates \ 16 | build-base \ 17 | libc-dev \ 18 | musl-dev \ 19 | alpine-sdk \ 20 | gcc \ 21 | rustup 22 | 23 | ENV PATH=/root/.cargo/bin:"$PATH" 24 | RUN rustup-init -y 25 | RUN rustup default stable 26 | RUN cargo +stable install senile --version=$VERSION 27 | 28 | FROM alpine:3.13 as runtime-env 29 | ENV LANG en_US.UTF-8 30 | RUN apk update && apk upgrade 31 | RUN apk add bash 32 | COPY --from=build-env /root/.cargo/bin/senile /bin/senile 33 | RUN addgroup -g 1000 app && adduser -D -s /bin/sh -u 1000 -G app app 34 | USER app 35 | WORKDIR /app/targets 36 | 37 | ENTRYPOINT [ "/bin/senile" ] 38 | -------------------------------------------------------------------------------- /docs/README.adoc: -------------------------------------------------------------------------------- 1 | = senile 2 | 3 | [cols="1,1,1,1", grid=none, stripes=none] 4 | |=== 5 | |image:https://img.shields.io/crates/v/senile.svg[link=https://crates.io/crates/senile] 6 | |image:https://img.shields.io/crates/d/senile?label=crates.io%20downloads[link=https://crates.io/crates/senile] 7 | |image:https://img.shields.io/crates/d/senile?label=crates.io%20downloads[link=https://crates.io/crates/senile] 8 | |image:https://img.shields.io/badge/AUR-package-blue[link=https://aur.archlinux.org/packages/senile] 9 | 10 | |image:https://img.shields.io/docker/pulls/replicadse/senile[link=https://hub.docker.com/repository/docker/replicadse/senile] 11 | |image:https://github.com/replicadse/senile/workflows/pipeline/badge.svg[link=https://github.com/replicadse/senile/actions?query=workflow%3Apipeline] 12 | |image:https://deps.rs/repo/github/replicadse/senile/status.svg[link=https://deps.rs/repo/github/replicadse/senile] 13 | |image:https://img.shields.io/badge/docs.rs:crate-latest-blue[link=https://docs.rs/crate/senile/latest] 14 | 15 | |image:https://img.shields.io/badge/docs.rs:docs-latest-blue[link=https://docs.rs/senile/latest/senile] 16 | |image:https://img.shields.io/badge/home-GitHub-blue[link=https://github.com/replicadse/senile] 17 | |image:https://img.shields.io/badge/website-GitHub-blue[link=https://replicadse.github.io/senile] 18 | | 19 | |=== 20 | 21 | `senile` is a ToDo statement collection and aggregation program written in Rust. It's primary use case is the collection of open ToDo items within sourcecode, especially within a CI environment. + 22 | It's use shall counteract the habit of writing ToDo statements for eternity and forgetting about them until it's too late (like "ToDo: add security to API endpoint" as comment in the deployed production branch). 23 | 24 | See https://replicadse.github.io/senile[the wiki] for more information. 25 | -------------------------------------------------------------------------------- /docs/todos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import fileinput 3 | import json 4 | 5 | content = '' 6 | for line in fileinput.input(): 7 | content += line 8 | 9 | data = json.loads(content) 10 | 11 | data_assignee = {} 12 | data_prio = {} 13 | 14 | for d in data: 15 | data_assignee[d['assignee']] = [] 16 | data_prio[d['priority']] = [] 17 | for d in data: 18 | data_assignee[d['assignee']].append(d) 19 | data_prio[d['priority']].append(d) 20 | 21 | def print_todo(d): 22 | print('- prio: `{}` +'.format(d['priority'])) 23 | print(' assignee: `{}` +'.format(d['assignee'])) 24 | print(' body: `{}` +'.format(d['body'])) 25 | print(' context: \n\t```\n\t{}\n\t``` +'.format('\n\t'.join([l.strip().strip('\n') for l in d['context']]))) 26 | print(' file: `{}` +'.format(d['file'])) 27 | print(' line: `{}`'.format(d['line'])) 28 | 29 | for assignee in data_assignee.keys(): 30 | print('=== {}'.format(assignee)) 31 | print('') 32 | v = data_assignee[assignee] 33 | v = sorted(v, key=lambda x: x['priority']) 34 | for d in v: 35 | print_todo(d) 36 | -------------------------------------------------------------------------------- /docs/wiki/.gitignore: -------------------------------------------------------------------------------- 1 | public 2 | -------------------------------------------------------------------------------- /docs/wiki/archetypes/default.adoc: -------------------------------------------------------------------------------- 1 | --- 2 | doctype: article 3 | title: "{{ replace .Name "-" " " | title }}" 4 | date: {{ .Date }} // 5 | draft: true 6 | weight: 0 7 | --- 8 | :toc: 9 | -------------------------------------------------------------------------------- /docs/wiki/config.toml: -------------------------------------------------------------------------------- 1 | baseURL = 'https://replicadse.github.io/senile' 2 | languageCode = 'en-us' 3 | title = 'senile wiki' 4 | theme = 'book' 5 | paginate = 10 6 | 7 | [markup] 8 | [markup.asciidocExt] 9 | backend = 'html5' 10 | failureLevel = 'fatal' 11 | noHeaderOrFooter = true 12 | preserveTOC = false 13 | safeMode = 'unsafe' 14 | sectionNumbers = false 15 | trace = false 16 | verbose = false 17 | extensions = ["asciidoctor-kroki"] 18 | 19 | 20 | 21 | # (Optional) Set this to true if you use capital letters in file names 22 | disablePathToLower = true 23 | # (Optional) Set this to true to enable 'Last Modified by' date and git author 24 | # information on 'doc' type pages. 25 | enableGitInfo = true 26 | # (Optional) Theme is intended for documentation use, therefore it doesn't render taxonomy. 27 | # You can remove related files with config below 28 | disableKinds = ['taxonomy', 'taxonomyTerm'] 29 | 30 | [params] 31 | BookTheme = 'auto' 32 | BookToC = true 33 | # BookMenuBundle = '/menu' 34 | BookSection = 'docs' 35 | BookRepo = 'https://github.com/replicadse/senile' 36 | BookDateFormat = 'Jan 2, 2006' 37 | BookSearch = true 38 | BookPortableLinks = true 39 | BookServiceWorker = true 40 | -------------------------------------------------------------------------------- /docs/wiki/content/_index.adoc: -------------------------------------------------------------------------------- 1 | --- 2 | doctype: article 3 | title: Home 4 | date: 2021-10-10 5 | draft: false 6 | weight: 1 7 | --- 8 | :toc: 9 | 10 | == Home 11 | 12 | include::./content/parts/status-table.adoc.part[] 13 | 14 | `senile` is a ToDo statement collection and aggregation program written in Rust. It's primary use case is the collection of open ToDo items within sourcecode, especially within a CI environment. + 15 | It's use shall counteract the habit of writing ToDo statements for eternity and forgetting about them until it's too late (like "ToDo: add security to API endpoint" as comment in the deployed production branch). 16 | -------------------------------------------------------------------------------- /docs/wiki/content/docs/command reference/_index.adoc: -------------------------------------------------------------------------------- 1 | --- 2 | doctype: article 3 | title: Command reference 4 | date: 2021-10-10 5 | draft: false 6 | weight: 4 7 | --- 8 | :toc: 9 | 10 | == Disclaimer 11 | 12 | All features that are marked as `experimental` are _not_ considered a public API and therefore eplicitly not covered by the backwards-compatibility policy inside a major version (see https://semver.org[semver v2]). Use these features on your own risk! 13 | 14 | == Commands 15 | 16 | [cols="1,3,1"] 17 | |=== 18 | |Command|Description|Status 19 | 20 | |help|Prints the help to `STDOUT`|stable 21 | |collect|Collects the todo statements|stable 22 | |=== 23 | 24 | == Application level arguments 25 | 26 | [cols="2,1,2,6"] 27 | |=== 28 | |Name|Short|Long|Description 29 | 30 | |Experimental|-e|--experimental|Activates experimental features that are not stable yet. All features that are marked as experimental are ignored when keeping backwards compatibility inside one major version. 31 | |=== 32 | -------------------------------------------------------------------------------- /docs/wiki/content/docs/command reference/collect.adoc: -------------------------------------------------------------------------------- 1 | --- 2 | doctype: article 3 | title: collect 4 | date: 2021-10-10 5 | draft: false 6 | weight: 1 7 | --- 8 | :toc: 9 | 10 | == `collect` command reference 11 | 12 | [cols="1,1,2,2,2,1,1"] 13 | |=== 14 | |Name|Short|Long|Description|Remark|Status|Multiple 15 | 16 | |Path|-p|--path|The path from which to start traversal.|None|stable|no 17 | |Workers|-w|--workers|The amount of workers (threads) used when parsing the collected files.|None|stable|no 18 | |Filter|-f|--filter|The regex for filtering the files that are to be parsed.|None|stable|no 19 | |Format||--format|The format of the todo statements that are parsed. It is the start literal followed by the end literal, separated with a comma.|Example: `--format="// TODO::(,):"`|stable|yes 20 | |=== 21 | 22 | === Examples 23 | - Basic usage: + 24 | `senile collect` 25 | - Specifying the root directory: + 26 | `senile collect -p ./src` 27 | - Specifying the file filter regex (only .rs files): + 28 | `senile collect -f="\.rs$"` 29 | - Specifying the todo statement format: + 30 | `senile collect --format="## TODO~[[,]]:"` 31 | - Specifying more formats: + 32 | `senile collect --format="// TODO!(,):" --format="## TODO~[[,]]:"` 33 | 34 | === Output 35 | 36 | The `collect` command searches for all todo statements recursively from the given directory/file (tree). It collects information about the todo body, the priority, the file and the line in that file.\ 37 | It will output a json formatted string to `STDOUT` as follows:\ 38 | ```json 39 | [ 40 | { 41 | "prio": "$priority", 42 | "assignee": "$assignee", 43 | "body": "$todo_body", 44 | "context": [ 45 | "$context_line", 46 | "$context_line", 47 | ... 48 | ], 49 | "file": "$fq_relative_file_path", 50 | "line": $line 51 | }, 52 | ... 53 | ] 54 | ``` 55 | NOTE: These entries are string-sorted by their assigned priority. 56 | 57 | 58 | === Default ToDo statement format in code 59 | 60 | The default format is `// TODO!($priority, $assignee, $context_lines_below): $content`.\ 61 | The priority and assignee are self explanatory. You can specify whatever string you want inside them.\ 62 | The `$context_lines_below` argument tells the tool how many lines it shall include _below_ your comment as context. 63 | -------------------------------------------------------------------------------- /docs/wiki/content/docs/installation.adoc: -------------------------------------------------------------------------------- 1 | --- 2 | doctype: article 3 | title: Installation 4 | date: 2021-10-10 5 | draft: false 6 | weight: 3 7 | --- 8 | :toc: 9 | 10 | == Installation 11 | 12 | Installing `senile` can be done on numerous ways. 13 | 14 | === The Rust way (via cargo) 15 | [source] 16 | ---- 17 | cargo install senile 18 | ---- 19 | 20 | === The Arch way via AUR (with https://aur.archlinux.org/packages/yay/[yay]): 21 | [source] 22 | ---- 23 | yay -S senile 24 | ---- 25 | 26 | === The AppImage way (`x86_64` example): 27 | [source] 28 | ---- 29 | curl -fSsL https://github.com/replicadse/senile/releases/latest/download/AppImage.x86_64.tar.gz > senile.AppImage.x86_64.tar.gz 30 | tar -xvzf ./senile.AppImage.x86_64.tar.gz 31 | rm ./senile.AppImage.x86_64.tar.gz 32 | chmod +x senile-x86_64.AppImage 33 | sudo mv senile-x86_64.AppImage /usr/bin/senile 34 | ---- 35 | NOTE: You might want to replace the `x86_64` part in the script above with your architecture. 36 | 37 | === The Docker way 38 | [source] 39 | ---- 40 | docker run --rm -v $HOST_PATH:/app/targets/mnt:ro replicadse/senile:latest collect -p=/app/targets 41 | ---- 42 | -------------------------------------------------------------------------------- /docs/wiki/content/docs/status.adoc: -------------------------------------------------------------------------------- 1 | --- 2 | doctype: article 3 | title: Status 4 | date: 2021-10-10 5 | draft: false 6 | weight: 2 7 | --- 8 | :toc: 9 | 10 | include::./content/parts/status-table.adoc.part[] 11 | -------------------------------------------------------------------------------- /docs/wiki/content/docs/todos.adoc: -------------------------------------------------------------------------------- 1 | --- 2 | doctype: article 3 | title: ToDos 4 | date: 2021-10-10 5 | draft: false 6 | weight: 5 7 | --- 8 | :toc: 9 | 10 | == ToDos 11 | 12 | `senile` uses itself in it's build pipeline. These are the currently open ToDos in the project itself: 13 | 14 | -------------------------------------------------------------------------------- /docs/wiki/content/parts/status-table.adoc.part: -------------------------------------------------------------------------------- 1 | [cols="1,1,1,1", grid=none] 2 | |=== 3 | |image:https://img.shields.io/crates/v/senile.svg[link=https://crates.io/crates/senile] 4 | |image:https://img.shields.io/crates/d/senile?label=crates.io%20downloads[link=https://crates.io/crates/senile] 5 | |image:https://img.shields.io/crates/d/senile?label=crates.io%20downloads[link=https://crates.io/crates/senile] 6 | |image:https://img.shields.io/badge/AUR-package-blue[link=https://aur.archlinux.org/packages/senile] 7 | 8 | |image:https://img.shields.io/docker/pulls/replicadse/senile[link=https://hub.docker.com/repository/docker/replicadse/senile] 9 | |image:https://github.com/replicadse/senile/workflows/pipeline/badge.svg[link=https://github.com/replicadse/senile/actions?query=workflow%3Apipeline] 10 | |image:https://deps.rs/repo/github/replicadse/senile/status.svg[link=https://deps.rs/repo/github/replicadse/senile] 11 | |image:https://img.shields.io/badge/docs.rs:crate-latest-blue[link=https://docs.rs/crate/senile/latest] 12 | |image:https://img.shields.io/badge/docs.rs:docs-latest-blue[link=https://docs.rs/senile/latest/senile] 13 | 14 | |image:https://img.shields.io/badge/home-GitHub-blue[link=https://github.com/replicadse/senile] 15 | |image:https://img.shields.io/badge/website-GitHub-blue[link=https://replicadse.github.io/senile] 16 | | 17 | | 18 | |=== 19 | -------------------------------------------------------------------------------- /docs/wiki/resources/.gitignore: -------------------------------------------------------------------------------- 1 | _gen 2 | -------------------------------------------------------------------------------- /pkg/appimage/.gitignore: -------------------------------------------------------------------------------- 1 | *.AppImage 2 | .DirIcon 3 | -------------------------------------------------------------------------------- /pkg/appimage/senile.AppDir/AppRun: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | BINARY_NAME="senile" 4 | HERE="$(dirname "$(readlink -f "${0}")")" 5 | 6 | exec "$HERE/usr/bin/$BINARY_NAME" "$@" 7 | -------------------------------------------------------------------------------- /pkg/appimage/senile.AppDir/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cchexcode/senile/26909c146c5beac04479447dd0654ba8bfb1db1b/pkg/appimage/senile.AppDir/icon.png -------------------------------------------------------------------------------- /pkg/appimage/senile.AppDir/senile.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=senile 3 | Exec=senile 4 | Icon=icon 5 | Type=Application 6 | Categories=Utility 7 | -------------------------------------------------------------------------------- /pkg/appimage/senile.AppDir/usr/bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cchexcode/senile/26909c146c5beac04479447dd0654ba8bfb1db1b/pkg/appimage/senile.AppDir/usr/bin/.gitkeep -------------------------------------------------------------------------------- /pkg/aur/.gitignore: -------------------------------------------------------------------------------- 1 | *.pkg.tar.zst 2 | .aur-repo 3 | .SRCINFO 4 | pkg 5 | src 6 | -------------------------------------------------------------------------------- /pkg/aur/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: replicadse 2 | pkgname=senile 3 | pkgver=0.0.0 4 | pkgrel=1 5 | epoch= 6 | pkgdesc='collecting ToDo statements from files/directories as JSON format' 7 | arch=('x86_64') 8 | url='https://github.com/replicadse/senile' 9 | license=('MIT') 10 | makedepends=( 11 | 'rust' 12 | 'rustup' 13 | 'cargo' 14 | ) 15 | provides=('senile') 16 | 17 | package() { 18 | cargo +stable install $pkgname --version=$pkgver --root="$pkgdir/usr" --no-track --all-features 19 | } 20 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | condense_wildcard_suffixes=true 2 | format_code_in_doc_comments=true 3 | format_macro_matchers=true 4 | format_strings=true 5 | imports_layout="Vertical" 6 | match_arm_leading_pipes="Always" 7 | match_block_trailing_comma=true 8 | max_width=120 9 | imports_granularity="Crate" 10 | newline_style="Unix" 11 | normalize_comments=true 12 | normalize_doc_attributes=true 13 | overflow_delimited_expr=true 14 | reorder_impl_items=true 15 | group_imports="StdExternalCrate" 16 | type_punctuation_density="Compressed" 17 | unstable_features=true 18 | use_field_init_shorthand=true 19 | use_try_shorthand=true 20 | where_single_line=true 21 | wrap_comments=true 22 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | result::Result, 4 | }; 5 | 6 | use crate::error::{ 7 | ArgumentError, 8 | UnknownCommandError, 9 | }; 10 | 11 | #[derive(Debug)] 12 | /// Combined arguments struct for the invoked command incl. all necessary 13 | /// information. 14 | pub struct CallArgs { 15 | /// The privilege with which the program was called. 16 | pub privilege: Privilege, 17 | /// The subcommand that was called incl. all arguments and parameters. 18 | pub command: Command, 19 | } 20 | 21 | impl CallArgs { 22 | /// Validating the arguments since some commands may only be called with 23 | /// certain privileges, arguments being XOR or similar. 24 | pub fn validate(&self) -> Result<(), Box> { 25 | match self.privilege { 26 | | Privilege::Normal => Ok(()), 27 | | Privilege::Experimental => Ok(()), 28 | } 29 | } 30 | } 31 | 32 | #[derive(Debug)] 33 | /// The privilege. 34 | pub enum Privilege { 35 | /// Normal privileges identify the normal scenario. 36 | Normal, 37 | /// Experimental privileges give access to unstable features. 38 | Experimental, 39 | } 40 | 41 | #[derive(Debug)] 42 | /// The (sub-)command representation for the call args. 43 | pub enum Command { 44 | /// Collect subcommand representation. 45 | Collect { 46 | /// The path from which to crawl. 47 | path: String, 48 | /// The (regex) filter on files in the (sub-)tree below incl. `path`. 49 | filter: String, 50 | /// The amount of worker(-thread)s. 51 | workers: usize, 52 | /// Tuples of start&end literals 53 | literals: Vec<(String, String)>, 54 | }, 55 | } 56 | 57 | /// The type that parses the arguments to the program. 58 | pub struct ClapArgumentLoader {} 59 | 60 | impl ClapArgumentLoader { 61 | /// Parsing the program arguments with the `clap` trait. 62 | pub fn load() -> Result> { 63 | let command = clap::App::new("senile") 64 | .version(env!("CARGO_PKG_VERSION")) 65 | .about("senile") 66 | .author("Weber, Alexander ") 67 | .arg( 68 | clap::Arg::with_name("experimental") 69 | .short("e") 70 | .long("experimental") 71 | .value_name("EXPERIMENTAL") 72 | .help("Enables experimental features that do not count as stable.") 73 | .required(false) 74 | .takes_value(false), 75 | ) 76 | .subcommand( 77 | clap::App::new("collect") 78 | .about("Collects the ToDo items from the given folder.") 79 | .arg( 80 | clap::Arg::with_name("path") 81 | .short("p") 82 | .long("path") 83 | .value_name("PATH") 84 | .help("The path on which to start with collecting.") 85 | .default_value("./") 86 | .multiple(false) 87 | .required(false) 88 | .takes_value(true)) 89 | .arg( 90 | // ^(?!(\.\/node_modules\/|\.\/target\/)).*(\.rs|\.asm)$ --> example for including all 91 | // .rs and .asm files that are not in [./node_modules/ or ./target/] root folders 92 | clap::Arg::with_name("filter") 93 | .short("f") 94 | .long("filter") 95 | .value_name("FILTER") 96 | .help("The regex pattern for filtering the files to include. An example that includes only [.rs | .asm] files if their path does not start with [./node_modules/ | ./target/] is the following line: \n^(?!(\\.\\/node_modules\\/|\\.\\/target\\/)).*(\\.rs|\\.asm)$\n") 97 | .default_value("(?s).*") // match anything 98 | .multiple(false) 99 | .required(false) 100 | .takes_value(true)) 101 | .arg( 102 | clap::Arg::with_name("workers") 103 | .short("w") 104 | .long("workers") 105 | .value_name("WORKERS") 106 | .help("The amount of worker threads used when parsing the file contents.") 107 | .default_value("4") 108 | .multiple(false) 109 | .required(false) 110 | .takes_value(true)) 111 | .arg( 112 | clap::Arg::with_name("format") 113 | .long("format") 114 | .value_name("FORMAT") 115 | .help("The format of the TODO statements in the source. Format separated with commas in the order \"start_literal\", \"end_literal\". To allow more than one format, use this arg multiple times.") 116 | .default_value("// TODO!(,):") 117 | .multiple(true) 118 | .required(false) 119 | .takes_value(true))) 120 | .get_matches(); 121 | 122 | let privilege = if command.is_present("experimental") { 123 | Privilege::Experimental 124 | } else { 125 | Privilege::Normal 126 | }; 127 | 128 | let cmd = if let Some(x) = command.subcommand_matches("collect") { 129 | let formats = x.values_of("format").unwrap(); 130 | let mut literals = Vec::<(String, String)>::new(); 131 | for f in formats { 132 | let format_arg = f.split(",").collect::>(); 133 | if format_arg.len() != 2 { 134 | return Err(Box::new(ArgumentError::new("invalid format"))); 135 | }; 136 | literals.push((format_arg[0].to_owned(), format_arg[1].to_owned())) 137 | } 138 | Command::Collect { 139 | path: x.value_of("path").unwrap().to_owned(), // should be covered by the clap arg being required 140 | filter: x.value_of("filter").unwrap().to_owned(), // same as above 141 | workers: x.value_of("workers").unwrap().parse::()?, // same as above 142 | literals, 143 | } 144 | } else { 145 | return Err(Box::new(UnknownCommandError::new("unknown command"))); 146 | }; 147 | 148 | let callargs = CallArgs { 149 | privilege, 150 | command: cmd, 151 | }; 152 | 153 | callargs.validate()?; 154 | Ok(callargs) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/commands.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | fs, 4 | io::{ 5 | prelude::*, 6 | Cursor, 7 | }, 8 | thread, 9 | }; 10 | 11 | use crossbeam::{ 12 | channel::unbounded, 13 | sync::WaitGroup, 14 | }; 15 | use fancy_regex::Regex; 16 | use threadpool::ThreadPool; 17 | 18 | use crate::{ 19 | crawler::Crawler, 20 | parser::{ 21 | ContentParser, 22 | ContentParserParams, 23 | }, 24 | types::ToDoItem, 25 | }; 26 | 27 | #[cfg_attr(doc, aquamarine::aquamarine)] 28 | /// Function to collect files in a tree and parse their content to match todo 29 | /// statements. The internal workings are described in the following diagram: 30 | /// 31 | /// ```mermaid 32 | /// graph TD 33 | /// crawler([crawler thread]) --> crawler_c([channel]) 34 | /// collector([collector]) --> stdout([stdout]) 35 | /// subgraph threadpool[thread pool] 36 | /// thread_0([thread 0]) 37 | /// thread_1([thread 1]) 38 | /// thread_n([thread n]) 39 | /// end 40 | /// thread_0 -. consume .-> crawler_c 41 | /// thread_1 -. consume .-> crawler_c 42 | /// thread_n -. consume .-> crawler_c 43 | /// thread_0 -. publish .-> collector 44 | /// thread_1 -. publish .-> collector 45 | /// thread_n -. publish .-> collector 46 | /// ``` 47 | pub fn collect( 48 | path: String, 49 | filter: String, 50 | workers: usize, 51 | literals: Vec<(String, String)>, 52 | ) -> Result<(), Box> { 53 | let (sender_crawler, receiver_crawler) = unbounded(); 54 | 55 | // fire and forget thread 56 | let crawler_thread = thread::spawn(move || { 57 | let matcher = Regex::new(&filter).unwrap(); 58 | let crawler = Crawler::new(&matcher); 59 | crawler.crawl(&path, sender_crawler).unwrap(); 60 | }); 61 | 62 | let wg = WaitGroup::new(); 63 | let (sender_parser, receiver_parser) = unbounded(); 64 | 65 | let pool = ThreadPool::new(workers); 66 | 67 | for s in receiver_crawler.iter() { 68 | let thread_wg = wg.clone(); 69 | let thread_sender = sender_parser.clone(); 70 | let thread_literals = literals.clone(); 71 | pool.execute(move || { 72 | let dothis = move || -> Result<(), Box> { 73 | let content = fs::read(&s)?; 74 | let mut cursor = Cursor::new(content); 75 | let mut parser = ContentParser::new(&mut cursor, ContentParserParams { 76 | file: s, 77 | literals: thread_literals, 78 | }) 79 | .unwrap(); 80 | let todos = parser.parse()?; 81 | for t in todos { 82 | thread_sender.send(t)?; 83 | } 84 | Ok(()) 85 | }; 86 | dothis().unwrap_or_default(); 87 | drop(thread_wg); 88 | }); 89 | } 90 | crawler_thread.join().expect("the crawler thread has panicked"); 91 | 92 | let mut all_todos = Vec::::new(); 93 | // drop orginal sender_parser to eliminate the +1 original copy from num_workers 94 | // + 1 (original) 95 | drop(sender_parser); 96 | for todo in receiver_parser { 97 | all_todos.push(todo); 98 | } 99 | all_todos.sort_by(|a, b| a.priority.cmp(&b.priority)); 100 | wg.wait(); 101 | 102 | // let output = serde_json::to_vec_pretty(&all_todos)?; 103 | let output = serde_json::to_vec(&all_todos)?; 104 | let stdout = std::io::stdout(); 105 | let mut stdout_lock = stdout.lock(); 106 | stdout_lock.write(&output)?; 107 | stdout_lock.flush()?; 108 | Ok(()) 109 | } 110 | -------------------------------------------------------------------------------- /src/crawler.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use crossbeam::channel::Sender; 4 | use fancy_regex::Regex; 5 | use walkdir::WalkDir; 6 | 7 | /// The crawler type that goes through a (sub-)tree of a file/directory (inode) 8 | /// and recursively gets files. 9 | pub struct Crawler<'s> { 10 | matcher: &'s Regex, 11 | } 12 | impl<'s> Crawler<'s> { 13 | /// Constructor for the crawler. 14 | /// Accepting the regex which shall match the (fully qualified, relative) 15 | /// file names. 16 | pub fn new(matcher: &'s Regex) -> Self { 17 | Self { matcher } 18 | } 19 | 20 | /// Executing the crawl, sending the results during the operation through 21 | /// the sender argument. 22 | pub fn crawl(&self, path: &str, sender: Sender) -> Result<(), Box> { 23 | for entry in WalkDir::new(path) 24 | .into_iter() 25 | .filter_map(Result::ok) 26 | .filter(|e| !e.file_type().is_dir()) 27 | { 28 | let entry_path = entry.into_path(); 29 | let entry_path_str = entry_path.to_str().unwrap(); 30 | if !self.matcher.is_match(entry_path_str)? { 31 | continue; 32 | } 33 | sender.send(entry_path_str.to_owned())?; 34 | } 35 | Ok(()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | macro_rules! make_error { 2 | ($name:ident) => { 3 | #[derive(Debug, Clone)] 4 | /// An error type. 5 | pub struct $name { 6 | details: String, 7 | } 8 | 9 | impl $name { 10 | /// Error type constructor. 11 | pub fn new(details: &str) -> Self { 12 | Self { 13 | details: details.to_owned(), 14 | } 15 | } 16 | } 17 | 18 | impl std::fmt::Display for $name { 19 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 20 | f.write_str(&self.details) 21 | } 22 | } 23 | 24 | impl std::error::Error for $name {} 25 | }; 26 | } 27 | 28 | make_error!(UnknownCommandError); 29 | make_error!(ArgumentError); 30 | make_error!(ParserError); 31 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | //! senile 3 | //! 4 | //! MIT licensed 5 | //! 6 | //! senile is a program for detecting parameterized literals in files. It is not 7 | //! incredibly fast because it has not been optimized for speed but for features 8 | //! (at least in the state it is in right now). Nevertheless, it has quite good 9 | //! performance when going through hundreds or thousands of files due to it's 10 | //! parallelized file crawling / parallel parsing capability. 11 | //! 12 | //! The OG use case was simply for myself as I like to write TODO statements 13 | //! right into the source code or other resources. This can lead to me 14 | //! forgetting that I put them there and they vanish into the infinite hunting 15 | //! grounds. I COULD have used `ripgrep` for such thing but I like to 16 | //! parameterize the todos a bit more (incl. assignee, priority and such) and I 17 | //! really wanted a nice-to-parse output format like JSON which is used right 18 | //! now as format for the `STDOUT` output. 19 | //! 20 | //! For more information on pretty much everything, consider checking the wiki for it . 21 | 22 | /// Responsible for parsing the arguments to commands. 23 | pub mod args; 24 | /// Declaration of all possible commands. 25 | pub mod commands; 26 | /// Responsible for crawling through a file/directory (tree). 27 | pub mod crawler; 28 | /// Error declarations. 29 | pub mod error; 30 | /// Responsible for parsing files (lines). 31 | pub mod parser; 32 | /// Declaration of basic types like the ToDo item itself. 33 | pub mod types; 34 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | 3 | //! senile 4 | 5 | use std::error::Error; 6 | 7 | mod args; 8 | mod commands; 9 | mod crawler; 10 | mod error; 11 | mod parser; 12 | mod types; 13 | 14 | use crate::{ 15 | args::{ 16 | ClapArgumentLoader, 17 | Command, 18 | }, 19 | commands::collect, 20 | }; 21 | 22 | // TODO!(aw,min,1): Wow, it actually works! 23 | fn main() -> Result<(), Box> { 24 | let args = ClapArgumentLoader::load()?; 25 | match args.command { 26 | | Command::Collect { 27 | path, 28 | filter, 29 | workers, 30 | literals, 31 | } => { 32 | collect(path, filter, workers, literals)?; 33 | Ok(()) 34 | }, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | error::Error, 3 | io::{ 4 | BufRead, 5 | Cursor, 6 | }, 7 | }; 8 | 9 | use fancy_regex::Regex; 10 | 11 | use crate::{ 12 | error::ParserError, 13 | types::ToDoItem, 14 | }; 15 | 16 | const C_TODO_PARAM_SEPARATOR: &str = ","; 17 | const C_TODO_PARAM_COUNT: usize = 3; 18 | 19 | /// Parameters for the parser. 20 | pub struct ContentParserParams { 21 | /// The file (path) for todo item context. 22 | pub file: String, 23 | /// The literals for finding the tokens. 24 | pub literals: Vec<(String, String)>, 25 | } 26 | /// The parser. 27 | pub struct ContentParser<'s> { 28 | cursor: &'s mut Cursor>, 29 | params: ContentParserParams, 30 | exprs: Regex, 31 | } 32 | impl<'s> ContentParser<'s> { 33 | /// Creates a new parser struct with the given parameters. 34 | pub fn new(cursor: &'s mut Cursor>, params: ContentParserParams) -> Result> { 35 | let params_regex_str = &format!(".+{}", C_TODO_PARAM_SEPARATOR) 36 | .repeat(C_TODO_PARAM_COUNT) 37 | .trim_end_matches(C_TODO_PARAM_SEPARATOR) 38 | .to_string(); 39 | let mut regex_parts = String::new(); 40 | for lit in ¶ms.literals { 41 | regex_parts.push_str("("); 42 | let mut part = String::new(); 43 | part.push_str(&lit.0); 44 | part.push_str(params_regex_str); 45 | part.push_str(&lit.1); 46 | part.push_str(".*"); 47 | regex_parts.push_str(&part); 48 | regex_parts.push_str(")|"); 49 | } 50 | regex_parts = regex_parts.trim_end_matches("|").to_owned(); 51 | let exprs = Regex::new(®ex_parts)?; 52 | Ok(Self { cursor, params, exprs }) 53 | } 54 | 55 | /// Begins parsing the content. 56 | /// Returns a result of a vector of found ToDo items. 57 | pub fn parse(&mut self) -> Result, Box> { 58 | let mut todos = Vec::::new(); 59 | let mut buf = String::new(); 60 | let mut line = 0u32; 61 | loop { 62 | match self.cursor.read_line(&mut buf) { 63 | | Ok(size) => { 64 | if size <= 0 { 65 | break; 66 | } 67 | }, 68 | | Err(_) => continue, 69 | } 70 | 71 | buf = buf.trim().to_owned(); 72 | if let Ok(res) = self.parse_buf(line, &buf) { 73 | match res { 74 | | Some(v) => { 75 | todos.push(v); 76 | }, 77 | | None => {}, 78 | } 79 | } 80 | 81 | buf.clear(); 82 | line += 1; 83 | } 84 | Ok(todos) 85 | } 86 | 87 | // |01234567 // TODO!(a, b, c): test 88 | // ^ ^ ^ ^ 89 | // 0 9 18 25 90 | 91 | fn parse_buf(&mut self, line: u32, buf: &str) -> Result, Box> { 92 | if let Some(caps) = self.exprs.captures(buf)? { 93 | for cap in caps.iter() { 94 | if let Some(cap_some) = cap { 95 | let substr = &buf[cap_some.start()..cap_some.end()]; 96 | match self.parse_stmt(line, substr) { 97 | | Ok(v) => return Ok(Some(v)), 98 | | Err(e) => return Err(e), 99 | } 100 | } 101 | } 102 | } 103 | Ok(None) 104 | } 105 | 106 | fn parse_stmt(&mut self, line: u32, buf: &str) -> Result> { 107 | let p_error = ParserError::new(&format!("failed to parse line {} in file {}", line, self.params.file)); 108 | for match_pair in &self.params.literals { 109 | if let Some(start_idx) = buf.find(&match_pair.0) { 110 | let min_todo_length = match_pair.0.len() 111 | + C_TODO_PARAM_COUNT 112 | + (C_TODO_PARAM_SEPARATOR.len() * C_TODO_PARAM_COUNT - 1) 113 | + match_pair.1.len(); 114 | let sub_buf = &buf[start_idx..]; 115 | if sub_buf.len() < min_todo_length { 116 | return Err(Box::new(p_error.clone())); 117 | } 118 | let params_start_idx = match_pair.0.len(); // index of first char of params 119 | let params_close_idx = sub_buf[params_start_idx..] 120 | .find(&match_pair.1) 121 | .ok_or(Box::new(p_error.clone()))? 122 | + params_start_idx; 123 | let parameters = &mut sub_buf[params_start_idx..params_close_idx].split(C_TODO_PARAM_SEPARATOR); 124 | let prio = parameters.next().ok_or(Box::new(p_error.clone()))?.trim(); 125 | let assignee = parameters.next().ok_or(Box::new(p_error.clone()))?.trim(); 126 | let next_lines = parameters.next().ok_or(Box::new(p_error.clone()))?.trim(); 127 | let next_lines_nr = next_lines.parse::()?; 128 | let content = sub_buf[params_close_idx + &match_pair.1.len()..].trim(); 129 | let mut context = Vec::::new(); 130 | for _ in 0..next_lines_nr { 131 | let mut line_buf = String::new(); 132 | if let Ok(size) = self.cursor.read_line(&mut line_buf) { 133 | if size <= 0 { 134 | break; 135 | } 136 | context.push(line_buf); 137 | } 138 | } 139 | 140 | return Ok(ToDoItem { 141 | priority: prio.to_owned(), 142 | body: content.to_owned(), 143 | assignee: assignee.to_owned(), 144 | context, 145 | file: self.params.file.to_owned(), 146 | line, 147 | }); 148 | } 149 | } 150 | Err(Box::new(p_error.clone())) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | use serde::{ 2 | Deserialize, 3 | Serialize, 4 | }; 5 | 6 | /// The typed todo item. 7 | #[derive(Debug, Serialize, Deserialize)] 8 | pub struct ToDoItem { 9 | /// The free-text priority. Can be something like ['min', 'max', '0', '1'] 10 | /// or whatever. 11 | pub priority: String, 12 | /// Main body of the item. This is intended to contain the majority of the 13 | /// free text. 14 | pub body: String, 15 | /// Who this todo belongs to. 16 | pub assignee: String, 17 | /// Some context for the item (lines below it's declaration). 18 | pub context: Vec, 19 | /// The (relative) file path where it has been collected. 20 | pub file: String, 21 | /// Line in the file at which the declaration stood. 22 | pub line: u32, 23 | } 24 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | from invoke import task, Collection 2 | 3 | @task 4 | def task_x_exec(c, command): 5 | c.run(command) 6 | 7 | @task 8 | def task_x_build(c, release=False): 9 | flags = [] 10 | if release: 11 | flags.append('--release') 12 | c.run('cargo build ' + ' '.join(flags)) 13 | 14 | @task 15 | def task_x_format(c): 16 | c.run('cargo fmt --all') 17 | 18 | @task 19 | def task_x_scan(c): 20 | c.run('cargo fmt --all -- --check') 21 | 22 | @task 23 | def task_other_installhooks(c): 24 | c.run('rm -rf .git/hooks') 25 | c.run('ln -s ./scripts/git-hooks .git/hooks') 26 | c.run('chmod -R +x ./scripts/*') 27 | 28 | @task 29 | def task_ci_updateversion(c, version): 30 | c.run(f'''sed 's/version = "0.0.0"/version = "'{version}'"/g' Cargo.toml > Cargo.toml.tmp''') 31 | c.run('mv Cargo.toml.tmp Cargo.toml') 32 | c.run(f'''sed 's/pkgver=0.0.0/pkgver='{version}'/g' pkg/aur/PKGBUILD > pkg/aur/PKGBUILD.tmp''') 33 | c.run('mv pkg/aur/PKGBUILD.tmp pkg/aur/PKGBUILD') 34 | 35 | ns = Collection() 36 | 37 | ns_x = Collection('x') 38 | ns_x.add_task(task_x_exec, 'exec') 39 | ns_x.add_task(task_x_build, 'build') 40 | ns_x.add_task(task_x_format, 'fmt') 41 | ns_x.add_task(task_x_scan, 'scan') 42 | ns.add_collection(ns_x, 'x') 43 | 44 | ns_other = Collection('other') 45 | ns_other.add_task(task_other_installhooks, 'install-hooks') 46 | ns.add_collection(ns_other, 'other') 47 | 48 | ns_ci = Collection('ci') 49 | ns_ci.add_task(task_ci_updateversion, 'update-version') 50 | ns.add_collection(ns_ci, 'ci') 51 | --------------------------------------------------------------------------------