├── docs ├── .lock ├── crates.js ├── cargo_auto │ ├── sidebar-items.js │ ├── generic_functions_mod │ │ ├── sidebar-items.js │ │ ├── constant.RED.html │ │ ├── constant.BLUE.html │ │ ├── constant.RESET.html │ │ ├── constant.GREEN.html │ │ ├── constant.YELLOW.html │ │ ├── fn.tracing_init.html │ │ └── index.html │ ├── fn.main.html │ ├── fn.main_returns_anyhow_result.html │ └── all.html ├── static.files │ ├── favicon-32x32-6580c154.png │ ├── FiraMono-Medium-86f75c8c.woff2 │ ├── FiraSans-Italic-81dc35de.woff2 │ ├── FiraSans-Medium-e1aa3f0a.woff2 │ ├── FiraMono-Regular-87c26294.woff2 │ ├── FiraSans-Regular-0fe48ade.woff2 │ ├── NanumBarunGothic-13b3dcba.ttf.woff2 │ ├── SourceCodePro-It-fc8b9304.ttf.woff2 │ ├── SourceSerif4-It-ca3b17ed.ttf.woff2 │ ├── FiraSans-MediumItalic-ccf7e434.woff2 │ ├── SourceSerif4-Bold-6d4fd4c0.ttf.woff2 │ ├── SourceCodePro-Regular-8badfe75.ttf.woff2 │ ├── SourceSerif4-Regular-6b053e98.ttf.woff2 │ ├── SourceSerif4-Semibold-457a13ac.ttf.woff2 │ ├── SourceCodePro-Semibold-aa29a496.ttf.woff2 │ ├── LICENSE-MIT-23f18e03.txt │ ├── normalize-9960930a.css │ ├── scrape-examples-5e967b76.js │ ├── COPYRIGHT-7fb11f4e.txt │ ├── rust-logo-9a9549ea.svg │ ├── src-script-813739b1.js │ ├── favicon-044be391.svg │ ├── FiraSans-LICENSE-05ab6dbd.txt │ ├── SourceCodePro-LICENSE-67f54ca7.txt │ ├── SourceSerif4-LICENSE-a2cfd9d5.md │ ├── NanumBarunGothic-LICENSE-a37d393b.txt │ └── storage-68b7e25d.js ├── index.html ├── src-files.js ├── search.desc │ └── cargo_auto │ │ └── cargo_auto-desc-0-.js ├── search-index.js ├── settings.html └── help.html ├── rustfmt.toml ├── automation_tasks_rs ├── rustfmt.toml ├── crates_io_config.json ├── github_api_config.json ├── src │ ├── encrypt_decrypt_with_ssh_key_mod │ │ └── mod.rs │ ├── build_lib_mod.rs │ ├── cargo_auto_lib │ │ ├── auto_doc_tidy_html_mod.rs │ │ ├── error_mod.rs │ │ ├── auto_semver_or_date_mod.rs │ │ ├── auto_helper_functions_mod.rs │ │ ├── auto_delete_old_js_snippets_mod.rs │ │ ├── auto_check_micro_xml_mod.rs │ │ ├── utils_mod.rs │ │ ├── auto_copy_files_to_strings_mod.rs │ │ ├── auto_git_mod.rs │ │ ├── auto_cargo_toml_mod.rs │ │ ├── auto_github_mod.rs │ │ ├── auto_semver_mod.rs │ │ └── auto_cargo_toml_to_md_mod.rs │ ├── build_cli_bin_win_mod.rs │ ├── build_cli_bin_musl_mod.rs │ ├── build_cli_bin_mod.rs │ ├── build_wasm_mod.rs │ └── utils_mod.rs ├── .gitignore ├── LICENSE ├── .vscode │ └── settings.json └── Cargo.toml ├── images ├── cargo_auto_new.png ├── cargo_auto_build.png ├── cargo_auto_screenshot.png ├── logo │ ├── logo_cargo_auto_1028x1028.png │ ├── logo_cargo_auto_512x512.jpg │ └── logo_cargo_auto_512x512.png └── cargo_auto_github_api_background_965x482.png ├── .gitattributes ├── .gitignore ├── LICENSE ├── src ├── utils_mod.rs ├── bin │ └── cargo-auto │ │ └── generic_functions_mod.rs ├── template_new_cli_mod.rs ├── outside_of_rust_project_mod.rs ├── file_hashes_mod.rs └── template_new_wasm_mod.rs ├── .github └── workflows │ ├── docs_pages.yml │ ├── rust_fmt_auto_build_test.yml │ └── clear_all_cache.yml ├── .vscode └── settings.json ├── Cargo.toml ├── DEVELOPMENT.md └── RELEASES.md /docs/.lock: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 140 2 | newline_style="Unix" 3 | -------------------------------------------------------------------------------- /automation_tasks_rs/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 140 2 | newline_style="Unix" 3 | -------------------------------------------------------------------------------- /docs/crates.js: -------------------------------------------------------------------------------- 1 | window.ALL_CRATES = ["cargo_auto"]; 2 | //{"start":21,"fragment_lengths":[12]} -------------------------------------------------------------------------------- /automation_tasks_rs/crates_io_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "crates_io_private_key_file_name": "crates_io_secret_token_ssh_1" 3 | } -------------------------------------------------------------------------------- /images/cargo_auto_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/images/cargo_auto_new.png -------------------------------------------------------------------------------- /images/cargo_auto_build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/images/cargo_auto_build.png -------------------------------------------------------------------------------- /docs/cargo_auto/sidebar-items.js: -------------------------------------------------------------------------------- 1 | window.SIDEBAR_ITEMS = {"fn":["main","main_returns_anyhow_result"],"mod":["generic_functions_mod"]}; -------------------------------------------------------------------------------- /images/cargo_auto_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/images/cargo_auto_screenshot.png -------------------------------------------------------------------------------- /images/logo/logo_cargo_auto_1028x1028.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/images/logo/logo_cargo_auto_1028x1028.png -------------------------------------------------------------------------------- /images/logo/logo_cargo_auto_512x512.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/images/logo/logo_cargo_auto_512x512.jpg -------------------------------------------------------------------------------- /images/logo/logo_cargo_auto_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/images/logo/logo_cargo_auto_512x512.png -------------------------------------------------------------------------------- /docs/cargo_auto/generic_functions_mod/sidebar-items.js: -------------------------------------------------------------------------------- 1 | window.SIDEBAR_ITEMS = {"constant":["BLUE","GREEN","RED","RESET","YELLOW"],"fn":["tracing_init"]}; -------------------------------------------------------------------------------- /docs/static.files/favicon-32x32-6580c154.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/favicon-32x32-6580c154.png -------------------------------------------------------------------------------- /docs/static.files/FiraMono-Medium-86f75c8c.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/FiraMono-Medium-86f75c8c.woff2 -------------------------------------------------------------------------------- /docs/static.files/FiraSans-Italic-81dc35de.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/FiraSans-Italic-81dc35de.woff2 -------------------------------------------------------------------------------- /docs/static.files/FiraSans-Medium-e1aa3f0a.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 -------------------------------------------------------------------------------- /docs/static.files/FiraMono-Regular-87c26294.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/FiraMono-Regular-87c26294.woff2 -------------------------------------------------------------------------------- /docs/static.files/FiraSans-Regular-0fe48ade.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/FiraSans-Regular-0fe48ade.woff2 -------------------------------------------------------------------------------- /images/cargo_auto_github_api_background_965x482.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/images/cargo_auto_github_api_background_965x482.png -------------------------------------------------------------------------------- /docs/static.files/NanumBarunGothic-13b3dcba.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/NanumBarunGothic-13b3dcba.ttf.woff2 -------------------------------------------------------------------------------- /docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 -------------------------------------------------------------------------------- /docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 -------------------------------------------------------------------------------- /docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 -------------------------------------------------------------------------------- /docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 -------------------------------------------------------------------------------- /docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 -------------------------------------------------------------------------------- /docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 -------------------------------------------------------------------------------- /docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 -------------------------------------------------------------------------------- /docs/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/automation-tasks-rs/cargo-auto/HEAD/docs/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2 -------------------------------------------------------------------------------- /automation_tasks_rs/github_api_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "github_app_name":"cargo-auto-github-api", 3 | "client_id":"Iv23lizs2nqJTi4CrIzS", 4 | "github_api_private_key_file_name":"github_bestia_dev_api_oauth2_ssh_1" 5 | } -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/src-files.js: -------------------------------------------------------------------------------- 1 | createSrcSidebar('[["cargo_auto",["",[],["bin_cli_functions_mod.rs","main.rs"]]],["cargo_auto",["",[],["generic_functions_mod.rs","main.rs"]]]]'); 2 | //{"start":19,"fragment_lengths":[61,62]} -------------------------------------------------------------------------------- /docs/search.desc/cargo_auto/cargo_auto-desc-0-.js: -------------------------------------------------------------------------------- 1 | searchState.loadedDescShard("cargo_auto", 0, "cargo-auto\nFunctions to work with CLI binary executable projects.\nFunction main() returns ExitCode.\nFunction main() returns anyhow::Result.\nANSI color\nANSI color\nANSI color\nANSI color\nANSI color\nInitialize tracing to file logs/cargo_auto.log. \\") -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Specific git config for the project 2 | 3 | # Declare files that will always have LF line endings on checkout. 4 | *.rs text eol=lf 5 | *.toml text eol=lf 6 | *.md text eol=lf 7 | *.json text eol=lf 8 | *.json5 text eol=lf 9 | *.lock text eol=lf 10 | *.yml text eol=lf 11 | *.html text eol=lf 12 | *.js text eol=lf 13 | *.css text eol=lf 14 | LICENSE text eol=lf 15 | .gitignore text eol=lf 16 | .gitattributes text eol=lf -------------------------------------------------------------------------------- /automation_tasks_rs/src/encrypt_decrypt_with_ssh_key_mod/mod.rs: -------------------------------------------------------------------------------- 1 | // encrypt_decrypt_with_ssh_key_mod/mod.rs 2 | 3 | //! The mod.rs generic name is difficult to find and maintain. 4 | //! 5 | //! Here I will have only code that uses other modules with meaningful names. 6 | //! Don't change this code, so it can be updated regularly with 7 | //! cargo auto update_automation_tasks_rs 8 | //! If you want to customize it, copy the code into main.rs and modify it there. 9 | 10 | pub mod crates_io_api_token_mod; 11 | pub mod encrypt_decrypt_mod; 12 | pub mod github_api_token_with_oauth2_mod; 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore will forbid for these files and folders to be committed in git. 2 | 3 | # Compiling artifacts are generated by cargo. 4 | /target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries. 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt. 11 | **/*.rs.bk 12 | 13 | # Result of compilation does not need to go to repository. 14 | /pkg/ 15 | 16 | # Not needed in commits, but also not a problem if they are committed. 17 | /.automation_tasks_rs_file_hashes.json 18 | /.file_hashes.json 19 | /.old_metadata.json 20 | 21 | # Logs for tracing in development are not committed. 22 | /logs/ 23 | 24 | # Temporary files are not needed in the repository. 25 | /tmp/ 26 | -------------------------------------------------------------------------------- /automation_tasks_rs/.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore will forbid for these files and folders to be committed in git. 2 | 3 | # Compiling artifacts are generated by cargo. 4 | /target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries. 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt. 11 | **/*.rs.bk 12 | 13 | # Result of compilation does not need to go to repository. 14 | /pkg/ 15 | 16 | # Not needed in commits, but also not a problem if they are committed. 17 | /.automation_tasks_rs_file_hashes.json 18 | /.file_hashes.json 19 | /.old_metadata.json 20 | 21 | # Logs for tracing in development are not committed. 22 | /logs/ 23 | 24 | # Temporary files are not needed in the repository. 25 | /tmp/ 26 | -------------------------------------------------------------------------------- /docs/search-index.js: -------------------------------------------------------------------------------- 1 | var searchIndex = new Map(JSON.parse('[["cargo_auto",{"t":"CHHSSSSSH","n":["bin_cli_functions_mod","main","main_returns_anyhow_result","BLUE","GREEN","RED","RESET","YELLOW","tracing_init"],"q":[[0,"cargo_auto"],[3,"cargo_auto::bin_cli_functions_mod"],[9,"std::process"],[10,"anyhow"]],"i":"`````````","f":"`{{}b}{{}{{f{d}}}}{{}h}00001","D":"Ad","p":[[5,"ExitCode",9],[1,"unit"],[8,"Result",10],[1,"reference",null,null,1]],"r":[],"b":[],"c":"OjAAAAAAAAA=","e":"OjAAAAAAAAA=","P":[]}],["cargo_auto",{"t":"CHHSSSSSH","n":["generic_functions_mod","main","main_returns_anyhow_result","BLUE","GREEN","RED","RESET","YELLOW","tracing_init"],"q":[[0,"cargo_auto"],[3,"cargo_auto::generic_functions_mod"],[9,"std::process"],[10,"anyhow"]],"i":"`````````","f":"`{{}b}{{}{{f{d}}}}{{}h}00001","D":"Ad","p":[[5,"ExitCode",9],[1,"unit"],[8,"Result",10],[1,"reference",null,null,1]],"r":[],"b":[],"c":"OjAAAAAAAAA=","e":"OjAAAAAAAAA=","P":[]}]]')); 2 | if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; 3 | else if (window.initSearch) window.initSearch(searchIndex); 4 | //{"start":39,"fragment_lengths":[444,445]} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 bestia.dev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/static.files/LICENSE-MIT-23f18e03.txt: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /automation_tasks_rs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 bestia.dev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/build_lib_mod.rs: -------------------------------------------------------------------------------- 1 | // build_lib_mod.rs 2 | 3 | //! Functions to build a library crate. 4 | //! 5 | //! Don't change this code, so it can be updated regularly with 6 | //! cargo auto update_automation_tasks_rs 7 | //! If you want to customize it, copy the code into main.rs and modify it there. 8 | 9 | use crate::cargo_auto_lib as cl; 10 | use crate::encrypt_decrypt_with_ssh_key_mod as ende; 11 | 12 | use crate::cargo_auto_lib::CargoTomlPublicApiMethods; 13 | use crate::utils_mod::pos; 14 | use crate::utils_mod::ResultLogError; 15 | #[allow(unused_imports)] 16 | use cl::{BLUE, GREEN, RED, RESET, YELLOW}; 17 | 18 | #[allow(dead_code)] 19 | /// publish to crates.io and git tag 20 | pub fn task_publish_to_crates_io() -> anyhow::Result<(String, String, String)> { 21 | let cargo_toml = cl::CargoToml::read().log(pos!())?; 22 | let package_name = cargo_toml.package_name(); 23 | let version = cargo_toml.package_version(); 24 | // take care of tags 25 | let tag_name_version = cl::git_tag_sync_check_create_push(&version).log(pos!())?; 26 | 27 | // cargo publish with encrypted secret secret_token 28 | ende::crates_io_api_token_mod::publish_to_crates_io().log(pos!())?; 29 | 30 | Ok((tag_name_version, package_name, version)) 31 | } 32 | -------------------------------------------------------------------------------- /src/utils_mod.rs: -------------------------------------------------------------------------------- 1 | // utils_mod.rs 2 | 3 | //! Functions for various utilities. 4 | 5 | /// Macro to get source code position to log errors before propagation. 6 | /// 7 | /// example: read_to_string("x").log(pos!())?; 8 | #[macro_export] 9 | macro_rules! pos { 10 | // `()` indicates that the macro takes no argument. 11 | () => { 12 | // The macro will expand into the contents of this block. 13 | &format!("{}:{}:{}:", file!(), line!(), column!()) 14 | }; 15 | } 16 | 17 | /// Trait to log the error from Result before propagation with ?. 18 | pub trait ResultLogError: Sized { 19 | fn log(self, file_line_column: &str) -> Self; 20 | } 21 | 22 | /// Implements LogError for anyhow::Result. 23 | impl ResultLogError for core::result::Result { 24 | fn log(self, file_line_column: &str) -> Self { 25 | self.inspect_err(|err| tracing::debug!("{} {:?}", file_line_column, err)) 26 | } 27 | } 28 | 29 | /// Run one shell command and return true if success. 30 | pub fn run_shell_command_success(shell_command: &str) -> bool { 31 | if !shell_command.starts_with("echo ") && !shell_command.starts_with("printf ") { 32 | println!(" $ {}", shell_command); 33 | } 34 | match std::process::Command::new("sh").arg("-c").arg(shell_command).status() { 35 | Ok(status) => status.success(), 36 | Err(_err) => false, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/docs_pages.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: docs_pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | - name: Setup Pages 35 | uses: actions/configure-pages@v4 36 | - name: Upload artifact 37 | uses: actions/upload-pages-artifact@v3 38 | with: 39 | # Upload entire repository 40 | path: 'docs' 41 | - name: Deploy to GitHub Pages 42 | id: deployment 43 | uses: actions/deploy-pages@v4 44 | -------------------------------------------------------------------------------- /.github/workflows/rust_fmt_auto_build_test.yml: -------------------------------------------------------------------------------- 1 | name: rust_fmt_auto_build_test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | rust_fmt_auto_build_test: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: cargo fmt -- --check 22 | run: cargo fmt -- --check 23 | 24 | - name: Run cache for rust dependencies 25 | uses: Swatinem/rust-cache@v2.7.7 26 | 27 | - name: Configure sccache 28 | run: printf "RUSTC_WRAPPER=sccache\n" >> $GITHUB_ENV; printf "SCCACHE_GHA_ENABLED=true\n" >> $GITHUB_ENV 29 | 30 | - name: Run sccache-cache for artifacts 31 | uses: mozilla-actions/sccache-action@v0.0.8 32 | 33 | - name: cargo clippy --no-deps 34 | run: cargo clippy --no-deps 35 | 36 | - name: install and cache cargo-auto 37 | uses: baptiste0928/cargo-install@v3.3.0 38 | with: 39 | crate: cargo-auto 40 | 41 | - name: Cache for automation tasks 42 | uses: actions/cache@v4.2.2 43 | with: 44 | path: | 45 | automation_tasks_rs/.file_hashes.json 46 | automation_tasks_rs/target 47 | key: automation_tasks_rs 48 | 49 | - name: cargo auto build 50 | run: cargo auto build 51 | 52 | - name: cargo auto test 53 | run: cargo auto test 54 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_doc_tidy_html_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_doc_tidy_html_mod 2 | 3 | //! Make HTML pretty. 4 | 5 | // region: use statements 6 | 7 | use crate::{ 8 | cargo_auto_lib::error_mod::Result, 9 | utils_mod::{pos, ResultLogError}, 10 | }; 11 | 12 | // endregion: use statements 13 | 14 | /// Pretty HTML for docs. 15 | /// 16 | /// The HTML generated by `cargo doc` is ugly and difficult to `git diff`. 17 | /// Tidy HTML is a HTML checker and formatter installed on most Linuxes. 18 | pub fn auto_doc_tidy_html() -> Result<()> { 19 | // First we check if tidy is installed on the system 20 | // Run a dummy command and write the std/err output to tidy_warnings.txt. 21 | // The command `2>` will overwrite the file and not append like `2>>`. 22 | crate::cargo_auto_lib::run_shell_command_static("tidy xxx 2> docs/tidy_warnings.txt").log(pos!())?; 23 | // Check if it contains `command not found` 24 | let text = std::fs::read_to_string("docs/tidy_warnings.txt").log(pos!())?; 25 | // don't need this file anymore 26 | crate::cargo_auto_lib::run_shell_command_static("rm -f docs/tidy_warnings.txt").log(pos!())?; 27 | if !text.contains("command not found") { 28 | // Use tidy HTML to format the docs/*.html files to be human readable and usable for git diff. 29 | // Options: -m modify file, -q quiet suppress nonessential output, -w wrap at 160, -i indent 2 spaces 30 | // The warnings and errors are appended to the file docs/tidy_warnings.txt 31 | crate::cargo_auto_lib::run_shell_command_static( 32 | r#"find ./docs -name '*.html' -type f -print -exec tidy -mq --tidy-mark no -w 160 -i 2 '{}' \; >> docs/tidy_warnings.txt 2>&1 "#, 33 | ).log(pos!())?; 34 | } 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /docs/static.files/normalize-9960930a.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} -------------------------------------------------------------------------------- /automation_tasks_rs/src/build_cli_bin_win_mod.rs: -------------------------------------------------------------------------------- 1 | // build_cli_bin_win_mod.rs 2 | 3 | //! Functions to cross-build a CLI binary executable from Linux to Windows. 4 | //! 5 | //! Don't change this code, so it can be updated regularly with 6 | //! cargo auto update_automation_tasks_rs 7 | //! If you want to customize it, copy the code into main.rs and modify it there. 8 | 9 | use crate::utils_mod::pos; 10 | use crate::{cargo_auto_lib as cl, utils_mod::ResultLogError}; 11 | 12 | use crate::cargo_auto_lib::CargoTomlPublicApiMethods; 13 | #[allow(unused_imports)] 14 | use cl::{BLUE, GREEN, RED, RESET, YELLOW}; 15 | 16 | #[allow(dead_code)] 17 | /// cargo build --target x86_64-pc-windows-gnu 18 | pub fn task_build() -> anyhow::Result { 19 | let cargo_toml = cl::CargoToml::read().log(pos!())?; 20 | cl::auto_version_increment_semver_or_date().log(pos!())?; 21 | cl::run_shell_command_static("cargo fmt").log(pos!())?; 22 | cl::run_shell_command_static("cargo clippy --no-deps --target x86_64-pc-windows-gnu").log(pos!())?; 23 | cl::run_shell_command_static("cargo build --target x86_64-pc-windows-gnu").log(pos!())?; 24 | Ok(cargo_toml) 25 | } 26 | 27 | #[allow(dead_code)] 28 | /// cargo build --release --target x86_64-pc-windows-gnu 29 | pub fn task_release() -> anyhow::Result { 30 | let cargo_toml = cl::CargoToml::read().log(pos!())?; 31 | cl::auto_version_increment_semver_or_date().log(pos!())?; 32 | cl::auto_cargo_toml_to_md().log(pos!())?; 33 | cl::auto_lines_of_code("").log(pos!())?; 34 | 35 | cl::run_shell_command_static("cargo fmt").log(pos!())?; 36 | cl::run_shell_command_static("cargo clippy --no-deps --target x86_64-pc-windows-gnu").log(pos!())?; 37 | cl::run_shell_command_static("cargo build --release --target x86_64-pc-windows-gnu").log(pos!())?; 38 | 39 | Ok(cargo_toml) 40 | } 41 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/build_cli_bin_musl_mod.rs: -------------------------------------------------------------------------------- 1 | // build_cli_bin_musl_mod.rs 2 | 3 | //! Functions to build a standalone CLI binary executable from Linux with musl. 4 | //! 5 | //! Don't change this code, so it can be updated regularly with 6 | //! cargo auto update_automation_tasks_rs 7 | //! If you want to customize it, copy the code into main.rs and modify it there. 8 | 9 | use crate::utils_mod::pos; 10 | use crate::{cargo_auto_lib as cl, utils_mod::ResultLogError}; 11 | 12 | use crate::cargo_auto_lib::CargoTomlPublicApiMethods; 13 | #[allow(unused_imports)] 14 | use cl::{BLUE, GREEN, RED, RESET, YELLOW}; 15 | 16 | #[allow(dead_code)] 17 | /// cargo build --target x86_64-unknown-linux-musl 18 | pub fn task_build() -> anyhow::Result { 19 | let cargo_toml = cl::CargoToml::read().log(pos!())?; 20 | cl::auto_version_increment_semver_or_date().log(pos!())?; 21 | cl::run_shell_command_static("cargo fmt").log(pos!())?; 22 | cl::run_shell_command_static("cargo clippy --no-deps --target x86_64-unknown-linux-musl").log(pos!())?; 23 | cl::run_shell_command_static("cargo build --target x86_64-unknown-linux-musl").log(pos!())?; 24 | Ok(cargo_toml) 25 | } 26 | 27 | #[allow(dead_code)] 28 | /// cargo build --release --target x86_64-unknown-linux-musl 29 | pub fn task_release() -> anyhow::Result { 30 | let cargo_toml = cl::CargoToml::read().log(pos!())?; 31 | cl::auto_version_increment_semver_or_date().log(pos!())?; 32 | cl::auto_cargo_toml_to_md().log(pos!())?; 33 | cl::auto_lines_of_code("").log(pos!())?; 34 | 35 | cl::run_shell_command_static("cargo fmt").log(pos!())?; 36 | cl::run_shell_command_static("cargo clippy --no-deps --target x86_64-unknown-linux-musl").log(pos!())?; 37 | cl::run_shell_command_static("cargo build --release --target x86_64-unknown-linux-musl").log(pos!())?; 38 | 39 | Ok(cargo_toml) 40 | } 41 | -------------------------------------------------------------------------------- /automation_tasks_rs/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "titleBar.activeForeground": "#fff", 4 | "titleBar.inactiveForeground": "#ffffffcc", 5 | "titleBar.activeBackground": "#a81c1c", 6 | "titleBar.inactiveBackground": "#630b0bcc" 7 | }, 8 | "spellright.language": [ 9 | "en" 10 | ], 11 | "spellright.documentTypes": [ 12 | "markdown", 13 | "latex", 14 | "plaintext" 15 | ], 16 | "files.associations": { 17 | "LICENSE": "plain text" 18 | }, 19 | "rust-analyzer.showUnlinkedFileNotification": false, 20 | "terminal.integrated.commandsToSkipShell": [ 21 | // Ensure the toggle sidebar visibility ctrl+b keybinding skips the terminal shell 22 | "workbench.action.toggleSidebarVisibility", 23 | ], 24 | "cSpell.words": [ 25 | "Alla", 26 | "alloc", 27 | "appender", 28 | "bestia", 29 | "bestiadev", 30 | "camino", 31 | "chrono", 32 | "commitish", 33 | "creds", 34 | "crossplatform", 35 | "CRUSTDE", 36 | "Datelike", 37 | "decryptor", 38 | "dodrio", 39 | "encryptor", 40 | "ende", 41 | "endregion", 42 | "enduml", 43 | "filehash", 44 | "filetime", 45 | "HEXLOWER", 46 | "keygen", 47 | "lizs", 48 | "microxml", 49 | "mmdd", 50 | "Nazdravlje", 51 | "new_cli", 52 | "octocrab", 53 | "passcode", 54 | "pathbuff", 55 | "plantuml", 56 | "Prost", 57 | "reqwest", 58 | "rustdevuser", 59 | "rustlang", 60 | "rustprojects", 61 | "serde", 62 | "sshadd", 63 | "startuml", 64 | "struct", 65 | "subsecond", 66 | "substack", 67 | "thiserror", 68 | "Timelike", 69 | "Unpadded", 70 | "urlencoding", 71 | "zcvf", 72 | "zdravje", 73 | "zeroize" 74 | ] 75 | } -------------------------------------------------------------------------------- /.github/workflows/clear_all_cache.yml: -------------------------------------------------------------------------------- 1 | name: cleanup caches on main 2 | 3 | # Configure Manual Trigger with workflow_dispatch 4 | on: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | cleanup: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | # `actions:write` permission is required to delete caches 12 | # See also: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id 13 | actions: write 14 | contents: read 15 | steps: 16 | - name: checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Cleanup 20 | run: | 21 | gh extension install actions/gh-actions-cache 22 | 23 | REPO=${{ github.repository }} 24 | printf "$REPO\n" 25 | BRANCH=main 26 | printf "$BRANCH\n" 27 | 28 | # loop until the list is empty, because it deletes only 30 per page 29 | has_items=true 30 | while [ "$has_items" = true ] 31 | do 32 | printf "\033[0;33m Fetching list of cache key\n\033[0m\n" 33 | printf "\033[0;32m gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 \n\033[0m\n" 34 | cache_keys=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) 35 | # printf "$cache_keys\n" 36 | if [ -z "$cache_keys" ]; then 37 | printf "\033[0;35m gh actions-cache list returned nothing.\n\033[0m\n" 38 | has_items=false 39 | fi 40 | ## Setting this to not fail the workflow while deleting cache keys. 41 | set +e 42 | for cacheKey in $cache_keys 43 | do 44 | # printf "\033[0;32m gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm\n\033[0m\n" 45 | gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm 46 | done 47 | done 48 | printf "\033[0;33m Done\n\033[0m\n" 49 | env: 50 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | 52 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/error_mod.rs: -------------------------------------------------------------------------------- 1 | // error_mod.rs 2 | 3 | //! Error library for this crate using thiserror. 4 | //! 5 | //! I am using the crate thiserror to create an enum for all library errors. 6 | //! It mostly forwards the source "from" error. 7 | //! The library never writes to the screen, because it contains only the logic. 8 | //! Is the bin project that knows if it is CLI, TUI or GUI and it presents the errors to the user and developer. 9 | //! Then in the bin project I use the crate anyhow. 10 | 11 | /// Enum of possible errors from this library 12 | #[derive(thiserror::Error, Debug)] 13 | pub enum Error { 14 | #[error("SerdeJsonError: {0}")] 15 | SerdeJsonError(#[from] serde_json::Error), 16 | #[error("InfallibleError: {0}")] 17 | InfallibleError(#[from] std::convert::Infallible), 18 | #[error("StdIoError: {0}")] 19 | StdIoError(#[from] std::io::Error), 20 | #[error(transparent)] 21 | ParseIntError(#[from] std::num::ParseIntError), 22 | #[error(transparent)] 23 | InquireError(#[from] inquire::InquireError), 24 | #[error(transparent)] 25 | FromUtf8Error(#[from] std::string::FromUtf8Error), 26 | #[error(transparent)] 27 | RegexError(#[from] regex::Error), 28 | #[error(transparent)] 29 | CargoTomlError(#[from] cargo_toml::Error), 30 | #[error(transparent)] 31 | PatternError(#[from] glob::PatternError), 32 | #[error(transparent)] 33 | GlobError(#[from] glob::GlobError), 34 | #[error(transparent)] 35 | StripPrefixError(#[from] std::path::StripPrefixError), 36 | #[error(transparent)] 37 | ReqwestError(#[from] reqwest::Error), 38 | #[error(transparent)] 39 | SemverError(#[from] semver::Error), 40 | 41 | #[error("{0}")] 42 | ErrorFromString(String), 43 | #[error("{0}")] 44 | ErrorFromStr(&'static str), 45 | //#[error("unknown error")] 46 | //UnknownError, 47 | } 48 | 49 | /// Result type alias with fixed LibError using thiserror 50 | /// 51 | /// It makes simpler to write returns from functions. 52 | pub type Result = core::result::Result; 53 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/build_cli_bin_mod.rs: -------------------------------------------------------------------------------- 1 | // build_cli_bin_mod.rs 2 | 3 | //! Functions to build a CLI binary executable. 4 | //! 5 | //! Don't change this code, so it can be updated regularly with 6 | //! cargo auto update_automation_tasks_rs 7 | //! If you want to customize it, copy the code into main.rs and modify it there. 8 | 9 | use crate::cargo_auto_lib as cl; 10 | 11 | use crate::cargo_auto_lib::CargoTomlPublicApiMethods; 12 | use crate::cargo_auto_lib::ShellCommandLimitedDoubleQuotesSanitizerTrait; 13 | use crate::utils_mod::pos; 14 | use crate::utils_mod::ResultLogError; 15 | #[allow(unused_imports)] 16 | use cl::{BLUE, GREEN, RED, RESET, YELLOW}; 17 | 18 | #[allow(dead_code)] 19 | /// cargo build 20 | pub fn task_build() -> anyhow::Result { 21 | let cargo_toml = cl::CargoToml::read().log(pos!())?; 22 | cl::auto_version_increment_semver_or_date().log(pos!())?; 23 | cl::run_shell_command_static("cargo fmt").log(pos!())?; 24 | cl::run_shell_command_static("cargo clippy --no-deps").log(pos!())?; 25 | cl::run_shell_command_static("cargo build").log(pos!())?; 26 | Ok(cargo_toml) 27 | } 28 | 29 | #[allow(dead_code)] 30 | /// cargo build --release 31 | pub fn task_release() -> anyhow::Result { 32 | let cargo_toml = cl::CargoToml::read().log(pos!())?; 33 | cl::auto_version_increment_semver_or_date().log(pos!())?; 34 | cl::auto_cargo_toml_to_md().log(pos!())?; 35 | cl::auto_lines_of_code("").log(pos!())?; 36 | 37 | cl::run_shell_command_static("cargo fmt").log(pos!())?; 38 | cl::run_shell_command_static("cargo clippy --no-deps").log(pos!())?; 39 | cl::run_shell_command_static("cargo build --release").log(pos!())?; 40 | 41 | // strip only for binary executables 42 | #[cfg(target_family = "unix")] 43 | if std::fs::exists("target/release/{package_name}").log(pos!())? { 44 | cl::ShellCommandLimitedDoubleQuotesSanitizer::new(r#"strip "target/release/{package_name}" "#) 45 | .log(pos!())? 46 | .arg("{package_name}", &cargo_toml.package_name()) 47 | .log(pos!())? 48 | .run() 49 | .log(pos!())?; 50 | } 51 | Ok(cargo_toml) 52 | } 53 | -------------------------------------------------------------------------------- /automation_tasks_rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "automation_tasks_rs" 3 | # binary executable does not need to be SemVer, because nobody depends on it 4 | version = "1.0.0" 5 | authors = ["bestia.dev"] 6 | homepage = "https://bestia.dev" 7 | edition = "2021" 8 | description = "Automation tasks coded in Rust language for the workflow of Rust projects" 9 | publish = false 10 | 11 | [dependencies] 12 | # SemVer rules: Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable. 13 | # WARNING: Although dependencies in Cargo.toml look like a specific version of the crate, they are just like a caret version (^x.y.z) and actually specify 14 | # the minimum version and allow automatic SemVer compatible updates up to a MAJOR increment! Always check the true version with `cargo tree`! 15 | 16 | tracing = "0.1.41" 17 | tracing-subscriber = { version = "0.3.20", features = ["env-filter", "std", "fmt", "time"] } 18 | tracing-appender="0.2.3" 19 | time = {version="0.3.44", features=["macros","local-offset"]} 20 | 21 | anyhow="1.0.100" 22 | reqwest={version="=0.12.24", features=["json","stream", "blocking"]} 23 | serde ={ version= "1.0.228", features=["std","derive"]} 24 | serde_json = "1.0.145" 25 | ssh-key = { version = "0.6.7", features = [ "rsa", "encryption","ed25519"] } 26 | ssh_agent_client_rs_git_bash = "0.0.19" 27 | rsa = { version = "0.9.8", features = ["sha2","pem"] } 28 | zeroize = {version="1.8.2", features=["derive"]} 29 | aes-gcm = "0.10.3" 30 | base64ct = {version = "1.8.0", features = ["alloc"] } 31 | secrecy = "0.10.3" 32 | chrono ={version="0.4.42", default-features=false, features=["now"]} 33 | tokio-util = {version = "0.7.17", features = ["codec"]} 34 | tokio = {version = "1.48.0", features = ["rt","rt-multi-thread","fs"]} 35 | url="2.5.7" 36 | inquire="0.9.1" 37 | home="0.5.12" 38 | crossplatform_path="4.0.1" 39 | 40 | lazy_static="1.5.0" 41 | regex = "1.12.2" 42 | glob = "0.3.3" 43 | reader_for_microxml="2.0.1" 44 | filetime = "0.2.26" 45 | cargo_toml = "0.22.3" 46 | camino = "1.2.1" 47 | thiserror="2.0.17" 48 | semver="1.0.27" 49 | serde_derive = "1.0.228" 50 | sha2 = "0.10.9" 51 | data-encoding = "2.9.0" 52 | deflate = "1.0.0" 53 | radix64 = "0.6.2" 54 | urlencoding = "2.1.3" 55 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_semver_or_date_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_semver_or_date_mod.rs 2 | 3 | //! If the major number is greater than 2000, it is a date-version else it is semver. 4 | 5 | use crate::cargo_auto_lib::error_mod::Result; 6 | use crate::cargo_auto_lib::public_api_mod::{RESET, YELLOW}; 7 | 8 | // this trait must be in scope to use these methods of CargoToml 9 | use crate::cargo_auto_lib::public_api_mod::CargoTomlPublicApiMethods; 10 | use crate::utils_mod::{pos, ResultLogError}; 11 | 12 | /// Increment the version in Cargo.toml. 13 | /// 14 | /// If the major version is greater than 2000, it is a date version 15 | /// else it is semver and increments the patch part. 16 | pub fn auto_version_increment_semver_or_date() -> Result<()> { 17 | println!(" {YELLOW}Running auto_semver_or_date{RESET}"); 18 | let cargo_toml = crate::cargo_auto_lib::auto_cargo_toml_mod::CargoToml::read().log(pos!())?; 19 | let version = cargo_toml.package_version(); 20 | let version = semver::Version::parse(&version).log(pos!())?; 21 | if version.major > 2000 { 22 | crate::cargo_auto_lib::auto_version_from_date_mod::auto_version_from_date().log(pos!())?; 23 | } else { 24 | crate::cargo_auto_lib::auto_semver_mod::auto_semver_increment_patch().log(pos!())?; 25 | } 26 | println!(" {YELLOW}Finished auto_semver_or_date{RESET}"); 27 | Ok(()) 28 | } 29 | 30 | /// Increment the version in Cargo.toml, forced. 31 | /// 32 | /// If the major version is greater than 2000, it is a date version 33 | /// else it is semver and increments the patch part. 34 | /// Forced is used in workspaces to force all members to have the same date version. 35 | pub fn auto_version_increment_semver_or_date_forced() -> Result<()> { 36 | println!(" {YELLOW}Running auto_semver_or_date{RESET}"); 37 | let cargo_toml = crate::cargo_auto_lib::auto_cargo_toml_mod::CargoToml::read().log(pos!())?; 38 | let version = cargo_toml.package_version(); 39 | let version = semver::Version::parse(&version).log(pos!())?; 40 | if version.major > 2000 { 41 | crate::cargo_auto_lib::auto_version_from_date_mod::auto_version_from_date_forced().log(pos!())?; 42 | } else { 43 | crate::cargo_auto_lib::auto_semver_mod::auto_semver_increment_patch().log(pos!())?; 44 | } 45 | println!(" {YELLOW}Finished auto_semver_or_date{RESET}"); 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/build_wasm_mod.rs: -------------------------------------------------------------------------------- 1 | // build_wasm_mod.rs 2 | 3 | //! Functions to build a WASM library. 4 | //! 5 | //! Don't change this code, so it can be updated regularly with 6 | //! cargo auto update_automation_tasks_rs 7 | //! If you want to customize it, copy the code into main.rs and modify it there. 8 | 9 | use crate::cargo_auto_lib as cl; 10 | 11 | use crate::cargo_auto_lib::CargoTomlPublicApiMethods; 12 | use crate::cargo_auto_lib::ShellCommandLimitedDoubleQuotesSanitizerTrait; 13 | use crate::utils_mod::pos; 14 | use crate::utils_mod::ResultLogError; 15 | #[allow(unused_imports)] 16 | use cl::{BLUE, GREEN, RED, RESET, YELLOW}; 17 | 18 | #[allow(dead_code)] 19 | /// wasm-pack build --profiling 20 | pub fn task_build() -> anyhow::Result { 21 | let cargo_toml = cl::CargoToml::read().log(pos!())?; 22 | cl::auto_version_increment_semver_or_date().log(pos!())?; 23 | cl::run_shell_command_static("cargo fmt").log(pos!())?; 24 | cl::run_shell_command_static("cargo clippy --no-deps").log(pos!())?; 25 | cl::run_shell_command_static("wasm-pack build --target web --profiling").log(pos!())?; 26 | 27 | cl::ShellCommandLimitedDoubleQuotesSanitizer::new(r#"rsync -a --delete-after pkg/ "web_server_folder/{package_name}/pkg/" "#) 28 | .log(pos!())? 29 | .arg("{package_name}", &cargo_toml.package_name()) 30 | .log(pos!())? 31 | .run() 32 | .log(pos!())?; 33 | 34 | Ok(cargo_toml) 35 | } 36 | 37 | #[allow(dead_code)] 38 | /// wasm-pack build --release 39 | pub fn task_release() -> anyhow::Result { 40 | let cargo_toml = cl::CargoToml::read().log(pos!())?; 41 | cl::auto_version_increment_semver_or_date().log(pos!())?; 42 | cl::auto_cargo_toml_to_md().log(pos!())?; 43 | cl::auto_lines_of_code("").log(pos!())?; 44 | 45 | cl::run_shell_command_static("cargo fmt").log(pos!())?; 46 | cl::run_shell_command_static("cargo clippy --no-deps").log(pos!())?; 47 | cl::run_shell_command_static("wasm-pack build --target web --release").log(pos!())?; 48 | 49 | cl::ShellCommandLimitedDoubleQuotesSanitizer::new(r#"rsync -a --delete-after pkg/ "web_server_folder/{package_name}/pkg/" "#) 50 | .log(pos!())? 51 | .arg("{package_name}", &cargo_toml.package_name()) 52 | .log(pos!())? 53 | .run() 54 | .log(pos!())?; 55 | 56 | Ok(cargo_toml) 57 | } 58 | -------------------------------------------------------------------------------- /src/bin/cargo-auto/generic_functions_mod.rs: -------------------------------------------------------------------------------- 1 | // generic_functions.rs 2 | 3 | //! Functions to work with CLI binary executable projects. 4 | //! 5 | //! Binary executables need some standard functions to help to develop them efficiently. 6 | 7 | // region: Public API constants 8 | // ANSI colors for Linux terminal 9 | // https://github.com/shiena/ansicolor/blob/master/README.md 10 | /// ANSI color 11 | #[allow(dead_code)] 12 | pub const RED: &str = "\x1b[31m"; 13 | /// ANSI color 14 | #[allow(dead_code)] 15 | pub const GREEN: &str = "\x1b[32m"; 16 | /// ANSI color 17 | #[allow(dead_code)] 18 | pub const YELLOW: &str = "\x1b[33m"; 19 | /// ANSI color 20 | #[allow(dead_code)] 21 | pub const BLUE: &str = "\x1b[34m"; 22 | /// ANSI color 23 | #[allow(dead_code)] 24 | pub const RESET: &str = "\x1b[0m"; 25 | // endregion: Public API constants 26 | 27 | /// Initialize tracing to file logs/cargo_auto.log. \ 28 | /// 29 | /// The folder logs/ is in .gitignore and will not be committed. 30 | pub fn tracing_init() -> anyhow::Result<()> { 31 | let offset = time::UtcOffset::current_local_offset()?; 32 | let timer = tracing_subscriber::fmt::time::OffsetTime::new( 33 | offset, 34 | time::macros::format_description!("[hour]:[minute]:[second].[subsecond digits:6]"), 35 | ); 36 | 37 | // A filter consists of one or more comma-separated directives 38 | // target[span{field=value}]=level 39 | // Levels order: 1. ERROR, 2. WARN, 3. INFO, 4. DEBUG, 5. TRACE 40 | // ERROR level is always logged. 41 | // Add filters to CARGO_AUTO_LOG environment variable for a single execution: 42 | // ```bash 43 | // CARGO_AUTO_LOG="debug,hyper_util=info,reqwest=info" ./{package_name} 44 | // ``` 45 | let filter = tracing_subscriber::EnvFilter::from_env("CARGO_AUTO_LOG"); 46 | 47 | let builder = tracing_subscriber::fmt() 48 | .with_file(true) 49 | .with_timer(timer) 50 | .with_line_number(true) 51 | .with_ansi(false) 52 | .with_env_filter(filter); 53 | if std::env::var("CARGO_AUTO_LOG").is_ok() { 54 | // if CARGO_AUTO_LOG exists than enable tracing to file 55 | let file_appender = tracing_appender::rolling::RollingFileAppender::builder() 56 | .rotation(tracing_appender::rolling::Rotation::DAILY) 57 | .filename_prefix("cargo_auto") 58 | .filename_suffix("log") 59 | .build("logs") 60 | .expect("initializing rolling file appender failed"); 61 | builder.with_writer(file_appender).init(); 62 | } else { 63 | builder.init(); 64 | }; 65 | 66 | Ok(()) 67 | } 68 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "titleBar.activeForeground": "#fff", 4 | "titleBar.inactiveForeground": "#ffffffcc", 5 | "titleBar.activeBackground": "#477587", 6 | "titleBar.inactiveBackground": "#3F758DCC" 7 | }, 8 | "spellright.language": [ 9 | "en" 10 | ], 11 | "spellright.documentTypes": [ 12 | "markdown", 13 | "latex", 14 | "plaintext" 15 | ], 16 | "files.associations": { 17 | "LICENSE": "plain text" 18 | }, 19 | "rust-analyzer.showUnlinkedFileNotification": false, 20 | "terminal.integrated.commandsToSkipShell": [ 21 | // Ensure the toggle sidebar visibility ctrl+b keybinding skips the terminal shell 22 | "workbench.action.toggleSidebarVisibility", 23 | ], 24 | "cSpell.words": [ 25 | "Alla", 26 | "alloc", 27 | "apos", 28 | "appender", 29 | "auto", 30 | "bestia", 31 | "bestiadev", 32 | "bindgen", 33 | "buildtool", 34 | "camino", 35 | "cdylib", 36 | "chrono", 37 | "clippy", 38 | "cmake", 39 | "commitish", 40 | "crev", 41 | "crossplatform", 42 | "CRUSTDE", 43 | "debuginfo", 44 | "deps", 45 | "developmenttool", 46 | "ende", 47 | "endregion", 48 | "filedate", 49 | "filehash", 50 | "filetime", 51 | "flate", 52 | "HEXLOWER", 53 | "imageops", 54 | "keygen", 55 | "Lanczos", 56 | "lizs", 57 | "microxml", 58 | "Nazdravlje", 59 | "new_cli", 60 | "noopener", 61 | "noscript", 62 | "octocrab", 63 | "onchange", 64 | "onclick", 65 | "passphrases", 66 | "paypal", 67 | "pgrsql", 68 | "plantuml", 69 | "println", 70 | "Prost", 71 | "reqwest", 72 | "RUSTC", 73 | "rustfmt", 74 | "rustlang", 75 | "rustprojects", 76 | "sccache", 77 | "serde", 78 | "spellright", 79 | "sscanf", 80 | "sshadd", 81 | "struct", 82 | "stylesheet", 83 | "subcommand", 84 | "subsecond", 85 | "substack", 86 | "Swatinem", 87 | "tempdir", 88 | "tempfile", 89 | "termion", 90 | "thiserror", 91 | "unoptimized", 92 | "urlencoding", 93 | "walkdir", 94 | "walkir", 95 | "webassembly", 96 | "wooow", 97 | "xtask", 98 | "zcvf", 99 | "zdravje", 100 | "zeroize" 101 | ] 102 | } -------------------------------------------------------------------------------- /docs/settings.html: -------------------------------------------------------------------------------- 1 | Settings

Rustdoc settings

Back
-------------------------------------------------------------------------------- /docs/help.html: -------------------------------------------------------------------------------- 1 | Help

Rustdoc help

Back
-------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo-auto" 3 | # binary executable does not need to be SemVer, because nobody depends on it 4 | version = "2025.1108.929" 5 | authors = ["bestia.dev"] 6 | homepage = "https://bestia.dev" 7 | edition = "2021" 8 | description = "Automation tasks coded in Rust language for the workflow of Rust projects" 9 | repository = "https://github.com/automation-tasks-rs/cargo-auto" 10 | readme = "README.md" 11 | license = "MIT" 12 | # Keyword must be only one word: lowercase letters, hyphens(-) or numbers, less then 35 characters, at most 5 keywords per crate 13 | keywords = ["maintained", "ready-for-use", "rustlang","automation","workflow"] 14 | # Allowed categories are listed here 15 | categories = ["command-line-interface","development-tools::build-utils","development-tools::cargo-plugins"] 16 | # Publish to crates.io. Only this files. 17 | publish = true 18 | include = [ 19 | "Cargo.toml", 20 | "LICENSE", 21 | "README.md", 22 | "src/*" 23 | ] 24 | 25 | # SemVer rules: Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable. 26 | # WARNING: Although dependencies in Cargo.toml look like a specific version of the crate, they are just like a caret version (=x.y.z) and actually specify 27 | # the minimum version and allow automatic SemVer compatible updates up to a MAJOR increment! Always check the true version with `cargo tree`! 28 | [dependencies] 29 | lazy_static="1.5.0" 30 | base64ct = {version = "1.8.0", features = ["alloc"] } 31 | json5 = "0.4.1" 32 | image = "0.25.8" 33 | ico = "0.4.0" 34 | sha2 = "0.10.9" 35 | data-encoding = "2.9.0" 36 | thiserror="2.0.17" 37 | anyhow="1.0.100" 38 | crossplatform_path = "4.0.1" 39 | # crossplatform_path = { path = "../crossplatform_path" } 40 | serde = { version = "1.0.228", features = ["derive"] } 41 | serde_derive = "1.0.228" 42 | serde_json = "1.0.145" 43 | reqwest = { version = "0.12.24", features = ["blocking"] } 44 | 45 | flate2 = "1.1.5" 46 | tar = "0.4.44" 47 | walkdir = "2.5.0" 48 | 49 | tracing = {version="0.1.41", features=["attributes" ]} 50 | tracing-subscriber = { version = "0.3.20", features = ["env-filter", "std", "fmt", "time","ansi"] } 51 | tracing-appender="0.2.3" 52 | time = {version="0.3.44", features=["macros","local-offset"]} 53 | chrono ={version="0.4.42", default-features=false, features=["now"]} 54 | 55 | # The library enables the code to be used by other binary executables. 56 | [lib] 57 | name = "cargo_auto_main_lib" 58 | path = "src/lib.rs" 59 | # A flag for enabling documentation of this target. This is used by `cargo doc`. 60 | doc = false 61 | 62 | # The only and main binary executable. 63 | [[bin]] 64 | name = "cargo-auto" 65 | path = "src/bin/cargo-auto/main.rs" 66 | # A flag for enabling documentation of this target. This is used by `cargo doc`. 67 | doc = true 68 | -------------------------------------------------------------------------------- /docs/static.files/scrape-examples-5e967b76.js: -------------------------------------------------------------------------------- 1 | "use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelectorAll("[data-nosnippet]");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines[line].offsetTop;}else{const halfHeight=elt.offsetHeight/2;const offsetTop=lines[loc[0]].offsetTop;const lastLine=lines[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight;}lines[0].parentElement.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset);}function createScrapeButton(parent,className,content){const button=document.createElement("button");button.className=className;button.title=content;parent.insertBefore(button,parent.firstChild);return button;}window.updateScrapedExample=(example,buttonHolder)=>{let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");let expandButton=null;if(!example.classList.contains("expanded")){expandButton=createScrapeButton(buttonHolder,"expand","Show all");}const isHidden=example.parentElement.classList.contains("more-scraped-examples");const locs=example.locs;if(locs.length>1){const next=createScrapeButton(buttonHolder,"next","Next usage");const prev=createScrapeButton(buttonHolder,"prev","Previous usage");const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title;};prev.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length;});});next.addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length;});});}if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");removeClass(expandButton,"collapse");expandButton.title="Show all";scrollToLoc(example,locs[0][0],isHidden);}else{addClass(example,"expanded");addClass(expandButton,"collapse");expandButton.title="Show single example";}});}};function setupLoc(example,isHidden){example.locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);scrollToLoc(example,example.locs[0][0],isHidden);}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>setupLoc(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false;});});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>setupLoc(el,true));});},{once:true});});})(); -------------------------------------------------------------------------------- /docs/static.files/COPYRIGHT-7fb11f4e.txt: -------------------------------------------------------------------------------- 1 | # REUSE-IgnoreStart 2 | 3 | These documentation pages include resources by third parties. This copyright 4 | file applies only to those resources. The following third party resources are 5 | included, and carry their own copyright notices and license terms: 6 | 7 | * Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): 8 | 9 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 10 | with Reserved Font Name Fira Sans. 11 | 12 | Copyright (c) 2014, Telefonica S.A. 13 | 14 | Licensed under the SIL Open Font License, Version 1.1. 15 | See FiraSans-LICENSE.txt. 16 | 17 | * rustdoc.css, main.js, and playpen.js: 18 | 19 | Copyright 2015 The Rust Developers. 20 | Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or 21 | the MIT license (LICENSE-MIT.txt) at your option. 22 | 23 | * normalize.css: 24 | 25 | Copyright (c) Nicolas Gallagher and Jonathan Neal. 26 | Licensed under the MIT license (see LICENSE-MIT.txt). 27 | 28 | * Source Code Pro (SourceCodePro-Regular.ttf.woff2, 29 | SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): 30 | 31 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), 32 | with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark 33 | of Adobe Systems Incorporated in the United States and/or other countries. 34 | 35 | Licensed under the SIL Open Font License, Version 1.1. 36 | See SourceCodePro-LICENSE.txt. 37 | 38 | * Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, 39 | SourceSerif4-It.ttf.woff2, SourceSerif4-Semibold.ttf.woff2): 40 | 41 | Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 42 | 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United 43 | States and/or other countries. 44 | 45 | Licensed under the SIL Open Font License, Version 1.1. 46 | See SourceSerif4-LICENSE.md. 47 | 48 | * Nanum Barun Gothic Font (NanumBarunGothic.woff2) 49 | 50 | Copyright 2010, NAVER Corporation (http://www.nhncorp.com) 51 | with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, 52 | NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, 53 | Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, 54 | Naver NanumMyeongjoEco, NanumMyeongjoEco, Naver NanumGothicLight, 55 | NanumGothicLight, NanumBarunGothic, Naver NanumBarunGothic. 56 | 57 | https://hangeul.naver.com/2017/nanum 58 | https://github.com/hiun/NanumBarunGothic 59 | 60 | Licensed under the SIL Open Font License, Version 1.1. 61 | See NanumBarunGothic-LICENSE.txt. 62 | 63 | * Rust logos (rust-logo.svg, favicon.svg, favicon-32x32.png) 64 | 65 | Copyright 2025 Rust Foundation. 66 | Licensed under the Creative Commons Attribution license (CC-BY). 67 | https://rustfoundation.org/policy/rust-trademark-policy/ 68 | 69 | This copyright file is intended to be distributed with rustdoc output. 70 | 71 | # REUSE-IgnoreEnd 72 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/utils_mod.rs: -------------------------------------------------------------------------------- 1 | // utils_mod.rs 2 | 3 | //! Helper functions that does not usually change. 4 | //! 5 | //! Don't change this code, so it can be updated regularly with 6 | //! cargo auto update_automation_tasks_rs 7 | //! If you want to customize it, copy the code into main.rs and modify it there. 8 | 9 | use crate::cargo_auto_lib as cl; 10 | 11 | #[allow(unused_imports)] 12 | pub use cl::{BLUE, GREEN, RED, RESET, YELLOW}; 13 | 14 | /// Initialize tracing to file logs/automation_tasks_rs.log. \ 15 | /// 16 | /// The folder logs/ is in .gitignore and will not be committed. 17 | pub fn tracing_init() -> anyhow::Result<()> { 18 | let offset = time::UtcOffset::current_local_offset()?; 19 | let timer = tracing_subscriber::fmt::time::OffsetTime::new( 20 | offset, 21 | time::macros::format_description!("[hour]:[minute]:[second].[subsecond digits:6]"), 22 | ); 23 | 24 | // A filter consists of one or more comma-separated directives 25 | // target[span{field=value}]=level 26 | // Levels order: 1. ERROR, 2. WARN, 3. INFO, 4. DEBUG, 5. TRACE 27 | // ERROR level is always logged. 28 | // Add filters to AUTOMATION_TASKS_RS_LOG environment variable for a single execution: 29 | // ```bash 30 | // AUTOMATION_TASKS_RS_LOG="debug,hyper_util=info,reqwest=info" ./{package_name} 31 | // ``` 32 | let filter = tracing_subscriber::EnvFilter::from_env("AUTOMATION_TASKS_RS_LOG"); 33 | 34 | let builder = tracing_subscriber::fmt() 35 | .with_timer(timer) 36 | .with_ansi(false) 37 | .with_target(false) 38 | .with_env_filter(filter); 39 | if std::env::var("AUTOMATION_TASKS_RS_LOG").is_ok() { 40 | // if AUTOMATION_TASKS_RS_LOG exists than enable tracing to file 41 | let file_appender = tracing_appender::rolling::RollingFileAppender::builder() 42 | .rotation(tracing_appender::rolling::Rotation::DAILY) 43 | .filename_prefix("automation_tasks_rs") 44 | .filename_suffix("log") 45 | .build("logs") 46 | .expect("initializing rolling file appender failed"); 47 | builder.with_writer(file_appender).init(); 48 | } else { 49 | builder.init(); 50 | }; 51 | 52 | Ok(()) 53 | } 54 | 55 | /// macro to get source code position to log errors before propagation 56 | /// 57 | /// example: read_to_string("x").log(pos!())?; 58 | macro_rules! pos { 59 | // `()` indicates that the macro takes no argument. 60 | () => { 61 | // The macro will expand into the contents of this block. 62 | &format!("{}:{}:{}:", file!(), line!(), column!()) 63 | }; 64 | } 65 | pub(crate) use pos; 66 | 67 | /// Trait to log the error from Result before propagation with ?. 68 | pub trait ResultLogError: Sized { 69 | fn log(self, file_line_column: &str) -> Self; 70 | } 71 | 72 | /// Implements LogError for anyhow::Result. 73 | impl ResultLogError for core::result::Result { 74 | fn log(self, file_line_column: &str) -> Self { 75 | self.inspect_err(|err| tracing::debug!("automation_tasks_rs/{} {:?}", file_line_column, err)) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_helper_functions_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_helper_functions_mod 2 | 3 | //! Various helper functions. 4 | 5 | use crate::cargo_auto_lib::error_mod::{Error, Result}; 6 | use crate::cargo_auto_lib::public_api_mod::{RED, RESET}; 7 | use crate::utils_mod::{pos, ResultLogError}; 8 | 9 | // region: auto_md_to_doc_comments include doc_comments/exit_if_not_run_in_rust_project_root_directory.md A /// 10 | /// Check if the code was run inside the Rust project root directory. 11 | /// 12 | /// There must be the `Cargo.toml` file and the directory `automation_tasks_rs` 13 | /// If not, exit with error message. 14 | /// 15 | // endregion: auto_md_to_doc_comments include doc_comments/exit_if_not_run_in_rust_project_root_directory.md A /// 16 | pub fn exit_if_not_run_in_rust_project_root_directory() { 17 | if !(camino::Utf8Path::new("automation_tasks_rs").exists() && (camino::Utf8Path::new("Cargo.toml").exists())) { 18 | eprintln!("{RED}Error: `automation_tasks_rs` must be run inside the Rust project in the dir that contains"); 19 | println!("`Cargo.toml` file and `automation_tasks_rs` directory. Exiting...{RESET}"); 20 | // early exit 21 | std::process::exit(1); 22 | } 23 | } 24 | 25 | /// Print one or more sub_commands. 26 | pub fn completion_return_one_or_more_sub_commands(sub_commands: Vec<&str>, word_being_completed: &str) { 27 | let mut sub_found = false; 28 | for sub_command in sub_commands.iter() { 29 | if sub_command.starts_with(word_being_completed) { 30 | println!("{sub_command}"); 31 | sub_found = true; 32 | } 33 | } 34 | if !sub_found { 35 | // print all sub-commands 36 | for sub_command in sub_commands.iter() { 37 | println!("{sub_command}"); 38 | } 39 | } 40 | } 41 | 42 | /// Get home dir using the home crate. 43 | /// 44 | /// Error if HOME not found. 45 | pub fn home_dir() -> Result { 46 | match home::home_dir() { 47 | Some(path_buff) => { 48 | if !path_buff.as_os_str().is_empty() { 49 | Ok(path_buff) 50 | } else { 51 | Err(Error::ErrorFromStr("{RED}Unable to get your home dir!{RESET}")) 52 | } 53 | } 54 | None => Err(Error::ErrorFromStr("{RED}Unable to get your home dir!{RESET}")), 55 | } 56 | } 57 | 58 | /// Replace tilde with home::home_dir, only for utf8. 59 | pub fn tilde_expand_to_home_dir_utf8(path_str: &str) -> Result { 60 | let mut expanded = String::new(); 61 | if path_str.starts_with("~") { 62 | let base = home::home_dir() 63 | .ok_or_else(|| Error::ErrorFromStr("Cannot find home_dir in this OS.")) 64 | .log(pos!())?; 65 | // only utf8 is accepted 66 | let base = base.to_string_lossy(); 67 | expanded.push_str(&base); 68 | expanded.push_str(path_str.trim_start_matches("~")); 69 | use std::str::FromStr; 70 | Ok(camino::Utf8PathBuf::from_str(&expanded).log(pos!())?) 71 | } else { 72 | use std::str::FromStr; 73 | Ok(camino::Utf8PathBuf::from_str(path_str).log(pos!())?) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /docs/cargo_auto/fn.main.html: -------------------------------------------------------------------------------- 1 | main in cargo_auto - Rust

Function main

Source
pub(crate) fn main() -> ExitCode
Expand description

Function main() returns ExitCode.

2 |
-------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_delete_old_js_snippets_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_delete_old_js_snippets_mod 2 | 3 | //! Deletes old js snippets when working with wasm-pack. 4 | 5 | //region: use statements 6 | use crate::cargo_auto_lib::error_mod::{Error, Result}; 7 | use crate::cargo_auto_lib::public_api_mod::{RED, RESET, YELLOW}; 8 | use crate::utils_mod::{pos, ResultLogError}; 9 | use filetime::FileTime; 10 | 11 | //endregion 12 | 13 | /// Deletes old js snippets when working with wasm-pack. 14 | /// 15 | /// The old folders for `js snippets` are not automatically deleted on building with `wasm-pack`. 16 | /// This utils do that. 17 | /// The function must be executed in the root project folder where is the Cargo.toml. 18 | pub fn auto_delete_old_js_snippets() -> Result<()> { 19 | let current_dir = std::env::current_dir().log(pos!())?; 20 | let snippets_dir = current_dir.join("pkg").join("snippets"); 21 | //the first folder can be None 22 | let mut opt_first_folder: Option = None; 23 | let mut opt_first_mtime: Option = None; 24 | 25 | //find the newer folder and remove the older folder 26 | //but not with dodrio_xxx name 27 | for entry in std::fs::read_dir(snippets_dir).log(pos!())? { 28 | let entry = entry?; 29 | let second_folder = entry.path(); 30 | let second_name = entry 31 | .file_name() 32 | .into_string() 33 | .map_err(|_| Error::ErrorFromStr("file_name into_string error")) 34 | .log(pos!())? 35 | .to_lowercase(); 36 | if !second_name.starts_with("dodrio") { 37 | //println!("{:?}",second_folder); 38 | let second_metadata = std::fs::metadata(&second_folder).log(pos!())?; 39 | let second_mtime = FileTime::from_last_modification_time(&second_metadata); 40 | //println!("{:?}",second_mtime); 41 | 42 | match opt_first_mtime { 43 | None => { 44 | opt_first_folder = Some(second_folder.clone()); 45 | opt_first_mtime = Some(second_mtime); 46 | } 47 | Some(first_mtime) => match second_mtime.cmp(&first_mtime) { 48 | // if second_mtime > first_mtime { 49 | std::cmp::Ordering::Greater => { 50 | let first_folder = opt_first_folder 51 | .ok_or_else(|| Error::ErrorFromStr("opt_first_folder is None")) 52 | .log(pos!())?; 53 | println!(" {YELLOW}delete first: {:?}{RESET}", first_folder); 54 | std::fs::remove_dir_all(first_folder).log(pos!())?; 55 | 56 | opt_first_folder = Some(second_folder.clone()); 57 | opt_first_mtime = Some(second_mtime); 58 | } 59 | // } else if first_mtime > second_mtime { 60 | std::cmp::Ordering::Less => { 61 | println!(" {YELLOW}delete second: {:?}{RESET}", second_folder); 62 | std::fs::remove_dir_all(second_folder).log(pos!())?; 63 | } 64 | // else 65 | std::cmp::Ordering::Equal => { 66 | eprintln!("{RED}Error: folders have the same date?{RESET}"); 67 | } 68 | }, 69 | } 70 | } 71 | } 72 | Ok(()) 73 | } 74 | -------------------------------------------------------------------------------- /docs/static.files/rust-logo-9a9549ea.svg: -------------------------------------------------------------------------------- 1 | 2 | 61 | 62 | -------------------------------------------------------------------------------- /docs/cargo_auto/fn.main_returns_anyhow_result.html: -------------------------------------------------------------------------------- 1 | main_returns_anyhow_result in cargo_auto - Rust

Function main_returns_anyhow_result

Source
pub(crate) fn main_returns_anyhow_result() -> Result<()>
Expand description

Function main() returns anyhow::Result.

2 |
-------------------------------------------------------------------------------- /docs/cargo_auto/generic_functions_mod/constant.RED.html: -------------------------------------------------------------------------------- 1 | RED in cargo_auto::generic_functions_mod - Rust

Constant RED

Source
pub const RED: &str = "\x1b[31m";
Expand description

ANSI color

2 |
-------------------------------------------------------------------------------- /docs/cargo_auto/generic_functions_mod/constant.BLUE.html: -------------------------------------------------------------------------------- 1 | BLUE in cargo_auto::generic_functions_mod - Rust

Constant BLUE

Source
pub const BLUE: &str = "\x1b[34m";
Expand description

ANSI color

2 |
-------------------------------------------------------------------------------- /docs/cargo_auto/generic_functions_mod/constant.RESET.html: -------------------------------------------------------------------------------- 1 | RESET in cargo_auto::generic_functions_mod - Rust

Constant RESET

Source
pub const RESET: &str = "\x1b[0m";
Expand description

ANSI color

2 |
-------------------------------------------------------------------------------- /docs/cargo_auto/generic_functions_mod/constant.GREEN.html: -------------------------------------------------------------------------------- 1 | GREEN in cargo_auto::generic_functions_mod - Rust

Constant GREEN

Source
pub const GREEN: &str = "\x1b[32m";
Expand description

ANSI color

2 |
-------------------------------------------------------------------------------- /docs/cargo_auto/generic_functions_mod/constant.YELLOW.html: -------------------------------------------------------------------------------- 1 | YELLOW in cargo_auto::generic_functions_mod - Rust

Constant YELLOW

Source
pub const YELLOW: &str = "\x1b[33m";
Expand description

ANSI color

2 |
-------------------------------------------------------------------------------- /docs/cargo_auto/all.html: -------------------------------------------------------------------------------- 1 | List of all items in this crate
-------------------------------------------------------------------------------- /docs/static.files/src-script-813739b1.js: -------------------------------------------------------------------------------- 1 | "use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth{removeClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","false");};window.rustdocShowSourceSidebar=()=>{addClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","true");};window.rustdocToggleSrcSidebar=()=>{if(document.documentElement.classList.contains("src-sidebar-expanded")){window.rustdocCloseSourceSidebar();}else{window.rustdocShowSourceSidebar();}};function createSrcSidebar(srcIndexStr){const container=nonnull(document.querySelector("nav.sidebar"));const sidebar=document.createElement("div");sidebar.id="src-sidebar";const srcIndex=new Map(JSON.parse(srcIndexStr));let hasFoundFile=false;for(const[key,source]of srcIndex){source[NAME_OFFSET]=key;hasFoundFile=createDirEntry(source,sidebar,"",hasFoundFile);}container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus();}}function highlightSrcLines(){const match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);if(!match){return;}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10);}if(to{removeClass(e,"line-highlighted");});for(let i=from;i<=to;++i){elem=document.getElementById(""+i);if(!elem){break;}addClass(elem,"line-highlighted");}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,"","#"+name);highlightSrcLines();}else{location.replace("#"+name);}window.scrollTo(x,y);};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return;}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp;}set_fragment(prev_line_id+"-"+cur_line_id);}else{prev_line_id=cur_line_id;set_fragment(""+cur_line_id);}};}());window.addEventListener("hashchange",highlightSrcLines);onEachLazy(document.querySelectorAll("a[data-nosnippet]"),el=>{el.addEventListener("click",handleSrcHighlight);});highlightSrcLines();window.createSrcSidebar=createSrcSidebar;})(); -------------------------------------------------------------------------------- /docs/cargo_auto/generic_functions_mod/fn.tracing_init.html: -------------------------------------------------------------------------------- 1 | tracing_init in cargo_auto::generic_functions_mod - Rust

Function tracing_init

Source
pub fn tracing_init() -> Result<()>
Expand description

Initialize tracing to file logs/cargo_auto.log. \

2 |

The folder logs/ is in .gitignore and will not be committed.

3 |
-------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_check_micro_xml_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_check_micro_xml_mod.rs 2 | 3 | //! Checks the correctness of micro XML files. 4 | 5 | use crate::cargo_auto_lib::error_mod::{Error, Result}; 6 | use crate::cargo_auto_lib::public_api_mod::{RED, RESET, YELLOW}; 7 | use crate::utils_mod::{pos, ResultLogError}; 8 | use glob::glob; 9 | use reader_for_microxml::{ReaderForMicroXml, Token}; 10 | 11 | /// I want html pages to be correct microXML when I use them for single page application. 12 | /// Before build or release this function will check for correctness. 13 | pub fn auto_check_micro_xml(path_to_html_pages: &str) -> Result<()> { 14 | println!(" {YELLOW}Running auto_check_micro_xml {path_to_html_pages}{RESET}"); 15 | let glob_str = format!("{}/*.html", path_to_html_pages.trim_end_matches('/')); 16 | // find html pages for single page application 17 | for filename_result in glob(&glob_str).log(pos!())? { 18 | let filename_pathbuff = filename_result?; 19 | let filename_pathbuff = camino::Utf8Path::from_path(&filename_pathbuff) 20 | .ok_or_else(|| Error::ErrorFromStr("filename is None")) 21 | .log(pos!())?; 22 | 23 | let file_name = filename_pathbuff 24 | .file_name() 25 | .ok_or_else(|| Error::ErrorFromStr("filename is None")) 26 | .log(pos!())?; 27 | let str_xml = std::fs::read_to_string(filename_pathbuff).log(pos!())?; 28 | 29 | // check if file have CRLF instead of LF and show error 30 | if str_xml.contains("\r\n") { 31 | return Err(Error::ErrorFromString(format!( 32 | "{RED}Error: {filename_pathbuff} has CRLF line endings instead of LF. Correct the file! {RESET}" 33 | ))); 34 | } 35 | 36 | // check microxml correctness. Error on errors. 37 | check_micro_xml(&str_xml, file_name).log(pos!())?; 38 | } 39 | println!(" {YELLOW}Finished auto_check_micro_xml{RESET}"); 40 | Ok(()) 41 | } 42 | 43 | /// Errors if the microXML string is not correct 44 | fn check_micro_xml(str_xml: &str, file_name: &str) -> Result<()> { 45 | println!(" {YELLOW}Check MicroXml: {file_name}{RESET}"); 46 | // remove because it is not microXML 47 | let str_xml = str_xml.replace("", ""); 48 | let reader_iterator = ReaderForMicroXml::new(&str_xml); 49 | // reader_iterator is iterator Option> 50 | // the first option is used for the iterator to know where is the end 51 | // then the Result can have an Token or an Error 52 | let mut vec_elem: Vec<&str> = vec![]; 53 | for result_token in reader_iterator { 54 | match result_token { 55 | Ok(token) => match token { 56 | Token::StartElement(name) => vec_elem.push(name), 57 | Token::Attribute(_name, _value) => continue, 58 | Token::TextNode(_txt) => continue, 59 | Token::Comment(_txt) => continue, 60 | Token::EndElement(end_element_name) => { 61 | let start_element_name = vec_elem 62 | .pop() 63 | .ok_or_else(|| Error::ErrorFromStr("vec_elem.pop() is None")) 64 | .log(pos!())?; 65 | if !end_element_name.is_empty() && end_element_name != start_element_name { 66 | return Err(Error::ErrorFromString(format!( 67 | "{RED}MicroXml {} start {} does not match end {}{RESET}", 68 | file_name, start_element_name, end_element_name, 69 | ))); 70 | } 71 | } 72 | }, 73 | Err(err_msg) => { 74 | return Err(Error::ErrorFromString(format!( 75 | "{RED}MicroXml {} incorrect : {}{RESET}", 76 | file_name, err_msg, 77 | ))); 78 | } 79 | } 80 | } 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /docs/static.files/favicon-044be391.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/utils_mod.rs: -------------------------------------------------------------------------------- 1 | // utils_mod.rs 2 | 3 | //! Functions for various utilities. 4 | 5 | use crate::{ 6 | cargo_auto_lib::error_mod::{Error, Result}, 7 | utils_mod::{pos, ResultLogError}, 8 | }; 9 | 10 | // region: delimiters cannot be INACTIVE like markers 11 | 12 | /// Position of start of the delimited data after the delimiter 13 | pub fn find_pos_start_data_after_delimiter(md_text_content: &str, pos: usize, delimiter: &str) -> Result { 14 | if let Ok(pos_start_data) = find_from(md_text_content, pos, delimiter) { 15 | let pos_start_data = pos_start_data + delimiter.len(); 16 | return Ok(pos_start_data); 17 | } 18 | // return 19 | Err(Error::ErrorFromStr("not found")) 20 | } 21 | 22 | /// Position of end of the delimited data before the delimiter 23 | pub fn find_pos_end_data_before_delimiter(md_text_content: &str, pos: usize, delimiter: &str) -> Result { 24 | if let Ok(pos_end_data) = find_from(md_text_content, pos, delimiter) { 25 | return Ok(pos_end_data); 26 | } 27 | //return 28 | Err(Error::ErrorFromStr("not found")) 29 | } 30 | 31 | // endregion: delimiters cannot be INACTIVE like markers 32 | 33 | /// Find from pos 34 | pub fn find_from(text: &str, from_pos: usize, find: &str) -> Result { 35 | let slice01 = text 36 | .get(from_pos..) 37 | .ok_or_else(|| Error::ErrorFromStr("text get is None")) 38 | .log(pos!())?; 39 | let option_location = slice01.find(find); 40 | if let Some(location) = option_location { 41 | // return Ok with usize 42 | Ok(from_pos + location) 43 | } else { 44 | // return Err 45 | Err(Error::ErrorFromStr("location is not find")) 46 | } 47 | } 48 | 49 | // region: auto_md_to_doc_comments include doc_comments/traverse_dir_with_exclude_dir.md A /// 50 | /// Traverse dir and its sub-dir, but avoid excluded dirs. 51 | /// 52 | /// The find_file and the exclude dir strings must start with /. 53 | /// 54 | /// ## Example 55 | /// 56 | /// ```Rust ignore 57 | /// 58 | /// let files = cargo_auto_lib::traverse_dir_with_exclude_dir( 59 | /// Path::new("/home/project/src"), 60 | /// "/*.rs", 61 | /// // avoid big folders 62 | /// &vec![ 63 | /// "/.git".to_string(), 64 | /// "/target".to_string(), 65 | /// "/docs".to_string() 66 | /// ] 67 | /// ).expect("error"); 68 | /// for rs_file_name in files.iter() { 69 | /// println!("{}", &rs_file_name); 70 | /// } 71 | /// ``` 72 | /// 73 | // endregion: auto_md_to_doc_comments include doc_comments/traverse_dir_with_exclude_dir.md A /// 74 | pub fn traverse_dir_with_exclude_dir(dir: &std::path::Path, find_file: &str, exclude_dirs: &[String]) -> Result> { 75 | // if the parameter is /*.rs, I can eliminate /* 76 | let find_file = &find_file.replace("/*", ""); 77 | 78 | let mut v = Vec::new(); 79 | if dir.is_dir() { 80 | for entry in std::fs::read_dir(dir).log(pos!())? { 81 | let entry = entry?; 82 | let path = entry.path(); 83 | let str_path = path.to_str().ok_or_else(|| Error::ErrorFromStr("path is None")).log(pos!())?; 84 | if path.is_dir() { 85 | let mut is_excluded = false; 86 | for excl in exclude_dirs { 87 | if str_path.ends_with(excl) { 88 | is_excluded = true; 89 | break; 90 | } 91 | } 92 | if !is_excluded { 93 | let mut sub_v = traverse_dir_with_exclude_dir(&path, find_file, exclude_dirs).log(pos!())?; 94 | v.append(&mut sub_v); 95 | } 96 | } else if str_path.ends_with(find_file) { 97 | v.push(str_path.to_string()); 98 | } 99 | } 100 | } 101 | Ok(v) 102 | } 103 | 104 | /// The original `concat()` function does not have a delimiter. 105 | pub fn concatenate_vec_to_string(vec: &[String], delimiter: &str) -> String { 106 | let size = vec.iter().fold(0, |a, b| a + b.len()); 107 | let mut concatenated_string = String::with_capacity(size); 108 | for (i, item) in vec.iter().enumerate() { 109 | if i > 0 { 110 | concatenated_string.push_str(delimiter); 111 | } 112 | concatenated_string.push_str(item); 113 | } 114 | // return 115 | concatenated_string 116 | } 117 | 118 | /// UTC date in iso standard like 2024-12-31. 119 | pub fn now_utc_date_iso() -> String { 120 | chrono::Utc::now().format("%Y-%m-%d").to_string() 121 | } 122 | -------------------------------------------------------------------------------- /docs/static.files/FiraSans-LICENSE-05ab6dbd.txt: -------------------------------------------------------------------------------- 1 | // REUSE-IgnoreStart 2 | 3 | Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. 4 | with Reserved Font Name < Fira >, 5 | 6 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 7 | This license is copied below, and is also available with a FAQ at: 8 | http://scripts.sil.org/OFL 9 | 10 | 11 | ----------------------------------------------------------- 12 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 13 | ----------------------------------------------------------- 14 | 15 | PREAMBLE 16 | The goals of the Open Font License (OFL) are to stimulate worldwide 17 | development of collaborative font projects, to support the font creation 18 | efforts of academic and linguistic communities, and to provide a free and 19 | open framework in which fonts may be shared and improved in partnership 20 | with others. 21 | 22 | The OFL allows the licensed fonts to be used, studied, modified and 23 | redistributed freely as long as they are not sold by themselves. The 24 | fonts, including any derivative works, can be bundled, embedded, 25 | redistributed and/or sold with any software provided that any reserved 26 | names are not used by derivative works. The fonts and derivatives, 27 | however, cannot be released under any other type of license. The 28 | requirement for fonts to remain under this license does not apply 29 | to any document created using the fonts or their derivatives. 30 | 31 | DEFINITIONS 32 | "Font Software" refers to the set of files released by the Copyright 33 | Holder(s) under this license and clearly marked as such. This may 34 | include source files, build scripts and documentation. 35 | 36 | "Reserved Font Name" refers to any names specified as such after the 37 | copyright statement(s). 38 | 39 | "Original Version" refers to the collection of Font Software components as 40 | distributed by the Copyright Holder(s). 41 | 42 | "Modified Version" refers to any derivative made by adding to, deleting, 43 | or substituting -- in part or in whole -- any of the components of the 44 | Original Version, by changing formats or by porting the Font Software to a 45 | new environment. 46 | 47 | "Author" refers to any designer, engineer, programmer, technical 48 | writer or other person who contributed to the Font Software. 49 | 50 | PERMISSION & CONDITIONS 51 | Permission is hereby granted, free of charge, to any person obtaining 52 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 53 | redistribute, and sell modified and unmodified copies of the Font 54 | Software, subject to the following conditions: 55 | 56 | 1) Neither the Font Software nor any of its individual components, 57 | in Original or Modified Versions, may be sold by itself. 58 | 59 | 2) Original or Modified Versions of the Font Software may be bundled, 60 | redistributed and/or sold with any software, provided that each copy 61 | contains the above copyright notice and this license. These can be 62 | included either as stand-alone text files, human-readable headers or 63 | in the appropriate machine-readable metadata fields within text or 64 | binary files as long as those fields can be easily viewed by the user. 65 | 66 | 3) No Modified Version of the Font Software may use the Reserved Font 67 | Name(s) unless explicit written permission is granted by the corresponding 68 | Copyright Holder. This restriction only applies to the primary font name as 69 | presented to the users. 70 | 71 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 72 | Software shall not be used to promote, endorse or advertise any 73 | Modified Version, except to acknowledge the contribution(s) of the 74 | Copyright Holder(s) and the Author(s) or with their explicit written 75 | permission. 76 | 77 | 5) The Font Software, modified or unmodified, in part or in whole, 78 | must be distributed entirely under this license, and must not be 79 | distributed under any other license. The requirement for fonts to 80 | remain under this license does not apply to any document created 81 | using the Font Software. 82 | 83 | TERMINATION 84 | This license becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 91 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 96 | OTHER DEALINGS IN THE FONT SOFTWARE. 97 | 98 | // REUSE-IgnoreEnd 99 | -------------------------------------------------------------------------------- /docs/cargo_auto/generic_functions_mod/index.html: -------------------------------------------------------------------------------- 1 | cargo_auto::generic_functions_mod - Rust

Module generic_functions_mod

Source
Expand description

Functions to work with CLI binary executable projects.

2 |

Binary executables need some standard functions to help to develop them efficiently.

3 |

Constants§

BLUE
ANSI color
GREEN
ANSI color
RED
ANSI color
RESET
ANSI color
YELLOW
ANSI color

Functions§

tracing_init
Initialize tracing to file logs/cargo_auto.log. \
-------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_copy_files_to_strings_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_copy_files_to_strings_mod.rs 2 | 3 | //! Copy all files from the folder into a module as strings (static &str). 4 | 5 | // trait must be in scope 6 | use base64ct::Encoding; 7 | 8 | use crate::cargo_auto_lib::error_mod::{Error, Result}; 9 | use crate::cargo_auto_lib::public_api_mod::{RESET, YELLOW}; 10 | use crate::utils_mod::{pos, ResultLogError}; 11 | 12 | // region: auto_md_to_doc_comments include doc_comments/copy_folder_files_into_module.md A /// 13 | /// Copy all files from the folder into a module as strings (static &str). 14 | /// 15 | /// The Rust code to modify has the markers: 16 | /// 17 | /// ```Rust ignore 18 | /// //comment region: files copied into strings by automation tasks 19 | /// 20 | /// //comment endregion: files copied into strings by automation tasks 21 | /// 22 | /// ``` 23 | /// 24 | /// In this instructions I changed `[//]` to `[//comment]` to not process these markers. 25 | /// 26 | /// First we create the complete text, then we check if the old text needs to be replaced. 27 | /// 28 | /// Binary files need a special treatment: 29 | /// 30 | /// ```Rust ignore 31 | /// ext_for_binary_files=vec![".ico",".jpg",".png",".woff2"]; 32 | /// ``` 33 | /// 34 | /// Exclude big folders: 35 | /// 36 | /// ```Rust ignore 37 | /// exclude_big_folders = vec!["/.git","/target","/docs"]; 38 | /// ``` 39 | /// 40 | // endregion: auto_md_to_doc_comments include doc_comments/copy_folder_files_into_module.md A /// 41 | pub fn copy_folder_files_into_module( 42 | folder_path: &std::path::Path, 43 | module_path: &std::path::Path, 44 | ext_for_binary_files: &[&str], 45 | exclude_big_folders: &[String], 46 | ) -> Result<()> { 47 | let folder_path = camino::Utf8Path::from_path(folder_path) 48 | .ok_or_else(|| Error::ErrorFromStr("folder_path is None")) 49 | .log(pos!())?; 50 | let module_path = camino::Utf8Path::from_path(module_path) 51 | .ok_or_else(|| Error::ErrorFromStr("module_path is None")) 52 | .log(pos!())?; 53 | 54 | println!(" {YELLOW}copy_folder_files_into_module {folder_path}, {module_path}{RESET}"); 55 | // traverse and get all file_names 56 | let files = crate::cargo_auto_lib::traverse_dir_with_exclude_dir(folder_path.as_std_path(), "", exclude_big_folders).log(pos!())?; 57 | let mut new_code = String::new(); 58 | for file_name in files.iter() { 59 | let file_name_short = file_name.trim_start_matches(&format!("{folder_path}/")); 60 | // avoid Cargo.lock file 61 | if file_name_short == "Cargo.lock" { 62 | continue; 63 | } 64 | // let the user define in an input parameter what files are binaries and not text. 65 | let mut is_binary_file = false; 66 | for x in ext_for_binary_files.iter() { 67 | if file_name_short.ends_with(x) { 68 | is_binary_file = true; 69 | break; 70 | } 71 | } 72 | 73 | let file_content = if is_binary_file { 74 | // convert binary file to base64 75 | let b = std::fs::read(file_name).log(pos!())?; 76 | base64ct::Base64::encode_string(&b) 77 | } else { 78 | // all others are text files 79 | std::fs::read_to_string(file_name).log(pos!())? 80 | }; 81 | 82 | new_code.push_str(&format!( 83 | r####"vec_file.push(crate::cargo_auto_lib::FileItem{{ 84 | file_name :"{}", 85 | file_content : r###"{}"###, 86 | }}); 87 | "####, 88 | &file_name_short, &file_content 89 | )); 90 | } 91 | 92 | // read the content of the module, delimited by markers 93 | let module_content = std::fs::read_to_string(module_path).log(pos!())?; 94 | let start_pos = crate::cargo_auto_lib::find_pos_start_data_after_delimiter( 95 | &module_content, 96 | 0, 97 | "// region: files copied into strings by automation tasks\n", 98 | ) 99 | .expect("didn't find // region: files copied.."); 100 | let end_pos = crate::cargo_auto_lib::find_pos_end_data_before_delimiter( 101 | &module_content, 102 | 0, 103 | "// endregion: files copied into strings by automation tasks", 104 | ) 105 | .expect("didn't find // endregion: files copied.."); 106 | let old_code = &module_content[start_pos..end_pos]; 107 | 108 | // compare the text, if different replace 109 | if old_code != new_code { 110 | let mut new_module_content = String::new(); 111 | new_module_content.push_str(&module_content[..start_pos]); 112 | new_module_content.push_str(&new_code); 113 | new_module_content.push_str(&module_content[end_pos..]); 114 | std::fs::write(module_path, &new_module_content).log(pos!())?; 115 | } 116 | Ok(()) 117 | } 118 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_git_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_git_mod 2 | 3 | //! Functions to work with git from automation_tasks_rs. 4 | 5 | use crate::cargo_auto_lib::error_mod::{Error, Result}; 6 | use crate::cargo_auto_lib::public_api_mod::{BLUE, RED, RESET}; 7 | use crate::utils_mod::{pos, ResultLogError}; 8 | 9 | /// Does git have settings for remote. 10 | pub fn git_has_remote() -> Result { 11 | // it returns only "origin" if exists or nothing if it does not exist 12 | let output = std::process::Command::new("git").arg("remote").output().log(pos!())?; 13 | // return 14 | Ok(!(String::from_utf8(output.stdout).log(pos!())?).is_empty()) 15 | } 16 | 17 | /// Check if this folder is a local Git repository. 18 | pub fn git_is_local_repository() -> Result { 19 | let output = std::process::Command::new("git").arg("status").output().log(pos!())?; 20 | let output = String::from_utf8(output.stderr).log(pos!())?; 21 | // return bool 22 | Ok(!output.contains("not a git repository")) 23 | } 24 | 25 | /// Return Url to repository: . 26 | /// 27 | /// Get the output string after $ git remote -v. 28 | /// Then finds out the link to the repository with regex. 29 | /// Returns empty string if something goes wrong: no git, no remote,... 30 | pub fn process_git_remote() -> String { 31 | /// Internal function for git remote 32 | fn git_remote_output() -> Result { 33 | let output = std::process::Command::new("git").arg("remote").arg("-v").output().log(pos!())?; 34 | 35 | let output = String::from_utf8(output.stdout).log(pos!())?; 36 | // return 37 | Ok(output) 38 | } 39 | 40 | /// Internal function returns remote repository url 41 | /// 42 | /// on GitHub actions they don't use SSH, but https, I need to check that also 43 | /// I test my regex on https://regex101.com/ 44 | /// regex capture 3 groups: website, user_name and repo_name 45 | /// "origin git@github.com:automation-tasks-rs/auto_lines_of_code.git (fetch)" 46 | /// origin https://github.com/automation-tasks-rs/auto_lines_of_code (fetch) 47 | /// println!("{}", &output); 48 | fn regex_capture(output: String) -> Result { 49 | let reg = regex::Regex::new( 50 | r#"origin\s*(?:https://)?(?:git@)?([^:/]*?)[:/]([^/]*?)/([^. ]*?)(?:\.git)?\s*\(fetch\)"#, 51 | ) 52 | .log(pos!())?; 53 | let cap = reg 54 | .captures(&output) 55 | .ok_or(Error::ErrorFromStr("Error: reg.captures is None")) 56 | .log(pos!())?; 57 | 58 | if cap.len() != 4 { 59 | return Err(Error::ErrorFromStr( 60 | "Error: cap len is not 4, because there are 4 capture groups in regex.", 61 | )); 62 | } 63 | Ok(format!("https://{}/{}/{}/", &cap[1], &cap[2], &cap[3])) 64 | } 65 | 66 | let output = match git_remote_output() { 67 | Ok(s) => s, 68 | Err(e) => { 69 | eprintln!("{RED}{e}{RESET}"); 70 | return "".to_string(); 71 | } 72 | }; 73 | match regex_capture(output) { 74 | Ok(s) => s, 75 | Err(_e) => { 76 | // eprintln!("{RED}process_git_remote error: {}{RESET}", e); 77 | "".to_string() 78 | } 79 | } 80 | } 81 | 82 | /// Interactive ask to create a new local git repository. 83 | pub fn new_local_repository(message: &str) -> Result<()> { 84 | // ask interactive 85 | println!("{BLUE}This project folder is not yet a Git repository.{RESET}"); 86 | let answer = inquire::Text::new(&format!("{BLUE}Do you want to initialize a new local git repository? (y/n){RESET}")) 87 | .prompt() 88 | .log(pos!())?; 89 | // continue if answer is "y" 90 | if answer.to_lowercase() != "y" { 91 | // early exit 92 | return Err(Error::ErrorFromStr("Ok. You don't want to initialize a new local git repository.")); 93 | } 94 | 95 | // the docs folder is mandatory because of GitHub action for pages deployment 96 | if !camino::Utf8Path::new("docs").exists() { 97 | std::fs::create_dir("docs").log(pos!())?; 98 | std::fs::write("docs/index.html", "project docs").log(pos!())?; 99 | } 100 | 101 | // create new local git repository and commit all on branch main 102 | crate::cargo_auto_lib::run_shell_command_static("git config --global init.defaultBranch main").log(pos!())?; 103 | crate::cargo_auto_lib::run_shell_command_static("git init").log(pos!())?; 104 | crate::cargo_auto_lib::run_shell_command_static("git add .").log(pos!())?; 105 | crate::cargo_auto_lib::run_shell_command(&format!(r#"git commit -m "{message}""#)).log(pos!())?; 106 | crate::cargo_auto_lib::run_shell_command_static("git branch -M main").log(pos!())?; 107 | Ok(()) 108 | } 109 | -------------------------------------------------------------------------------- /docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt: -------------------------------------------------------------------------------- 1 | // REUSE-IgnoreStart 2 | 3 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 4 | 5 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 6 | 7 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 8 | 9 | 10 | ----------------------------------------------------------- 11 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 12 | ----------------------------------------------------------- 13 | 14 | PREAMBLE 15 | The goals of the Open Font License (OFL) are to stimulate worldwide 16 | development of collaborative font projects, to support the font creation 17 | efforts of academic and linguistic communities, and to provide a free and 18 | open framework in which fonts may be shared and improved in partnership 19 | with others. 20 | 21 | The OFL allows the licensed fonts to be used, studied, modified and 22 | redistributed freely as long as they are not sold by themselves. The 23 | fonts, including any derivative works, can be bundled, embedded, 24 | redistributed and/or sold with any software provided that any reserved 25 | names are not used by derivative works. The fonts and derivatives, 26 | however, cannot be released under any other type of license. The 27 | requirement for fonts to remain under this license does not apply 28 | to any document created using the fonts or their derivatives. 29 | 30 | DEFINITIONS 31 | "Font Software" refers to the set of files released by the Copyright 32 | Holder(s) under this license and clearly marked as such. This may 33 | include source files, build scripts and documentation. 34 | 35 | "Reserved Font Name" refers to any names specified as such after the 36 | copyright statement(s). 37 | 38 | "Original Version" refers to the collection of Font Software components as 39 | distributed by the Copyright Holder(s). 40 | 41 | "Modified Version" refers to any derivative made by adding to, deleting, 42 | or substituting -- in part or in whole -- any of the components of the 43 | Original Version, by changing formats or by porting the Font Software to a 44 | new environment. 45 | 46 | "Author" refers to any designer, engineer, programmer, technical 47 | writer or other person who contributed to the Font Software. 48 | 49 | PERMISSION & CONDITIONS 50 | Permission is hereby granted, free of charge, to any person obtaining 51 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 52 | redistribute, and sell modified and unmodified copies of the Font 53 | Software, subject to the following conditions: 54 | 55 | 1) Neither the Font Software nor any of its individual components, 56 | in Original or Modified Versions, may be sold by itself. 57 | 58 | 2) Original or Modified Versions of the Font Software may be bundled, 59 | redistributed and/or sold with any software, provided that each copy 60 | contains the above copyright notice and this license. These can be 61 | included either as stand-alone text files, human-readable headers or 62 | in the appropriate machine-readable metadata fields within text or 63 | binary files as long as those fields can be easily viewed by the user. 64 | 65 | 3) No Modified Version of the Font Software may use the Reserved Font 66 | Name(s) unless explicit written permission is granted by the corresponding 67 | Copyright Holder. This restriction only applies to the primary font name as 68 | presented to the users. 69 | 70 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 71 | Software shall not be used to promote, endorse or advertise any 72 | Modified Version, except to acknowledge the contribution(s) of the 73 | Copyright Holder(s) and the Author(s) or with their explicit written 74 | permission. 75 | 76 | 5) The Font Software, modified or unmodified, in part or in whole, 77 | must be distributed entirely under this license, and must not be 78 | distributed under any other license. The requirement for fonts to 79 | remain under this license does not apply to any document created 80 | using the Font Software. 81 | 82 | TERMINATION 83 | This license becomes null and void if any of the above conditions are 84 | not met. 85 | 86 | DISCLAIMER 87 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 88 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 89 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 90 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 91 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 92 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 93 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 94 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 95 | OTHER DEALINGS IN THE FONT SOFTWARE. 96 | 97 | // REUSE-IgnoreEnd 98 | -------------------------------------------------------------------------------- /src/template_new_cli_mod.rs: -------------------------------------------------------------------------------- 1 | // template_new_cli_mod.rs 2 | 3 | //! Template for new_cli. 4 | //! 5 | //! The template is downloaded from github: 6 | //! 7 | 8 | use crate::{pos, ResultLogError, GREEN, RED, RESET, YELLOW}; 9 | 10 | /// Creates a new Rust project from template. 11 | pub fn new_cli(rust_project_name: Option, github_owner_or_organization: Option) -> anyhow::Result<()> { 12 | if rust_project_name.is_none() { 13 | println!("{RED}Error: Project name argument is missing: `cargo auto new_cli project_name github_owner_or_organization`{RESET}"); 14 | return Ok(()); 15 | } 16 | if github_owner_or_organization.is_none() { 17 | println!("{RED}Error: github_owner argument is missing: `cargo auto new_cli project_name github_owner_or_organization`{RESET}"); 18 | return Ok(()); 19 | } 20 | use anyhow::Context; 21 | let rust_project_name = rust_project_name.context("rust_project_name is None").log(pos!())?; 22 | let github_owner_or_organization = github_owner_or_organization 23 | .context("github_owner_or_organization is None") 24 | .log(pos!())?; 25 | 26 | copy_to_files(&rust_project_name, &github_owner_or_organization).log(pos!())?; 27 | 28 | println!(); 29 | println!(" {YELLOW}The command `cargo auto new_cli` generated the directory `{rust_project_name}`.{RESET}"); 30 | println!(" {YELLOW}You can open this new Rust project in VSCode:{RESET}",); 31 | println!("{GREEN}code {rust_project_name}{RESET}"); 32 | println!(" {YELLOW}Then build inside the VSCode terminal with:{RESET}"); 33 | println!("{GREEN}cargo auto build{RESET}"); 34 | println!(" {YELLOW}and follow the detailed instructions.{RESET}"); 35 | Ok(()) 36 | } 37 | 38 | /// Copy the Rust project into a compressed file. 39 | fn copy_to_files(rust_project_name: &str, github_owner_or_organization: &str) -> anyhow::Result<()> { 40 | let folder_path = std::path::Path::new(rust_project_name); 41 | if folder_path.exists() { 42 | anyhow::bail!("{RED}Error: Folder {rust_project_name} already exists! {RESET}"); 43 | } 44 | std::fs::create_dir_all(folder_path).log(pos!())?; 45 | 46 | // download latest template.tar.gz 47 | println!(" {YELLOW}Downloading template.tar.gz...{RESET}"); 48 | let file_name = "template.tar.gz"; 49 | let path = "./template.tar.gz"; 50 | let url = "https://github.com/automation-tasks-rs/cargo_auto_template_new_cli/releases/latest/download/template.tar.gz"; 51 | let reqwest_client = reqwest::blocking::Client::new(); 52 | let http_response = reqwest_client.get(url).send(); 53 | if let Ok(body) = http_response { 54 | let body = body.bytes().log(pos!())?; 55 | // Get the content of the response 56 | std::fs::write(path, &body) 57 | .or_else(|err| anyhow::bail!("Download failed for {file_name} {err}")) 58 | .log(pos!())?; 59 | } else { 60 | anyhow::bail!("Error while retrieving data: {:#?}", http_response.err()); 61 | } 62 | 63 | // decompress into folder_path 64 | let tar_gz = std::fs::File::open(path).log(pos!())?; 65 | let tar = flate2::read::GzDecoder::new(tar_gz); 66 | let mut archive = tar::Archive::new(tar); 67 | archive.unpack(folder_path).log(pos!())?; 68 | std::fs::remove_file(path).log(pos!())?; 69 | 70 | // replace placeholders inside text files 71 | for entry in walkdir::WalkDir::new(folder_path).into_iter().filter_map(Result::ok) { 72 | if entry.file_type().is_file() { 73 | // template has only valid utf8 files 74 | println!("replace: {}", entry.path().to_string_lossy()); 75 | let content = std::fs::read_to_string(entry.path()).log(pos!())?; 76 | let content = content.replace("cargo_auto_template_new_cli", rust_project_name); 77 | let content = content.replace(&"cargo_auto_template_new_cli".to_uppercase(), &rust_project_name.to_uppercase()); 78 | let content = content.replace("automation-tasks-rs", github_owner_or_organization); 79 | let content = content.replace("automation--tasks--rs", "automation-tasks-rs"); 80 | std::fs::write(entry.path(), content).log(pos!())?; 81 | } 82 | } 83 | // renaming files is tricky and must be traverse in reverse. 84 | let mut traverse_reverse: Vec = walkdir::WalkDir::new(folder_path).into_iter().filter_map(Result::ok).collect(); 85 | traverse_reverse.reverse(); 86 | for entry in traverse_reverse.iter() { 87 | if entry.file_name() == "cargo_auto_template_new_cli" { 88 | println!("rename: {}", entry.path().to_string_lossy()); 89 | use anyhow::Context; 90 | std::fs::rename( 91 | entry.path(), 92 | entry.path().parent().context("parent is None")?.join(rust_project_name), 93 | ) 94 | .log(pos!())?; 95 | } 96 | } 97 | Ok(()) 98 | } 99 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Development details 2 | 3 | ## CRUSTDE - Containerized Rust Development Environment 4 | 5 | I recommend using the CRUSTDE - Containerized Rust Development Environment to write Rust projects. 6 | Follow the instructions here 7 | . 8 | 9 | It is an isolated development environment that will not mess with you system. 10 | It will work on Linux (tested on Debian) and inside WSL (Windows Subsystem for Linux). 11 | 12 | You just need to install the Docker newer alternative: [Podman](https://podman.io/). 13 | Then you download the prepared container image from DockerHub (3GB). 14 | And then a little juggling with ssh keys. 15 | All this is simplified by running a few bash scripts. 16 | Just follow the easy instructions. 17 | 18 | The container image contains cargo, rustc, wasm-pack, basic-http-server, cargo-auto and other utils that a Rust project needs. 19 | 20 | ## Workflow with automation_tasks_rs 21 | 22 | Automation tasks that are already coded in the sub-project `automation_tasks_rs`. This is a basic workflow: 23 | 24 | ```bash 25 | cargo auto build 26 | cargo auto release 27 | cargo auto doc 28 | cargo auto test 29 | cargo auto commit_and push 30 | cargo auto publish_to_crates_io 31 | cargo auto github_new_release 32 | ``` 33 | 34 | Every task finishes with instructions how to proceed. 35 | The [cargo-auto](https://github.com/automation-tasks-rs/cargo-auto) and [dev_bestia_cargo_completion](https://github.com/automation-tasks-rs/dev_bestia_cargo_completion) are already installed inside the CRUSTDE container. 36 | 37 | You can open the automation sub-project in VSCode and then code your own tasks in Rust. 38 | 39 | ```bash 40 | code automation_tasks_rs 41 | ``` 42 | 43 | ## Development of cargo-auto 44 | 45 | I am using the previous version of `cargo-auto` to develop the next version. I added the `automation_tasks_rs` folder and prepared the automation tasks that are used repetitively. 46 | 47 | ## Templates 48 | 49 | Inside the cargo-auto project, there are some Rust sub-projects that are templates. I can open a new editor for these directories and build these crates independently. So it is easy to debug and develop. 50 | Sadly, I cannot publish these directories and files to `crates.io`. I can effectively publish only the source code inside my main Rust project `cargo-auto`. 51 | Therefore, before publishing I copy the content of these files into the modules `template_new_auto_for_cli_mod.rs` on every build. It is not difficult now that Rust has fantastic [raw strings](https://doc.rust-lang.org/rust-by-example/std/str.html). For this repetitive task as always, I prepared an automation task in `automation_tasks_rs`. 52 | 53 | ## Error handling thiserror and anyhow 54 | 55 | Rule number one is never to use `.unwrap()` and `panic!()` in your real Rust code. It is a sign, you are not Error handling properly. 56 | When using panic or even worse process.exit() the program will not finish execution in a nice way. Avoid that. 57 | Maybe `unwrap()` can be fine for some fast learning examples, but for any real-life Rust code, you must use some `Error handling`. There are many different ways to do that in Rust. I choose the pair of libraries `thiserror` and `anyhow`. The first is made for libraries, the second is made for bin-executables. 58 | The library needs an Enum with all the possible errors that this library can return. With `#[derive(Error)]` this enum gets everything needed to be a true Rust error struct. Every error can have a formatting string and a struct of data. Internal errors can be propagated without change using the `transparent` cfg. 59 | To transform `Option<>` into `Result<>` when using `thiserror` use `ok_or_else(||)`. 60 | The bin-executable does not want to be involved in every possible error separately. It needs an umbrella for all possible errors with `anyhow::Result`. 61 | Inside the code, mostly propagate the errors with the `?` Operator after the `Result` value instead of unwrap() or the match expression. 62 | To transform `Option<>` into `Result<>` `use anyhow::Context` trait and `context()` method. 63 | In the tests we don't want to work with Error handling. There, instead of `.unwrap()`, use the similar function `.expect(&str)` that has an additional description string. I use `expect()` when I am 100% sure the panic cannot happen because I checked some conditions before it. 64 | 65 | ## Debug with tracing and log to file 66 | 67 | For debugging purposes the program has tracing and log to file. 68 | If the environment variable CARGO_AUTO_LOG exists than the tracing to file is enabled. 69 | The log is appended to files in the local `logs/` folder. 70 | In the env var CARGO_AUTO_LOG we can define filters. 71 | A filter consists of one or more comma-separated directives 72 | target[span{field=value}]=level 73 | Levels order: 1. ERROR, 2. WARN, 3. INFO, 4. DEBUG, 5. TRACE 74 | ERROR level is always logged. 75 | Example of filter for a single execution: 76 | 77 | ```bash 78 | CARGO_AUTO_LOG="debug,hyper_util=info,reqwest=info" ./{package_name} 79 | ``` 80 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_cargo_toml_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_cargo_toml_mod 2 | 3 | //! Functions to get data from Cargo.toml. 4 | 5 | use crate::{ 6 | cargo_auto_lib::error_mod::{Error, Result}, 7 | utils_mod::{pos, ResultLogError}, 8 | }; 9 | use lazy_static::lazy_static; 10 | use regex::*; 11 | 12 | lazy_static! { 13 | /// remove email from author 14 | static ref REGEX_REMOVE_EMAIL: Regex = Regex::new(r#"( <.+?>)"#).expect("regex new"); 15 | } 16 | 17 | /// Read data from Cargo.toml. 18 | pub struct CargoToml { 19 | /// the first Cargo.toml is maybe a workspace. It has a different structure. 20 | cargo_toml_workspace_maybe: cargo_toml::Manifest, 21 | /// the main Cargo.toml is different for single project or for workspace 22 | /// for workspace is the first `main` member 23 | _cargo_toml_main: cargo_toml::Manifest, 24 | /// the package is read from the main Cargo.toml 25 | package: cargo_toml::Package, 26 | } 27 | 28 | impl crate::cargo_auto_lib::public_api_mod::CargoTomlPublicApiMethods for CargoToml { 29 | /// read Cargo.toml, for workspaces it is the Cargo.toml of the first member 30 | fn read() -> Result { 31 | let absolute_path = std::path::absolute("Cargo.toml").log(pos!())?; 32 | let cargo_toml_workspace_maybe = cargo_toml::Manifest::from_path(absolute_path).log(pos!())?; 33 | let cargo_toml_main = match &cargo_toml_workspace_maybe.workspace { 34 | None => cargo_toml_workspace_maybe.clone(), 35 | Some(workspace) => { 36 | let main_member = &workspace.members[0]; 37 | let absolute_path = std::path::absolute(format!("{}/Cargo.toml", main_member)).log(pos!())?; 38 | // return cargo_main 39 | cargo_toml::Manifest::from_path(absolute_path).log(pos!())? 40 | } 41 | }; 42 | let package = cargo_toml_main 43 | .package 44 | .as_ref() 45 | .ok_or_else(|| Error::ErrorFromStr("package is None")) 46 | .log(pos!())? 47 | .to_owned(); 48 | Ok(CargoToml { 49 | cargo_toml_workspace_maybe, 50 | _cargo_toml_main: cargo_toml_main, 51 | package, 52 | }) 53 | } 54 | 55 | /// Cargo.toml package name 56 | fn package_name(&self) -> String { 57 | self.package.name.to_string() 58 | } 59 | 60 | /// Cargo.toml package version 61 | fn package_version(&self) -> String { 62 | self.package.version().to_string() 63 | } 64 | 65 | /// Cargo.toml package authors as string 66 | fn package_authors_string(&self) -> String { 67 | let authors = crate::cargo_auto_lib::utils_mod::concatenate_vec_to_string(self.package.authors(), ", "); 68 | authors 69 | } 70 | 71 | /// Cargo.toml package authors as string without emails 72 | fn package_author_name(&self) -> String { 73 | let author = self.package_authors_string(); 74 | let author = REGEX_REMOVE_EMAIL.replace_all(&author, "").to_string(); 75 | author 76 | } 77 | 78 | /// Cargo.toml package repository 79 | fn package_repository(&self) -> Option { 80 | self.package.repository().map(|x| x.to_string()) 81 | } 82 | 83 | /// Cargo.toml package repository 84 | fn package_description(&self) -> Option { 85 | self.package.description().map(|x| x.to_string()) 86 | } 87 | 88 | /// Cargo.toml package homepage 89 | fn package_homepage(&self) -> String { 90 | match self.package.homepage() { 91 | None => String::new(), 92 | Some(x) => x.to_string(), 93 | } 94 | } 95 | 96 | /// Cargo.toml workspace members 97 | fn workspace_members(&self) -> Option> { 98 | self.cargo_toml_workspace_maybe 99 | .workspace 100 | .as_ref() 101 | .map(|workspace| workspace.members.clone()) 102 | } 103 | 104 | /// github_owner from package_repository 105 | fn github_owner(&self) -> Option { 106 | match self.package_repository() { 107 | Some(repository) => { 108 | let splitted: Vec<&str> = repository.trim_start_matches("https://").split("/").collect(); 109 | Some(splitted[1].to_string()) 110 | } 111 | None => None, 112 | } 113 | } 114 | /// Cargo.toml package keywords 115 | fn package_keywords(&self) -> Vec { 116 | self.package.keywords().to_owned() 117 | } 118 | } 119 | 120 | #[cfg(test)] 121 | mod test { 122 | 123 | use super::*; 124 | 125 | #[test] 126 | pub fn test_cargo_toml() { 127 | use crate::cargo_auto_lib::public_api_mod::CargoTomlPublicApiMethods; 128 | let cargo_toml = CargoToml::read().expect("error"); 129 | assert_eq!(cargo_toml.package_author_name(), "Bestia.dev"); 130 | assert_eq!(cargo_toml.package_homepage(), "https://bestia.dev"); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /docs/static.files/SourceSerif4-LICENSE-a2cfd9d5.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. 4 | Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. 5 | 6 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 7 | 8 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 9 | 10 | 11 | ----------------------------------------------------------- 12 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 13 | ----------------------------------------------------------- 14 | 15 | PREAMBLE 16 | The goals of the Open Font License (OFL) are to stimulate worldwide 17 | development of collaborative font projects, to support the font creation 18 | efforts of academic and linguistic communities, and to provide a free and 19 | open framework in which fonts may be shared and improved in partnership 20 | with others. 21 | 22 | The OFL allows the licensed fonts to be used, studied, modified and 23 | redistributed freely as long as they are not sold by themselves. The 24 | fonts, including any derivative works, can be bundled, embedded, 25 | redistributed and/or sold with any software provided that any reserved 26 | names are not used by derivative works. The fonts and derivatives, 27 | however, cannot be released under any other type of license. The 28 | requirement for fonts to remain under this license does not apply 29 | to any document created using the fonts or their derivatives. 30 | 31 | DEFINITIONS 32 | "Font Software" refers to the set of files released by the Copyright 33 | Holder(s) under this license and clearly marked as such. This may 34 | include source files, build scripts and documentation. 35 | 36 | "Reserved Font Name" refers to any names specified as such after the 37 | copyright statement(s). 38 | 39 | "Original Version" refers to the collection of Font Software components as 40 | distributed by the Copyright Holder(s). 41 | 42 | "Modified Version" refers to any derivative made by adding to, deleting, 43 | or substituting -- in part or in whole -- any of the components of the 44 | Original Version, by changing formats or by porting the Font Software to a 45 | new environment. 46 | 47 | "Author" refers to any designer, engineer, programmer, technical 48 | writer or other person who contributed to the Font Software. 49 | 50 | PERMISSION & CONDITIONS 51 | Permission is hereby granted, free of charge, to any person obtaining 52 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 53 | redistribute, and sell modified and unmodified copies of the Font 54 | Software, subject to the following conditions: 55 | 56 | 1) Neither the Font Software nor any of its individual components, 57 | in Original or Modified Versions, may be sold by itself. 58 | 59 | 2) Original or Modified Versions of the Font Software may be bundled, 60 | redistributed and/or sold with any software, provided that each copy 61 | contains the above copyright notice and this license. These can be 62 | included either as stand-alone text files, human-readable headers or 63 | in the appropriate machine-readable metadata fields within text or 64 | binary files as long as those fields can be easily viewed by the user. 65 | 66 | 3) No Modified Version of the Font Software may use the Reserved Font 67 | Name(s) unless explicit written permission is granted by the corresponding 68 | Copyright Holder. This restriction only applies to the primary font name as 69 | presented to the users. 70 | 71 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 72 | Software shall not be used to promote, endorse or advertise any 73 | Modified Version, except to acknowledge the contribution(s) of the 74 | Copyright Holder(s) and the Author(s) or with their explicit written 75 | permission. 76 | 77 | 5) The Font Software, modified or unmodified, in part or in whole, 78 | must be distributed entirely under this license, and must not be 79 | distributed under any other license. The requirement for fonts to 80 | remain under this license does not apply to any document created 81 | using the Font Software. 82 | 83 | TERMINATION 84 | This license becomes null and void if any of the above conditions are 85 | not met. 86 | 87 | DISCLAIMER 88 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 89 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 90 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 91 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 92 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 93 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 94 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 96 | OTHER DEALINGS IN THE FONT SOFTWARE. 97 | 98 | 99 | -------------------------------------------------------------------------------- /docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt: -------------------------------------------------------------------------------- 1 | // REUSE-IgnoreStart 2 | 3 | Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), 4 | 5 | with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, 6 | NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, 7 | Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, 8 | NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, 9 | Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri 10 | 11 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 12 | This license is copied below, and is also available with a FAQ at: 13 | http://scripts.sil.org/OFL 14 | 15 | 16 | ----------------------------------------------------------- 17 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 18 | ----------------------------------------------------------- 19 | 20 | PREAMBLE 21 | The goals of the Open Font License (OFL) are to stimulate worldwide 22 | development of collaborative font projects, to support the font creation 23 | efforts of academic and linguistic communities, and to provide a free and 24 | open framework in which fonts may be shared and improved in partnership 25 | with others. 26 | 27 | The OFL allows the licensed fonts to be used, studied, modified and 28 | redistributed freely as long as they are not sold by themselves. The 29 | fonts, including any derivative works, can be bundled, embedded, 30 | redistributed and/or sold with any software provided that any reserved 31 | names are not used by derivative works. The fonts and derivatives, 32 | however, cannot be released under any other type of license. The 33 | requirement for fonts to remain under this license does not apply 34 | to any document created using the fonts or their derivatives. 35 | 36 | DEFINITIONS 37 | "Font Software" refers to the set of files released by the Copyright 38 | Holder(s) under this license and clearly marked as such. This may 39 | include source files, build scripts and documentation. 40 | 41 | "Reserved Font Name" refers to any names specified as such after the 42 | copyright statement(s). 43 | 44 | "Original Version" refers to the collection of Font Software components as 45 | distributed by the Copyright Holder(s). 46 | 47 | "Modified Version" refers to any derivative made by adding to, deleting, 48 | or substituting -- in part or in whole -- any of the components of the 49 | Original Version, by changing formats or by porting the Font Software to a 50 | new environment. 51 | 52 | "Author" refers to any designer, engineer, programmer, technical 53 | writer or other person who contributed to the Font Software. 54 | 55 | PERMISSION & CONDITIONS 56 | Permission is hereby granted, free of charge, to any person obtaining 57 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 58 | redistribute, and sell modified and unmodified copies of the Font 59 | Software, subject to the following conditions: 60 | 61 | 1) Neither the Font Software nor any of its individual components, 62 | in Original or Modified Versions, may be sold by itself. 63 | 64 | 2) Original or Modified Versions of the Font Software may be bundled, 65 | redistributed and/or sold with any software, provided that each copy 66 | contains the above copyright notice and this license. These can be 67 | included either as stand-alone text files, human-readable headers or 68 | in the appropriate machine-readable metadata fields within text or 69 | binary files as long as those fields can be easily viewed by the user. 70 | 71 | 3) No Modified Version of the Font Software may use the Reserved Font 72 | Name(s) unless explicit written permission is granted by the corresponding 73 | Copyright Holder. This restriction only applies to the primary font name as 74 | presented to the users. 75 | 76 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 77 | Software shall not be used to promote, endorse or advertise any 78 | Modified Version, except to acknowledge the contribution(s) of the 79 | Copyright Holder(s) and the Author(s) or with their explicit written 80 | permission. 81 | 82 | 5) The Font Software, modified or unmodified, in part or in whole, 83 | must be distributed entirely under this license, and must not be 84 | distributed under any other license. The requirement for fonts to 85 | remain under this license does not apply to any document created 86 | using the Font Software. 87 | 88 | TERMINATION 89 | This license becomes null and void if any of the above conditions are 90 | not met. 91 | 92 | DISCLAIMER 93 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 94 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 95 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 96 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 97 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 98 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 99 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 100 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 101 | OTHER DEALINGS IN THE FONT SOFTWARE. 102 | 103 | // REUSE-IgnoreEnd 104 | -------------------------------------------------------------------------------- /src/outside_of_rust_project_mod.rs: -------------------------------------------------------------------------------- 1 | // outside_of_rust_project_mod.rs 2 | 3 | //! Commands accessible outside a Rust project folder. 4 | 5 | // region: use statements 6 | // endregion 7 | 8 | use crate::{pos, ResultLogError}; 9 | #[allow(unused)] 10 | use crate::{GREEN, RED, RESET, YELLOW}; 11 | 12 | /// Parse when it is run outside a Rust project. \ 13 | /// 14 | /// It must have the argument "new_cli" or "new_wasm" or "new_pwa_wasm" and the project title. 15 | pub fn parse_args(args: &mut std::env::Args) -> anyhow::Result<()> { 16 | // the first argument is the task: new_cli 17 | // wooow! There is a difference if I call the standalone binary or as a cargo subcommand: 18 | // cargo-auto build - build is the arg_1 19 | // cargo auto build - build is the arg_2 20 | let arg_1 = args.next(); 21 | match arg_1 { 22 | None => print_help_from_cargo_auto(), 23 | Some(task) => { 24 | if task != "auto" { 25 | // when calling as `cargo auto build` 26 | match_first_argument(&task, args).log(pos!())?; 27 | } else { 28 | // when calling as `cargo-auto build` 29 | let arg_2 = args.next(); 30 | match arg_2 { 31 | None => print_help_from_cargo_auto(), 32 | Some(task) => match_first_argument(&task, args)?, 33 | } 34 | } 35 | } 36 | } 37 | Ok(()) 38 | } 39 | 40 | /// Print help for cargo-auto. 41 | fn print_help_from_cargo_auto() { 42 | println!( 43 | r#" 44 | {YELLOW}Welcome to cargo-auto! 45 | This program automates your custom tasks when developing a Rust project.{RESET} 46 | 47 | {YELLOW}Outside of a Rust project, cargo-auto can create a new Rust project:{RESET} 48 | {GREEN}cargo auto new_cli project_name github_owner{RESET}{YELLOW} - a simple yet complete CLI application, better then `cargo new`{RESET} 49 | {GREEN}cargo auto new_wasm project_name github_owner web_server_domain server_username{RESET}{YELLOW} - a complete wasm application that works inside the browser{RESET} 50 | {GREEN}cargo auto new_pwa_wasm project_name github_owner web_server_domain server_username{RESET}{YELLOW} - There must already be the `icon512x512.png` file to create the icons.{RESET} 51 | {YELLOW}Modify them with the required data for your pwa project and then repeat the same command.{RESET} 52 | 53 | {YELLOW}© 2025 bestia.dev MIT License github.com/automation-tasks-rs/cargo-auto{RESET} 54 | "# 55 | ); 56 | } 57 | 58 | /// Get the first argument is the task: new_cli, or new_wasm... \ 59 | /// 60 | /// In development use: `cargo run -- new_cli`. 61 | fn match_first_argument(task: &str, args: &mut std::env::Args) -> anyhow::Result<()> { 62 | if task == "completion" { 63 | completion(); 64 | } else if task == "new_cli" { 65 | let rust_project_name = args.next(); 66 | let github_owner_or_organization = args.next(); 67 | crate::template_new_cli_mod::new_cli(rust_project_name, github_owner_or_organization).log(pos!())?; 68 | } else if task == "new_wasm" { 69 | let rust_project_name = args.next(); 70 | let github_owner_or_organization = args.next(); 71 | let web_server_domain = args.next(); 72 | let server_username = args.next(); 73 | crate::template_new_wasm_mod::new_wasm(rust_project_name, github_owner_or_organization, web_server_domain, server_username) 74 | .log(pos!())?; 75 | } else if task == "new_pwa_wasm" { 76 | let rust_project_name = args.next(); 77 | let github_owner_or_organization = args.next(); 78 | let web_server_domain = args.next(); 79 | let server_username = args.next(); 80 | crate::template_new_pwa_wasm_mod::new_pwa_wasm(rust_project_name, github_owner_or_organization, web_server_domain, server_username) 81 | .log(pos!())?; 82 | } else { 83 | print_help_from_cargo_auto(); 84 | } 85 | Ok(()) 86 | } 87 | 88 | /// Sub-command for bash auto-completion of `cargo auto` using the crate `dev_bestia_cargo_completion`. 89 | fn completion() { 90 | /// println one, more or all sub_commands 91 | fn completion_return_one_or_more_sub_commands(sub_commands: Vec<&str>, word_being_completed: &str) { 92 | let mut sub_found = false; 93 | for sub_command in sub_commands.iter() { 94 | if sub_command.starts_with(word_being_completed) { 95 | println!("{}", sub_command); 96 | sub_found = true; 97 | } 98 | } 99 | if !sub_found { 100 | // print all sub-commands 101 | for sub_command in sub_commands.iter() { 102 | println!("{}", sub_command); 103 | } 104 | } 105 | } 106 | 107 | let args: Vec = std::env::args().collect(); 108 | let last_word = args[2].as_str(); 109 | let mut word_being_completed = " "; 110 | if args.len() > 3 { 111 | word_being_completed = args[3].as_str(); 112 | } 113 | if last_word == "cargo-auto" || last_word == "auto" { 114 | let sub_commands = vec!["new_cli", "new_wasm", "new_pwa_wasm"]; 115 | completion_return_one_or_more_sub_commands(sub_commands, word_being_completed); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_github_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_github_mod 2 | 3 | //! Functions to work with GitHub. 4 | 5 | // bring trait into scope 6 | use crate::cargo_auto_lib::error_mod::{Error, Result}; 7 | use crate::cargo_auto_lib::{CargoTomlPublicApiMethods, ShellCommandLimitedDoubleQuotesSanitizerTrait}; 8 | use crate::utils_mod::{pos, ResultLogError}; 9 | /// File contains releases changelog 10 | pub const RELEASES_MD: &str = "RELEASES.md"; 11 | 12 | /// Sync, check, create, push git tag. 13 | pub fn git_tag_sync_check_create_push(version: &str) -> Result { 14 | // sync the local and remote tags 15 | crate::cargo_auto_lib::run_shell_command_static("git fetch origin --tags --force").log(pos!())?; 16 | 17 | let tags = crate::cargo_auto_lib::run_shell_command_output("git tag").log(pos!())?.stdout; 18 | let tag_name_version = format!("v{}", &version); 19 | if !tags.contains(&format!("{}\n", tag_name_version)) { 20 | // create git tag and push 21 | crate::cargo_auto_lib::ShellCommandLimitedDoubleQuotesSanitizer::new( 22 | r#"git tag -f -a "{tag_name_version}" -m "version_{version}" "#, 23 | ) 24 | .log(pos!())? 25 | .arg("{tag_name_version}", &tag_name_version) 26 | .log(pos!())? 27 | .arg("{version}", version) 28 | .log(pos!())? 29 | .run() 30 | .log(pos!())?; 31 | 32 | crate::cargo_auto_lib::run_shell_command_static("git push origin --tags").log(pos!())?; 33 | } 34 | // return 35 | Ok(tag_name_version) 36 | } 37 | 38 | /// Get release text from RELEASES.md. 39 | /// 40 | /// First, the user must write the content into file RELEASES.md in the section ## Unreleased. 41 | /// Then the automation task will copy the content to GitHub release 42 | /// and create a new Version title in RELEASES.md. 43 | pub fn body_text_from_releases_md() -> Result { 44 | create_releases_md_if_file_not_exist().log(pos!())?; 45 | let release_md = std::fs::read_to_string(RELEASES_MD).log(pos!())?; 46 | // find the start of ## Unreleased 47 | let pos_start_data = crate::cargo_auto_lib::find_pos_start_data_after_delimiter(&release_md, 0, "## Unreleased\n") 48 | .map_err(|_| Error::ErrorFromStr("## Unreleased is None")) 49 | .log(pos!())?; 50 | // find the beginning of the next ## Version 51 | let pos_end_data = crate::cargo_auto_lib::find_pos_end_data_before_delimiter(&release_md, pos_start_data, "## Version ") 52 | .map_err(|_| Error::ErrorFromStr("## Version is None")) 53 | .log(pos!())?; 54 | let body_md_text = release_md[pos_start_data..pos_end_data - 1].to_string(); 55 | 56 | // return 57 | Ok(body_md_text) 58 | } 59 | 60 | /// Create a new Version title in RELEASES.md. 61 | pub fn create_new_version_in_releases_md(release_name: &str) -> Result<()> { 62 | create_releases_md_if_file_not_exist().log(pos!())?; 63 | let release_md = std::fs::read_to_string(RELEASES_MD).log(pos!())?; 64 | // find the start of ## Unreleased 65 | let pos_start_data = crate::cargo_auto_lib::find_pos_start_data_after_delimiter(&release_md, 0, "## Unreleased\n") 66 | .map_err(|_| Error::ErrorFromStr("## Unreleased is None")) 67 | .log(pos!())?; 68 | 69 | // create a new Version title after ## Unreleased in RELEASES.md 70 | let new_release_md = format!( 71 | "{}\n## {}\n{}", 72 | &release_md[..pos_start_data], 73 | &release_name, 74 | &release_md[pos_start_data..] 75 | ); 76 | std::fs::write(RELEASES_MD, new_release_md).log(pos!())?; 77 | // return 78 | Ok(()) 79 | } 80 | 81 | /// Create RELEASES.md if file not exist 82 | fn create_releases_md_if_file_not_exist() -> Result<()> { 83 | if !camino::Utf8Path::new(RELEASES_MD).exists() { 84 | // create the template file 85 | let cargo_toml = crate::cargo_auto_lib::CargoToml::read().log(pos!())?; 86 | let project_name = cargo_toml.package_name(); 87 | let github_owner = cargo_toml 88 | .github_owner() 89 | .ok_or_else(|| Error::ErrorFromStr("github_owner is None")) 90 | .log(pos!())?; 91 | let template = format!( 92 | r#"# Releases changelog of {project_name} 93 | 94 | All notable changes to this project will be documented in this file. 95 | This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 96 | The library releases will be published on crates.io. 97 | The cargo-auto automation task will use the content of the section `## Unreleased` to create 98 | the GitHub release consistently with this file. 99 | The ongoing changes that are not released, are visible in the git commits and GitHub pull requests. 100 | The TODO section is part of the [README.md](https://github.com/{github_owner}/{project_name}). 101 | 102 | ## Unreleased 103 | 104 | ## Version 0.0.1 105 | 106 | "# 107 | ); 108 | std::fs::write(RELEASES_MD, template).log(pos!())?; 109 | } 110 | Ok(()) 111 | } 112 | 113 | /// Add commit message to Unreleased in RELEASES.md. 114 | pub fn add_message_to_unreleased(message: &str) -> Result<()> { 115 | create_releases_md_if_file_not_exist().log(pos!())?; 116 | let release_md = std::fs::read_to_string(RELEASES_MD).log(pos!())?; 117 | // find the beginning of the first ## Version 118 | let Ok(pos_end_data) = crate::cargo_auto_lib::find_pos_end_data_before_delimiter(&release_md, 0, "## Version ") else { 119 | return Ok(()); 120 | }; 121 | // add before the first ## Version 122 | // I expect only one empty line before ## Version 123 | let added_message_md = format!("{}- {}\n\n{}", &release_md[..pos_end_data], message, &release_md[pos_end_data..]); 124 | std::fs::write(RELEASES_MD, added_message_md).log(pos!())?; 125 | Ok(()) 126 | } 127 | -------------------------------------------------------------------------------- /src/file_hashes_mod.rs: -------------------------------------------------------------------------------- 1 | // file_hashes_mod.rs 2 | 3 | //! Calculate file hashes. 4 | //! 5 | //! File hashes are used to check if some file has changed. 6 | //! Then we can run commands like compile only if a file has changed. 7 | 8 | use serde_derive::{Deserialize, Serialize}; 9 | use sha2::Digest; 10 | 11 | use crate::{pos, ResultLogError}; 12 | 13 | // region: structs 14 | 15 | /// Struct with file metadata. 16 | #[derive(Serialize, Deserialize)] 17 | pub struct FileMetaData { 18 | /// filename with path from Cargo.toml folder 19 | filename: String, 20 | /// hash of file 21 | filehash: String, 22 | } 23 | 24 | /// The struct represents the file automation_tasks_rs/._file_hashes.json. 25 | #[derive(Serialize, Deserialize)] 26 | pub struct FileHashes { 27 | /// vector of file metadata 28 | pub vec_file_metadata: Vec, 29 | } 30 | 31 | // endregion: structs 32 | 33 | /// Check if the files are modified in automation_tasks_rs. \ 34 | /// 35 | /// The modified date of files is not usable when using git. \ 36 | /// The checkout will make dates newer than they really are. \ 37 | /// I should use a hash of files and write them in the same directory for later comparison. 38 | pub fn is_project_changed() -> anyhow::Result { 39 | let vec_of_metadata = read_file_metadata().log(pos!())?; 40 | let js_struct = read_json_file(&crate::PATH_FILE_HASHES_JSON.to_string_lossy()).log(pos!())?; 41 | // return true or false 42 | Ok(!are_all_files_equal(&vec_of_metadata, &js_struct.vec_file_metadata)) 43 | } 44 | 45 | /// Check if all files are equal. 46 | fn are_all_files_equal(vec_of_metadata: &[FileMetaData], js_vec_of_metadata: &[FileMetaData]) -> bool { 47 | let mut is_files_equal = true; 48 | for x in vec_of_metadata.iter() { 49 | //search in json file 50 | let mut is_one_equal = false; 51 | for y in js_vec_of_metadata.iter() { 52 | if x.filename == y.filename && x.filehash == y.filehash { 53 | is_one_equal = true; 54 | break; 55 | } 56 | } 57 | if !is_one_equal { 58 | // println!("{} {}", x.filename, x.filehash); 59 | is_files_equal = false; 60 | break; 61 | } 62 | } 63 | is_files_equal 64 | } 65 | 66 | /// Make a vector of file metadata. 67 | pub fn read_file_metadata() -> anyhow::Result> { 68 | let mut vec_of_metadata: Vec = Vec::new(); 69 | 70 | // calculate hash of Cargo.toml 71 | let filehash = sha256_digest(&crate::PATH_CARGO_TOML).log(pos!())?; 72 | vec_of_metadata.push(FileMetaData { 73 | filename: crate::PATH_CARGO_TOML.to_string_lossy().to_string(), 74 | filehash, 75 | }); 76 | 77 | // calculate hash of file of the executable file 78 | let filehash = sha256_digest(&crate::PATH_TARGET_DEBUG_AUTOMATION_TASKS_RS).log(pos!())?; 79 | vec_of_metadata.push(FileMetaData { 80 | filename: crate::PATH_TARGET_DEBUG_AUTOMATION_TASKS_RS.to_string_lossy().to_string(), 81 | filehash, 82 | }); 83 | 84 | // all files in the src/ directory 85 | for entry in walkdir::WalkDir::new(crate::PATH_SRC.as_path()).into_iter().filter_map(Result::ok) { 86 | if entry.file_type().is_file() { 87 | let path = entry.path(); 88 | // calculate hash of file 89 | let filehash = sha256_digest(path).log(pos!())?; 90 | vec_of_metadata.push(FileMetaData { 91 | filename: path.to_string_lossy().to_string(), 92 | filehash, 93 | }); 94 | } 95 | } 96 | Ok(vec_of_metadata) 97 | } 98 | 99 | /// Read automation_tasks_rs/.file_hashes.json. 100 | fn read_json_file(json_filepath: &str) -> anyhow::Result { 101 | let js_struct: FileHashes; 102 | let f = std::fs::read_to_string(json_filepath); 103 | 104 | match f { 105 | Ok(x) => { 106 | // check if file have CRLF instead of LF. This are unusable - create empty struct 107 | if x.contains("\r\n") { 108 | //create empty struct 109 | js_struct = FileHashes { 110 | vec_file_metadata: Vec::new(), 111 | } 112 | } else { 113 | //read struct from file 114 | js_struct = serde_json::from_str(x.as_str()).log(pos!())?; 115 | } 116 | } 117 | Err(_error) => { 118 | // println!("Creating new file: {}", json_filepath); 119 | //create empty struct 120 | js_struct = FileHashes { 121 | vec_file_metadata: Vec::new(), 122 | } 123 | } 124 | }; 125 | Ok(js_struct) 126 | } 127 | 128 | /// Calculate the hash for a file. 129 | fn sha256_digest(path: &std::path::Path) -> anyhow::Result { 130 | let file = std::fs::File::open(path).log(pos!())?; 131 | let mut reader = std::io::BufReader::new(file); 132 | let mut hasher = ::new(); 133 | let mut buffer = [0; 1024]; 134 | use std::io::Read; 135 | loop { 136 | let count = reader.read(&mut buffer)?; 137 | if count == 0 { 138 | break; 139 | } 140 | hasher.update(&buffer[..count]); 141 | } 142 | let digest = hasher.finalize(); 143 | let hash_string = data_encoding::HEXLOWER.encode(digest.as_ref()); 144 | // return 145 | Ok(hash_string) 146 | } 147 | 148 | /// Save the new file metadata. 149 | pub fn save_json_file_for_file_meta_data(vec_of_metadata: Vec) -> anyhow::Result<()> { 150 | let x = FileHashes { 151 | vec_file_metadata: vec_of_metadata, 152 | }; 153 | let y = serde_json::to_string_pretty(&x)?; 154 | let json_filepath = crate::PATH_FILE_HASHES_JSON.as_path(); 155 | let _f = std::fs::write(json_filepath, y); 156 | Ok(()) 157 | } 158 | -------------------------------------------------------------------------------- /RELEASES.md: -------------------------------------------------------------------------------- 1 | # RELEASES cargo-auto 2 | 3 | All notable changes to this project will be documented in this file. 4 | This project does not adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The version number represents the datetime of release. This is just fine for binary executables. 5 | The executable binary releases will be published on crates.io. 6 | The cargo-auto automation task will use the content of the section `## Unreleased` to create 7 | the GitHub release consistently with this file. 8 | The ongoing changes that are not released, are visible in the git commits and github pull requests. 9 | The TODO section is part of the [README.md](https://github.com/automation-tasks-rs/rust_project_name). 10 | 11 | ## Unreleased 12 | 13 | ## Version 2025.1108.909 (2025-11-08) 14 | 15 | - utils, debug 16 | 17 | ## Version 2025.1107.19 (2025-11-07) 18 | 19 | - .log(pos!())? 20 | 21 | ## Version 2025.1106.2142 (2025-11-06) 22 | 23 | - to_uppercase 24 | 25 | ## Version 2025.1106.1803 (2025-11-06) 26 | 27 | - ResultLogError 28 | 29 | ## Version 2025.1105.1936 (2025-11-05) 30 | 31 | - update_automation_tasks_rs 32 | - crossplatform_path 4.0.1 33 | 34 | ## Version 2025.1104.1045 (2025-11-04) 35 | 36 | - anyhow error handling 37 | - automation_tasks_rs_update 38 | 39 | ## Version 2025.814.1154 (2025-08-14) 40 | 41 | - replace filenames 42 | 43 | ## Version 2025.331.1323 (2025-03-31) 44 | 45 | - check dir exists 46 | 47 | ## Version 2025.330.1926 (2025-03-30) 48 | 49 | - update_auto 50 | 51 | ## Version 2025.330.1849 (2025-03-30) 52 | 53 | - update automation 54 | 55 | ## Version 2025.330.1709 (2025-03-30) 56 | 57 | - update 58 | 59 | ## Version 2025.330.1650 (2025-03-30) 60 | 61 | - removed cargo_auto_template_new_auto_for_cli 62 | 63 | ## Version 2025.329.2356 (2025-03-29) 64 | 65 | - update_automation_tasks_rs 66 | 67 | ## Version 2025.329.1647 (2025-03-29) 68 | 69 | - update automation 70 | 71 | ## Version 2025.318.1132 (2025-03-18) 72 | 73 | - auto 74 | 75 | ## Version 2025.317.1619 (2025-03-17) 76 | 77 | - cargo_auto_lib = 3.0.12 78 | 79 | - ssh_agent_client_rs_git_bash = 0.0.11 80 | 81 | ## Version 2025.313.1549 (2025-03-13) 82 | 83 | - docs 84 | - compatibility with win-git-bash 85 | 86 | ## Version 2025.310.1341 (2025-03-10) 87 | 88 | - clippy 89 | 90 | ## Version 2025.310.1327 (2025-03-10) 91 | 92 | - update automation 93 | 94 | ## Version 2025.309.1933 (2025-03-09) 95 | 96 | - walkir src because possible sub folders 97 | 98 | ## Version 2025.309.1725 (2025-03-09) 99 | 100 | - ring does not compile in windows 101 | 102 | ## Version 2024.504.1814 (2024-05-04) 103 | 104 | - new_auto 105 | - pest 2.7.9->2.7.10 106 | - caret versions 107 | 108 | ## Version 2024.501.1827 (2024-05-01) 109 | 110 | - templates from GitHub 111 | 112 | ## Version 2024.422.214 (2024-04-22) 113 | 114 | - allow dead code 115 | 116 | ## Version 2024.419.1824 (2024-04-19) 117 | 118 | - no log file for tracing 119 | 120 | ## Version 2024.419.1816 (2024-04-19) 121 | 122 | - 1.1.2 123 | 124 | ## Version 2024.419.1740 (2024-04-19) 125 | 126 | - 1.1.2 127 | 128 | ## Version 2024.419.1726 (2024-04-19) 129 | 130 | - sanitize shell command 131 | 132 | ## Version 2024.330.139 (2024-03-30) 133 | 134 | - 2.0.8 135 | 136 | ## Version 2024.329.2014 (2024-03-29) 137 | 138 | - cargo_auto_lib = 2.0.6 139 | 140 | ## Version 2024.328.526 (2024-03-28) 141 | 142 | - github_owner 143 | 144 | - description_and_topics_to_github 145 | 146 | ## Version 2024.328.429 (2024-03-28) 147 | 148 | - printf, lib 149 | 150 | ## Version 2024.307.2040 (2024-03-07) 151 | 152 | - 2.0.4 153 | 154 | ## Version 2024.307.1516 (2024-03-07) 155 | 156 | - readme 157 | 158 | ## Version 2024.307.36 (2024-03-07) 159 | 160 | - docs 161 | 162 | ## Version 2024.307.14 (2024-03-07) 163 | 164 | - lib and bin for doc 165 | 166 | ## Version 2024.302.2122 (2024-03-02) 167 | 168 | - auto_md_to_doc_comments include 169 | 170 | ## Version 2024.302.2056 (2024-03-02) 171 | 172 | - 1.4.4 173 | 174 | ## Version 2024.229.2136 (2024-02-29) 175 | 176 | - 1.3.63 177 | 178 | ## Version 2024.229.335 (2024-02-29) 179 | 180 | - 1.3.40 181 | 182 | ## Version 2024.229.210 (2024-02-29) 183 | 184 | - run_shell_command_success 185 | 186 | ## Version 2024.225.1915 (2024-02-25) 187 | 188 | - description_and_topics_to_github 189 | - cargo_auto_lib = 1.3.33 190 | 191 | ## Version 2024.223.1536 (2024-02-23) 192 | 193 | - cargo_auto_lib = 1.3.6 194 | 195 | ## Version 2024.223.1510 (2024-02-23) 196 | 197 | - bestia_dev_ssh_1 198 | 199 | ## Version 2024.222.2136 (2024-02-22) 200 | 201 | - cargo_auto_lib = 1.3.4 202 | - task_publish_to_web 203 | 204 | ## Version 2024.221.2339 (2024-02-21) 205 | 206 | - update DEVELOPMENT.md 207 | 208 | ## Version 2024.220.2216 (2024-02-20) 209 | 210 | - update templates to 1.2.13 211 | 212 | ## Version 2024.220.2134 (2024-02-20) 213 | 214 | - cargo_auto_lib 1.2.4 215 | - copy_folder_files_into_module 216 | 217 | ## Version v2024.220.752 218 | 219 | cargo_auto_lib = 1.2.13 220 | automation task commit can now init local git repository and create new remote GitHub repo 221 | 222 | ## Version v2024.219.258 223 | 224 | cargo_auto_lib = 1.1.32 225 | 226 | ## Version v2024.219.243 227 | 228 | update templates wasm 229 | pretty json 230 | 231 | ## Version v2024.218.2136 232 | 233 | cargo_auto_lib = 1.1.23 234 | added tidy HTML function to make pretty docs HTML 235 | 236 | ## Version v2024.204.206 237 | 238 | updated dependency to cargo_auto_lib to 1.0.96 239 | use filehash instead of filedate to check if automation_tasks_rs must be recompiled 240 | add automation_tasks_rs/file_hashes.json 241 | 242 | ## Version v2024.203.327 243 | 244 | update cargo_auto_lib to "1.0.78" 245 | 246 | ## Version v2023.603.904 247 | 248 | curly brackets 249 | 250 | ## Version v2023.601.1218 251 | 252 | updated new pwa wasm 253 | 254 | ## Version v2023.601.646 255 | 256 | added new_release to automation 257 | 258 | ## Version v2023.530.1223 259 | 260 | added relatively simple, yet fully functional template for pwa_wasm project 261 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_semver_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_semver_mod 2 | 3 | //! Semver utilities. 4 | 5 | use crate::cargo_auto_lib::public_api_mod::{GREEN, RED, RESET, YELLOW}; 6 | use crate::cargo_auto_lib::{ 7 | error_mod::{Error, Result}, 8 | utils_mod::*, 9 | }; 10 | use crate::utils_mod::{pos, ResultLogError}; 11 | 12 | /// Enum for version parts: Minor or Patch 13 | enum VersionPart { 14 | Patch, 15 | Minor, 16 | } 17 | 18 | /// Increment the patch version in Cargo.toml file only if files are changed. 19 | pub fn auto_semver_increment_patch() -> Result<()> { 20 | increment_part(VersionPart::Patch, false) 21 | } 22 | 23 | /// Increment the patch version in Cargo.toml file even if files are not changed. 24 | pub fn auto_semver_increment_patch_forced() -> Result<()> { 25 | increment_part(VersionPart::Patch, true) 26 | } 27 | 28 | /// Increment the minor version in Cargo.toml file only if files are changed. 29 | pub fn auto_semver_increment_minor() -> Result<()> { 30 | increment_part(VersionPart::Minor, false) 31 | } 32 | 33 | /// Increment the minor version in Cargo.toml file even if files are not changed. 34 | pub fn auto_semver_increment_minor_forced() -> Result<()> { 35 | increment_part(VersionPart::Minor, true) 36 | } 37 | 38 | /// Increment a part of version in Cargo.toml file even if files are not changed or forced 39 | fn increment_part(part: VersionPart, force_version: bool) -> Result<()> { 40 | let mut vec_of_metadata = crate::cargo_auto_lib::auto_version_from_date_mod::read_file_metadata().log(pos!())?; 41 | let is_files_equal = if force_version { 42 | false 43 | } else { 44 | let js_struct = 45 | crate::cargo_auto_lib::auto_version_from_date_mod::read_json_file(".automation_tasks_rs_file_hashes.json").log(pos!())?; 46 | crate::cargo_auto_lib::auto_version_from_date_mod::are_files_equal(&vec_of_metadata, &js_struct.vec_file_metadata) 47 | }; 48 | 49 | if !is_files_equal { 50 | // println!("pub fn increment_patch"); 51 | let cargo_toml_filename = "Cargo.toml"; 52 | let cargo_toml_text = std::fs::read_to_string(cargo_toml_filename).log(pos!())?; 53 | 54 | // check if file have CRLF instead of LF and show error 55 | if cargo_toml_text.contains("\r\n") { 56 | return Err(Error::ErrorFromString(format!( 57 | "{RED}Error: {cargo_toml_filename} has CRLF line endings instead of LF. Correct the file! {RESET}" 58 | ))); 59 | } 60 | 61 | // find the line with "version = " including the start quote 62 | if let Ok(pos_start_data) = find_pos_start_data_after_delimiter(&cargo_toml_text, 0, r#"version = ""#) { 63 | // find the end quote 64 | if let Ok(pos_end_data) = find_pos_end_data_before_delimiter(&cargo_toml_text, pos_start_data, r#"""#) { 65 | let version = cargo_toml_text[pos_start_data..pos_end_data].to_string(); 66 | println!(r#" {YELLOW}old version: "{version}"{RESET}"#); 67 | //increment the last number 68 | let pos = pos_start_data; 69 | let (major, pos) = parse_next_number(&cargo_toml_text, pos).log(pos!())?; 70 | //jump over dot 71 | let pos = pos + 1; 72 | let (mut minor, pos) = parse_next_number(&cargo_toml_text, pos).log(pos!())?; 73 | //jump over dot 74 | let pos = pos + 1; 75 | let (mut patch, pos) = parse_next_number(&cargo_toml_text, pos).log(pos!())?; 76 | let pos_at_the_end_of_semver = pos; 77 | // increment 78 | match part { 79 | VersionPart::Patch => { 80 | patch += 1; 81 | } 82 | VersionPart::Minor => { 83 | minor += 1; 84 | patch = 0; 85 | } 86 | } 87 | // println!(r#"major: {},minor: {}, patch: {}"#, major, minor, patch); 88 | let new_semver = format!("{}.{}.{}", major, minor, patch); 89 | println!("{GREEN}new version: '{}'{RESET}", &new_semver); 90 | let new_cargo_toml_text = format!( 91 | "{}{}{}", 92 | &cargo_toml_text[..pos_start_data], 93 | &new_semver, 94 | &cargo_toml_text[pos_at_the_end_of_semver..] 95 | ); 96 | //save the file 97 | let _x = std::fs::write(cargo_toml_filename, new_cargo_toml_text); 98 | 99 | //the Cargo.toml is now different 100 | crate::cargo_auto_lib::auto_version_from_date_mod::correct_file_metadata_for_cargo_tom_inside_vec(&mut vec_of_metadata) 101 | .log(pos!())?; 102 | crate::cargo_auto_lib::auto_version_from_date_mod::save_json_file_for_file_meta_data(vec_of_metadata).log(pos!())?; 103 | } else { 104 | return Err(Error::ErrorFromString(format!("{RED}no end quote for version{RESET}"))); 105 | } 106 | } else { 107 | return Err(Error::ErrorFromString(format!("{RED}Cargo.toml has no version{RESET}"))); 108 | } 109 | } 110 | Ok(()) 111 | } 112 | 113 | /// Parse next number in version 114 | fn parse_next_number(cargo_toml_text: &str, pos: usize) -> Result<(usize, usize)> { 115 | let mut pos = pos; 116 | let mut number = "".to_string(); 117 | let mut one_char = cargo_toml_text[pos..pos + 1] 118 | .chars() 119 | .next() 120 | .ok_or(Error::ErrorFromStr("error chars().next()")) 121 | .log(pos!())?; 122 | while one_char.is_numeric() { 123 | number.push(one_char); 124 | pos += 1; 125 | one_char = cargo_toml_text[pos..pos + 1] 126 | .chars() 127 | .next() 128 | .ok_or(Error::ErrorFromStr("error chars().next()")) 129 | .log(pos!())?; 130 | } 131 | let number: usize = number.parse().log(pos!())?; 132 | //return 133 | Ok((number, pos)) 134 | } 135 | -------------------------------------------------------------------------------- /automation_tasks_rs/src/cargo_auto_lib/auto_cargo_toml_to_md_mod.rs: -------------------------------------------------------------------------------- 1 | // auto_cargo_toml_to_md_mod 2 | 3 | //! Includes data from Cargo.toml to `md` files: version, authors, description,... 4 | //! ![auto_cargo_toml_to_md.png](https://github.com/automation-tasks-rs/cargo_auto_lib/blob/main/images/auto_cargo_toml_to_md.png?raw=true) 5 | //! Read more in the auto_cargo_toml_to_md() function. 6 | 7 | // region: use statements 8 | 9 | use crate::cargo_auto_lib::error_mod::{Error, Result}; 10 | use crate::cargo_auto_lib::public_api_mod::{GREEN, RED, RESET, YELLOW}; 11 | use crate::utils_mod::{pos, ResultLogError}; 12 | use glob::glob; 13 | use lazy_static::lazy_static; 14 | use regex::*; 15 | 16 | // this trait must be in scope to use these methods of CargoToml 17 | use crate::cargo_auto_lib::public_api_mod::CargoTomlPublicApiMethods; 18 | 19 | // endregion: use statements 20 | 21 | lazy_static! { 22 | /// Regex for start marker 23 | static ref REGEX_MD_START: Regex = Regex::new(r#"(?m)^\[//\]: # \(auto_cargo_toml_to_md start\)$"#).expect("regex new"); 24 | /// Regex for end marker 25 | static ref REGEX_MD_END: Regex = Regex::new(r#"(?m)^\[//\]: # \(auto_cargo_toml_to_md end\)$"#).expect("regex new"); 26 | } 27 | 28 | // region: auto_md_to_doc_comments include doc_comments/auto_cargo_toml_to_md.md A /// 29 | /// This function includes data from Cargo.toml to markdown files. 30 | /// 31 | /// ![auto_cargo_toml_to_md.png](https://github.com/automation-tasks-rs/cargo_auto_lib/blob/main/images/auto_cargo_toml_to_md.png?raw=true) 32 | /// 33 | /// This is nice for avoiding out of sync data. 34 | /// Run it on every build with `automation_tasks_rs` and [cargo auto](https://crates.io/crates/cargo-auto). 35 | /// 36 | /// In the md file write these markers in invisible markdown comments. 37 | /// 38 | /// ```markdown 39 | /// [//comment]: # (auto_cargo_toml_to_md start) 40 | /// 41 | /// [//comment]: # (auto_cargo_toml_to_md end) 42 | /// 43 | /// ``` 44 | /// 45 | /// In this instructions I changed `[//]` to `[//comment]` to not process these markers. 46 | /// 47 | /// `auto_cargo_toml_to_md` deletes the old lines between the markers and includes the Cargo.toml data: 48 | /// description, repository, version, utc_now, authors and creates badges for keywords and categories. 49 | /// 50 | /// The words topics, keywords, hashtags and tags all mean the same concept. 51 | /// In cargo.toml we have keywords. 52 | /// In README.md I want to have badges with different color. And hashtags for SEO. 53 | /// In GitHub they are topics. 54 | /// 55 | /// Some keywords have defined colors, others are orange like Rust. 56 | /// This can be expanded in the future. 57 | /// 58 | /// - Yellow: work-in-progress 59 | /// - Green: maintained, ready-for-use 60 | /// - Red: obsolete, archived 61 | /// 62 | // endregion: auto_md_to_doc_comments include doc_comments/auto_cargo_toml_to_md.md A /// 63 | pub fn auto_cargo_toml_to_md() -> Result<()> { 64 | let cargo_toml = crate::cargo_auto_lib::auto_cargo_toml_mod::CargoToml::read().log(pos!())?; 65 | let version = cargo_toml.package_version(); 66 | let author_name = cargo_toml.package_author_name(); 67 | let homepage = cargo_toml.package_homepage(); 68 | let repository = cargo_toml.package_repository().unwrap_or("".to_owned()); 69 | let description = cargo_toml.package_description().unwrap_or("".to_owned()); 70 | let keywords = cargo_toml.package_keywords().to_vec(); 71 | let now_utc_date_iso = &crate::cargo_auto_lib::utils_mod::now_utc_date_iso(); 72 | 73 | let mut new_text = format!("\n**{description}** \n"); 74 | new_text.push_str(&format!( 75 | "***version: {version} date: {now_utc_date_iso} author: [{author_name}]({homepage}) repository: [GitHub]({repository})***\n\n" 76 | )); 77 | 78 | for keyword in keywords.iter() { 79 | let color = if keyword == "work-in-progress" { 80 | "yellow" 81 | } else if keyword == "maintained" || keyword == "ready-for-use" { 82 | "green" 83 | } else if keyword == "obsolete" || keyword == "archived" { 84 | "red" 85 | } else { 86 | "orange" 87 | }; 88 | // inside the shield badge syntax, hyphens must be replaced by underscore 89 | let keyword_underscore = keyword.replace('-', "_"); 90 | new_text.push_str(&format!( 91 | " ![{keyword}](https://img.shields.io/badge/{keyword_underscore}-{color})\n" 92 | )); 93 | } 94 | new_text.push('\n'); 95 | 96 | for filename_result in glob("*.md").log(pos!())? { 97 | let filename_pathbuff = filename_result?; 98 | let md_filename = filename_pathbuff 99 | .to_str() 100 | .ok_or_else(|| Error::ErrorFromStr("filename_pathbuff is None")) 101 | .log(pos!())?; 102 | // println!("checking md_filename: {}", &md_filename); 103 | let mut md_text_content = std::fs::read_to_string(md_filename).log(pos!())?; 104 | 105 | // check if file have CRLF and show error 106 | if md_text_content.contains("\r\n") { 107 | return Err(Error::ErrorFromString(format!( 108 | "{RED}Error: {md_filename} has CRLF line endings instead of LF. Correct the file! {RESET}" 109 | ))); 110 | } 111 | 112 | if let Some(cap) = REGEX_MD_START.captures(&md_text_content) { 113 | let pos_start = cap 114 | .get(0) 115 | .ok_or_else(|| Error::ErrorFromStr("cap get 0 is None")) 116 | .log(pos!())? 117 | .end() 118 | + 1; 119 | if let Some(cap) = REGEX_MD_END.captures(&md_text_content) { 120 | let pos_end = cap 121 | .get(0) 122 | .ok_or_else(|| Error::ErrorFromStr("cap get 0 is None")) 123 | .log(pos!())? 124 | .start(); 125 | md_text_content.replace_range(pos_start..pos_end, &new_text); 126 | println!(" {YELLOW}Write to md file: {}{RESET}", md_filename); 127 | println!("{GREEN}{}{RESET}", &new_text.trim_end_matches("\n\n")); 128 | std::fs::write(md_filename, md_text_content).log(pos!())?; 129 | } 130 | } 131 | } 132 | Ok(()) 133 | } 134 | -------------------------------------------------------------------------------- /src/template_new_wasm_mod.rs: -------------------------------------------------------------------------------- 1 | // template_new_wasm_mod.rs 2 | 3 | //! Template for new_wasm. 4 | //! 5 | //! The template is downloaded from github: 6 | //! 7 | 8 | use crate::{pos, ResultLogError, GREEN, RED, RESET, YELLOW}; 9 | 10 | /// Creates a new Rust project from template. 11 | pub fn new_wasm( 12 | rust_project_name: Option, 13 | github_owner_or_organization: Option, 14 | web_server_domain: Option, 15 | server_username: Option, 16 | ) -> anyhow::Result<()> { 17 | if rust_project_name.is_none() { 18 | println!("{RED}Error: Project name argument is missing: `cargo auto new_wasm project_name github_owner_or_organization web_server server_username`{RESET}"); 19 | return Ok(()); 20 | } 21 | if github_owner_or_organization.is_none() { 22 | println!("{RED}Error: github_owner or Organization argument is missing: `cargo auto new_wasm project_name github_owner_or_organization web_server server_username`{RESET}"); 23 | return Ok(()); 24 | } 25 | if web_server_domain.is_none() { 26 | println!("{RED}Error: Web server argument is missing: `cargo auto new_wasm project_name github_owner_or_organization web_server server_username`{RESET}"); 27 | return Ok(()); 28 | } 29 | if server_username.is_none() { 30 | println!("{RED}Error: Server username argument is missing: `cargo auto new_wasm project_name github_owner_or_organization web_server server_username`{RESET}"); 31 | return Ok(()); 32 | } 33 | use anyhow::Context; 34 | let rust_project_name = rust_project_name.context("rust_project_name is None").log(pos!())?; 35 | let github_owner_or_organization = github_owner_or_organization 36 | .context("github_owner_or_organization is None") 37 | .log(pos!())?; 38 | let web_server_domain = web_server_domain.context("web_server_domain is None").log(pos!())?; 39 | let server_username = server_username.context("server_username is None").log(pos!())?; 40 | 41 | copy_to_files( 42 | &rust_project_name, 43 | &github_owner_or_organization, 44 | &web_server_domain, 45 | &server_username, 46 | ) 47 | .log(pos!())?; 48 | 49 | println!(); 50 | println!(" {YELLOW}The command `cargo auto new_wasm` generated the directory `{rust_project_name}`{RESET}"); 51 | println!(" {YELLOW}You can open this new Rust project `{rust_project_name}` in a new Rust editor.{RESET}",); 52 | println!(" {YELLOW}For example VSCode:{RESET}"); 53 | println!("{GREEN}code {rust_project_name}{RESET}"); 54 | println!(" {YELLOW}Then build with:{RESET}"); 55 | println!("{GREEN}cargo auto build{RESET}"); 56 | println!(" {YELLOW}and follow the detailed instructions.{RESET}"); 57 | Ok(()) 58 | } 59 | 60 | /// Copy the Rust project into a compressed file. 61 | fn copy_to_files( 62 | rust_project_name: &str, 63 | github_owner_or_organization: &str, 64 | web_server_domain: &str, 65 | server_username: &str, 66 | ) -> anyhow::Result<()> { 67 | let folder_path = std::path::Path::new(rust_project_name); 68 | if folder_path.exists() { 69 | anyhow::bail!("{RED}Error: Folder {rust_project_name} already exists! {RESET}"); 70 | } 71 | std::fs::create_dir_all(folder_path).log(pos!())?; 72 | 73 | // download latest template.tar.gz 74 | println!(" {YELLOW}Downloading template.tar.gz...{RESET}"); 75 | let file_name = "template.tar.gz"; 76 | let path = "./template.tar.gz"; 77 | let url = "https://github.com/automation-tasks-rs/cargo_auto_template_new_wasm/releases/latest/download/template.tar.gz"; 78 | let reqwest_client = reqwest::blocking::Client::new(); 79 | let http_response = reqwest_client.get(url).send(); 80 | if let Ok(body) = http_response { 81 | let body = body.bytes().log(pos!())?; 82 | // Get the content of the response 83 | std::fs::write(path, &body) 84 | .or_else(|err| anyhow::bail!("Download failed for {file_name} {err}")) 85 | .log(pos!())?; 86 | } else { 87 | anyhow::bail!("Error while retrieving data: {:#?}", http_response.err()); 88 | } 89 | 90 | // decompress into folder_path 91 | let tar_gz = std::fs::File::open(path).log(pos!())?; 92 | let tar = flate2::read::GzDecoder::new(tar_gz); 93 | let mut archive = tar::Archive::new(tar); 94 | archive.unpack(folder_path).log(pos!())?; 95 | std::fs::remove_file(path).log(pos!())?; 96 | 97 | // replace placeholders inside text files 98 | for entry in walkdir::WalkDir::new(folder_path).into_iter().filter_map(Result::ok) { 99 | if entry.file_type().is_file() { 100 | // template has only valid utf8 files 101 | println!("replace: {}", entry.path().to_string_lossy()); 102 | let content = std::fs::read_to_string(entry.path()).log(pos!())?; 103 | let content = content.replace("cargo_auto_template_new_wasm", rust_project_name); 104 | let content = content.replace(&"cargo_auto_template_new_wasm".to_uppercase(), &rust_project_name.to_uppercase()); 105 | let content = content.replace("automation-tasks-rs", github_owner_or_organization); 106 | let content = content.replace("automation--tasks--rs", "automation-tasks-rs"); 107 | let content = content.replace("web_server_domain", web_server_domain); 108 | let content = content.replace("server_username", server_username); 109 | std::fs::write(entry.path(), content).log(pos!())?; 110 | } 111 | } 112 | // renaming files is tricky and must be traverse in reverse. 113 | let mut traverse_reverse: Vec = walkdir::WalkDir::new(folder_path).into_iter().filter_map(Result::ok).collect(); 114 | traverse_reverse.reverse(); 115 | for entry in traverse_reverse.iter() { 116 | if entry.file_name().to_string_lossy().contains("cargo_auto_template_new_wasm") { 117 | println!("rename: {}", entry.path().to_string_lossy()); 118 | std::fs::rename( 119 | entry.path(), 120 | entry 121 | .path() 122 | .to_string_lossy() 123 | .replace("cargo_auto_template_new_wasm", rust_project_name), 124 | ) 125 | .log(pos!())?; 126 | } 127 | } 128 | Ok(()) 129 | } 130 | -------------------------------------------------------------------------------- /docs/static.files/storage-68b7e25d.js: -------------------------------------------------------------------------------- 1 | "use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=(function(){const currentTheme=document.getElementById("themeStyle");return currentTheme instanceof HTMLLinkElement?currentTheme:null;})();const settingsDataset=(function(){const settingsElement=document.getElementById("default-settings");return settingsElement&&settingsElement.dataset?settingsElement.dataset:null;})();function nonnull(x,msg){if(x===null){throw(msg||"unexpected null value!");}else{return x;}}function nonundef(x,msg){if(x===undefined){throw(msg||"unexpected null value!");}else{return x;}}function getSettingValue(settingName){const current=getCurrentValue(settingName);if(current===null&&settingsDataset!==null){const def=settingsDataset[settingName.replace(/-/g,"_")];if(def!==undefined){return def;}}return current;}const localStoredTheme=getSettingValue("theme");function hasClass(elem,className){return!!elem&&!!elem.classList&&elem.classList.contains(className);}function addClass(elem,className){if(elem&&elem.classList){elem.classList.add(className);}}function removeClass(elem,className){if(elem&&elem.classList){elem.classList.remove(className);}}function onEach(arr,func){for(const elem of arr){if(func(elem)){return true;}}return false;}function onEachLazy(lazyArray,func){return onEach(Array.prototype.slice.call(lazyArray),func);}function updateLocalStorage(name,value){try{if(value===null){window.localStorage.removeItem("rustdoc-"+name);}else{window.localStorage.setItem("rustdoc-"+name,value);}}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name);}catch(e){return null;}}function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.getAttribute("data-"+name):null;}function switchTheme(newThemeName,saveTheme){const themeNames=(getVar("themes")||"").split(",").filter(t=>t);themeNames.push(...builtinThemes);if(newThemeName===null||themeNames.indexOf(newThemeName)===-1){return;}if(saveTheme){updateLocalStorage("theme",newThemeName);}document.documentElement.setAttribute("data-theme",newThemeName);if(builtinThemes.indexOf(newThemeName)!==-1){if(window.currentTheme&&window.currentTheme.parentNode){window.currentTheme.parentNode.removeChild(window.currentTheme);window.currentTheme=null;}}else{const newHref=getVar("root-path")+encodeURIComponent(newThemeName)+getVar("resource-suffix")+".css";if(!window.currentTheme){if(document.readyState==="loading"){document.write(``);window.currentTheme=(function(){const currentTheme=document.getElementById("themeStyle");return currentTheme instanceof HTMLLinkElement?currentTheme:null;})();}else{window.currentTheme=document.createElement("link");window.currentTheme.rel="stylesheet";window.currentTheme.id="themeStyle";window.currentTheme.href=newHref;document.documentElement.appendChild(window.currentTheme);}}else if(newHref!==window.currentTheme.href){window.currentTheme.href=newHref;}}}const updateTheme=(function(){const mql=window.matchMedia("(prefers-color-scheme: dark)");function updateTheme(){if(getSettingValue("use-system-theme")!=="false"){const lightTheme=getSettingValue("preferred-light-theme")||"light";const darkTheme=getSettingValue("preferred-dark-theme")||"dark";updateLocalStorage("use-system-theme","true");switchTheme(mql.matches?darkTheme:lightTheme,true);}else{switchTheme(getSettingValue("theme"),false);}}mql.addEventListener("change",updateTheme);return updateTheme;})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&localStoredTheme!==null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("preferred-dark-theme",localStoredTheme);}}updateTheme();if(getSettingValue("source-sidebar-show")==="true"){addClass(document.documentElement,"src-sidebar-expanded");}if(getSettingValue("hide-sidebar")==="true"){addClass(document.documentElement,"hide-sidebar");}if(getSettingValue("hide-toc")==="true"){addClass(document.documentElement,"hide-toc");}if(getSettingValue("hide-modnav")==="true"){addClass(document.documentElement,"hide-modnav");}if(getSettingValue("sans-serif-fonts")==="true"){addClass(document.documentElement,"sans-serif");}if(getSettingValue("word-wrap-source-code")==="true"){addClass(document.documentElement,"word-wrap-source-code");}function updateSidebarWidth(){const desktopSidebarWidth=getSettingValue("desktop-sidebar-width");if(desktopSidebarWidth&&desktopSidebarWidth!=="null"){document.documentElement.style.setProperty("--desktop-sidebar-width",desktopSidebarWidth+"px",);}const srcSidebarWidth=getSettingValue("src-sidebar-width");if(srcSidebarWidth&&srcSidebarWidth!=="null"){document.documentElement.style.setProperty("--src-sidebar-width",srcSidebarWidth+"px",);}}updateSidebarWidth();window.addEventListener("pageshow",ev=>{if(ev.persisted){setTimeout(updateTheme,0);setTimeout(updateSidebarWidth,0);}});class RustdocSearchElement extends HTMLElement{constructor(){super();}connectedCallback(){const rootPath=getVar("root-path");const currentCrate=getVar("current-crate");this.innerHTML=``;}}window.customElements.define("rustdoc-search",RustdocSearchElement);class RustdocToolbarElement extends HTMLElement{constructor(){super();}connectedCallback(){if(this.firstElementChild){return;}const rootPath=getVar("root-path");this.innerHTML=` 17 |
18 | Settings 19 |
20 |
21 | Help 22 |
23 | `;}}window.customElements.define("rustdoc-toolbar",RustdocToolbarElement); --------------------------------------------------------------------------------