From 211438a4e85a8136bbc34334a1727080d84e2d4c Mon Sep 17 00:00:00 2001 From: Dexoron Date: Tue, 17 Feb 2026 18:15:08 +0800 Subject: [PATCH] Rewrite project from Python to Rust and add Windows/MacOS supports --- .github/workflows/ci.yml | 28 + .github/workflows/release.yml | 58 ++ .gitignore | 14 +- .python-version | 1 - CHANGELOG.md | 16 + CONTRIBUTING.md | 44 +- Cargo.lock | 1670 +++++++++++++++++++++++++++++++++ Cargo.toml | 10 + README.md | 92 +- build.rs | 4 + check.sh | 15 - dcr | 10 - install.ps1 | 113 +++ install.sh | 174 ++-- pyproject.toml | 11 - src/__init__.py | 0 src/cli/build.rs | 82 ++ src/cli/clean.rs | 72 ++ src/cli/flag_update.rs | 161 ++++ src/cli/help.rs | 36 + src/cli/init.rs | 96 ++ src/cli/mod.rs | 7 + src/cli/new.rs | 123 +++ src/cli/run.rs | 55 ++ src/config.rs | 43 + src/dcr/__init__.py | 0 src/dcr/cli/__init__.py | 18 - src/dcr/cli/build.py | 70 -- src/dcr/cli/clean.py | 53 -- src/dcr/cli/flag_update.py | 23 - src/dcr/cli/help.py | 39 - src/dcr/cli/init.py | 61 -- src/dcr/cli/new.py | 89 -- src/dcr/cli/run.py | 37 - src/dcr/config.py | 43 - src/dcr/main.py | 38 - src/dcr/utils/__init__.py | 0 src/dcr/utils/fs.py | 8 - src/dcr/utils/text.py | 40 - src/main.rs | 34 + src/test/__init__.py | 0 src/utils/fs.rs | 17 + src/utils/log.rs | 30 + src/utils/mod.rs | 3 + src/utils/text.rs | 16 + 45 files changed, 2828 insertions(+), 726 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml delete mode 100644 .python-version create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs delete mode 100755 check.sh delete mode 100755 dcr create mode 100644 install.ps1 delete mode 100644 pyproject.toml delete mode 100644 src/__init__.py create mode 100644 src/cli/build.rs create mode 100644 src/cli/clean.rs create mode 100644 src/cli/flag_update.rs create mode 100644 src/cli/help.rs create mode 100644 src/cli/init.rs create mode 100644 src/cli/mod.rs create mode 100644 src/cli/new.rs create mode 100644 src/cli/run.rs create mode 100644 src/config.rs delete mode 100644 src/dcr/__init__.py delete mode 100644 src/dcr/cli/__init__.py delete mode 100644 src/dcr/cli/build.py delete mode 100644 src/dcr/cli/clean.py delete mode 100644 src/dcr/cli/flag_update.py delete mode 100644 src/dcr/cli/help.py delete mode 100644 src/dcr/cli/init.py delete mode 100644 src/dcr/cli/new.py delete mode 100644 src/dcr/cli/run.py delete mode 100644 src/dcr/config.py delete mode 100644 src/dcr/main.py delete mode 100644 src/dcr/utils/__init__.py delete mode 100644 src/dcr/utils/fs.py delete mode 100644 src/dcr/utils/text.py create mode 100644 src/main.rs delete mode 100644 src/test/__init__.py create mode 100644 src/utils/fs.rs create mode 100644 src/utils/log.rs create mode 100644 src/utils/mod.rs create mode 100644 src/utils/text.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f7c5269 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,28 @@ +name: CI + +on: + push: + branches: + - master + - main + pull_request: + +jobs: + checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Format + run: cargo fmt --all --check + + - name: Lint + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Check + run: cargo check --all-targets --all-features diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..55cba1a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,58 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + asset_name: dcr-x86_64-unknown-linux-gnu + - os: macos-13 + target: x86_64-apple-darwin + asset_name: dcr-x86_64-apple-darwin + - os: macos-14 + target: aarch64-apple-darwin + asset_name: dcr-aarch64-apple-darwin + - os: windows-latest + target: x86_64-pc-windows-msvc + asset_name: dcr-x86_64-pc-windows-msvc.exe + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Build + run: cargo build --release --target ${{ matrix.target }} + + - name: Prepare Artifact (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + cp target/${{ matrix.target }}/release/dcr ${{ matrix.asset_name }} + + - name: Prepare Artifact (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + Copy-Item "target/${{ matrix.target }}/release/dcr.exe" "${{ matrix.asset_name }}" + + - name: Upload Release Asset + uses: softprops/action-gh-release@v2 + with: + files: ${{ matrix.asset_name }} diff --git a/.gitignore b/.gitignore index c9098eb..be6d93f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,5 @@ -__pycache__/ -build/ -dist/ -wheels/ -*.egg-info -.venv -sandbox/* -.ruff_cache/ -.vscode/ \ No newline at end of file +target/ +sandbox/** +.idea/ +.vscode/ +.env \ No newline at end of file diff --git a/.python-version b/.python-version deleted file mode 100644 index 95ed564..0000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.14.2 diff --git a/CHANGELOG.md b/CHANGELOG.md index f5eb55b..354c680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## [0.2.0] - 2026-02-17 +Изменено: +- Проект переведен с Python на Rust +- CLI и команды (`new`, `init`, `build`, `run`, `clean`, `--help`, `--version`, `--update`) перенесены на Rust +- Обновлен флаг `--update` - добавлена поддержка GNU\Linux, Windows, MacOS +- Обновлены `README.md`, `CONTRIBUTING.md`, `install.sh` под Rust-реализацию +- Добавлен `install.ps1` для Windows +- Обновлен `install.sh` для GNU/Linux и MacOS + +Добавлено: +- Добавлен `install.ps1` для Windows +- Поддержка GNU\Linux, Windows, MacOS(x86_64/arm) + +ВАЖНО: +- Код перенесен с помощью нейронных сетей, в будущих версиях будет баг-фикс и изменение логики + ## [0.1.2] - 2026-02-12 Добавлено: - Команда обновления diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2c74346..96a46df 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,41 +4,43 @@ ## Требования -- Обязательно использовать форматирование `ruff`. -- Обязательно использовать аннотации типов и проверять их через `ty`. +- Использовать Rust stable. +- Перед PR код должен проходить форматирование, линт и сборку. -## Быстрый старт(GNU/Linux) +## Быстрый старт -Создайте линковку ./PATH_TO_PROJECT/dcr и ~/.local/bin/dcr -После этого, вы можете использовать команду dcr где угодно +```bash +git clone https://github.com/dexoron/dcr.git +cd dcr +cargo check +``` ## Стиль кода -- Форматируйте код через `ruff`. +- Форматируйте код через `cargo fmt`. +- Проверяйте предупреждения через `cargo clippy`. - Держите функции небольшими и сфокусированными. - Предпочитайте понятные имена вместо "хитрых". -## Типизация - -- Добавляйте аннотации типов для нового и изменяемого кода. -- Запускайте `ty` для проверки корректности типов. - ## Проверки -Для запуска всех проверок используйте скрипт проекта: +Перед PR запускайте: ```bash -./check.sh +cargo fmt --all +cargo clippy --all-targets --all-features -- -D warnings +cargo check ``` -> Скрипт можно запустить из любой папки, указав путь до него: `../check.sh` (или больше `../`, если нужно). +## CI/CD и релизы -## Запуск инструментов напрямую - -```bash -ruff check src -uvx ty check src -``` +- CI: `.github/workflows/ci.yml` +- Release: `.github/workflows/release.yml` +- Релиз запускается по тегу `v*` и публикует бинарники для: +- `x86_64-unknown-linux-gnu` +- `x86_64-apple-darwin` +- `aarch64-apple-darwin` +- `x86_64-pc-windows-msvc` ## Коммиты и PR @@ -48,7 +50,7 @@ uvx ty check src ## Вопросы -Если что-то непонятно, откройте issue или задайте вопрос в PR. +Если что-то непонятно, открой issue или задайте вопрос в PR. Также можно задать вопрос напрямую: - TG: @dexoron diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..407edff --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1670 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "dcr" +version = "2.0.0" +dependencies = [ + "reqwest", + "self-replace", + "serde", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "self-replace" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ec815b5eab420ab893f63393878d89c90fdd94c0bcc44c07abb8ad95552fb7" +dependencies = [ + "fastrand", + "tempfile", + "windows-sys 0.52.0", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +dependencies = [ + "fastrand", + "getrandom 0.4.1", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c6282b4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "dcr" +version = "2.0.0" +edition = "2024" +build = "build.rs" + +[dependencies] +reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] } +serde = { version = "1", features = ["derive"] } +self-replace = "1" diff --git a/README.md b/README.md index 4dfd905..145ca82 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # DCR (Dexoron Cargo Realization) -DCR — это утилита для управления проектами на C. Она задает единый формат проекта, дает команды для создания, сборки и запуска, и использует один конфигурационный файл `dcr.toml`. +DCR — это утилита для управления проектами на C/C++ в стиле Cargo. -Проект находится на стадии MVP. Текущая реализация написана на Python и в будущем будет переписана. +Текущая реализация написана на Rust. ## Зачем DCR - Единая структура проекта без ручной настройки @@ -15,25 +15,50 @@ DCR — это утилита для управления проектами н - Запуск собранного бинарника - Очистка результатов сборки - Генерация минимального шаблона C-проекта +- Обновление бинарника через `dcr --update` (GitHub Releases) + +## Поддерживаемые платформы +- Linux: `x86_64-unknown-linux-gnu` +- macOS Intel: `x86_64-apple-darwin` +- macOS Apple Silicon: `aarch64-apple-darwin` +- Windows: `x86_64-pc-windows-msvc` ## Установка -### Автоматическая установка(Рекомендуется) - -**GNU/Linux** -```sh -curl -L dcr.zov.tatar | bash -``` - -### Самостоятельная установка +### Из исходников ```sh -git clone https://github.com/dexoron/dcr.git "~/.local/share/dcr" -chmod +x "~/.local/share/dcr/dcr" -ln -sf ~/.local/share/dcr/dcr ~/.local/bin +git clone https://github.com/dexoron/dcr.git +cd dcr +cargo build --release +mkdir -p ~/.local/bin +ln -sf "$PWD/target/release/dcr" ~/.local/bin/dcr ``` +### Через install.sh (Linux/macOS) + +```sh +curl -fsSL https://raw.githubusercontent.com/dexoron/dcr/master/install.sh | bash +``` + +### Через install.ps1 (Windows) + +```powershell +irm https://raw.githubusercontent.com/dexoron/dcr/master/install.ps1 | iex +``` + +Оба скрипта при запуске спрашивают: +- скачать готовый бинарник из GitHub Release +- или собрать проект из `git` + +Релизные ассеты: +- `dcr-x86_64-unknown-linux-gnu` +- `dcr-x86_64-apple-darwin` +- `dcr-aarch64-apple-darwin` +- `dcr-x86_64-pc-windows-msvc.exe` + ## Быстрый старт + Создать новый проект: `dcr new hello` @@ -41,7 +66,8 @@ ln -sf ~/.local/share/dcr/dcr ~/.local/bin `dcr init` Структура проекта: -``` + +```txt hello/ - src/ - - main.c @@ -49,8 +75,7 @@ hello/ ``` Сборка и запуск проекта: -`dcr run` или `dcr run --release` - для сбрки с оптимизацией - +`dcr run` или `dcr run --release` ## Команды @@ -78,10 +103,11 @@ hello/ - `--release` — флаги: `-O3 -DNDEBUG -march=native` ## Конфигурация -Основной файл проекта — `dcr.toml`. Сейчас он создается автоматически и служит точкой входа для будущих настроек. +Основной файл проекта — `dcr.toml`. Пример `dcr.toml`: -``` + +```toml [package] name = "hello" version = "0.1.0" @@ -91,34 +117,12 @@ compiler = "clang" [dependencies] ``` -## Установка в PATH -Чтобы вызывать `dcr` из любой директории, можно сделать symlink в `~/.local/bin`: -```sh -ln -s "$PWD/dcr" ~/.local/bin/dcr -``` -Убедитесь, что `~/.local/bin` находится в `PATH`. - -## Пример проекта -`src/main.c` содержит минимальный `Hello, World!`: -```c -#include - -int main(void) { - puts("Hello, World!"); - return 0; -} -``` - ## Требования -- C-компилятор (например, `gcc` или `clang`) -- Python 3.14.2 (для текущей версии DCR) -- uv (менеджер проекта для Python) +- Rust toolchain (`rustc`, `cargo`) - Для ручной сборки DCR +- C-компилятор (`clang`, `gcc` или другие) -## Статус и планы -Проект в стадии MVP. Планируется переписывание на более подходящий язык и расширение функциональности: -- более гибкая конфигурация через `dcr.toml` -- расширяемые профили сборки -- менеджер пакетов +## Релизы +Релизы собираются автоматически через GitHub Actions (`.github/workflows/release.yml`) при пуше тега формата `v*`. ## Лицензия См. файл `LICENSE`. diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..5d9d14a --- /dev/null +++ b/build.rs @@ -0,0 +1,4 @@ +fn main() { + let target = std::env::var("TARGET").unwrap_or_else(|_| "unknown-target".to_string()); + println!("cargo:rustc-env=DCR_TARGET={target}"); +} diff --git a/check.sh b/check.sh deleted file mode 100755 index 7786dc5..0000000 --- a/check.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -BASE_DIR="src" - -echo "#============#" -echo "# Ruff #" -echo "#============#" -(cd "$SCRIPT_DIR" && ruff check "$BASE_DIR") -echo -echo -echo "#============#" -echo "# Ty #" -echo "#============#" -(cd "$SCRIPT_DIR" && uvx ty check "$BASE_DIR") diff --git a/dcr b/dcr deleted file mode 100755 index 11e1c7f..0000000 --- a/dcr +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -BASE_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" - -if command -v python3 >/dev/null 2>&1; then - PYTHON_BIN="python3" -else - PYTHON_BIN="$BASE_DIR/Python/bin/python3" -fi - -PYTHONPATH="$BASE_DIR/src" exec "$PYTHON_BIN" -m dcr.main "$@" diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..379a52f --- /dev/null +++ b/install.ps1 @@ -0,0 +1,113 @@ +$ErrorActionPreference = 'Stop' + +$RepoUrl = 'https://github.com/dexoron/dcr' +$ApiUrl = 'https://api.github.com/repos/dexoron/dcr/releases/latest' +$InstallPath = Join-Path $env:LOCALAPPDATA 'dcr' +$BinPath = Join-Path $env:USERPROFILE '.local\bin' +$BinaryPath = Join-Path $InstallPath 'dcr.exe' + +function Info($msg) { Write-Host "[INFO] $msg" -ForegroundColor Cyan } +function Ok($msg) { Write-Host "[OK] $msg" -ForegroundColor Green } +function Warn($msg) { Write-Host "[WARN] $msg" -ForegroundColor Yellow } +function Fail($msg) { Write-Host "[ERROR] $msg" -ForegroundColor Red; exit 1 } + +function Require-Cmd($name) { + if (-not (Get-Command $name -ErrorAction SilentlyContinue)) { + Fail "Не найдено: $name" + } +} + +function Select-Mode { + Write-Host "Выбери способ установки:" + Write-Host " 1) Скачать готовый бинарник из GitHub Release (рекомендуется)" + Write-Host " 2) Собрать из git" + $choice = Read-Host "Введите 1 или 2 [1]" + if ([string]::IsNullOrWhiteSpace($choice)) { $choice = '1' } + + switch ($choice) { + '1' { return 'release' } + '2' { return 'build' } + default { Fail 'Неизвестный вариант' } + } +} + +function Get-Target { + if ($env:PROCESSOR_ARCHITECTURE -ne 'AMD64') { + Fail "Поддерживается только x86_64 Windows, текущая архитектура: $env:PROCESSOR_ARCHITECTURE" + } + return 'x86_64-pc-windows-msvc' +} + +function Install-FromRelease { + param([string]$target) + + New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null + + $assetName = "dcr-$target.exe" + Info "Получение последнего релиза..." + $release = Invoke-RestMethod -Method Get -Uri $ApiUrl + + $asset = $release.assets | Where-Object { $_.name -eq $assetName } | Select-Object -First 1 + if (-not $asset) { + Fail "Не найден ассет $assetName в релизе $($release.tag_name)" + } + + Info "Скачивание $assetName..." + Invoke-WebRequest -Uri $asset.browser_download_url -OutFile $BinaryPath + Ok "Скачан бинарник $assetName" +} + +function Install-FromSource { + param([string]$target) + + Require-Cmd 'git' + Require-Cmd 'cargo' + + $tmp = Join-Path $env:TEMP 'dcr-install' + if (Test-Path $tmp) { Remove-Item -Recurse -Force $tmp } + + Info 'Клонирование репозитория...' + git clone --depth 1 $RepoUrl $tmp | Out-Null + + Info 'Сборка release-бинарника...' + Push-Location $tmp + cargo build --release --target $target + Pop-Location + + New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null + Copy-Item (Join-Path $tmp "target\$target\release\dcr.exe") $BinaryPath -Force + Ok 'Установлен бинарник из исходников' + + Remove-Item -Recurse -Force $tmp +} + +function Setup-Path { + New-Item -ItemType Directory -Path $BinPath -Force | Out-Null + $linkPath = Join-Path $BinPath 'dcr.exe' + Copy-Item $BinaryPath $linkPath -Force + + $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') + if (-not $userPath) { $userPath = '' } + + if (-not ($userPath -split ';' | Where-Object { $_ -eq $BinPath })) { + $newPath = if ($userPath) { "$userPath;$BinPath" } else { $BinPath } + [Environment]::SetEnvironmentVariable('Path', $newPath, 'User') + $env:Path = "$env:Path;$BinPath" + Warn "Добавлен путь в PATH: $BinPath" + } + + Ok "Команда dcr доступна через $linkPath" +} + +Info 'Запуск установки DCR' +$mode = Select-Mode +$target = Get-Target + +if ($mode -eq 'build') { + Install-FromSource -target $target +} else { + Install-FromRelease -target $target +} + +Setup-Path +Ok 'Установка завершена успешно' diff --git a/install.sh b/install.sh index ae9d003..50a18a6 100755 --- a/install.sh +++ b/install.sh @@ -1,14 +1,11 @@ #!/usr/bin/env bash set -Eeuo pipefail -# ===== CONFIG ===== -TMPDIR="/tmp/dcr-temp" +TMPDIR="/tmp/dcr-install" INSTALL_PATH="$HOME/.local/share/dcr" -PYTHON_DIR="$INSTALL_PATH/Python" BINPATH="$HOME/.local/bin" LOGFILE="$HOME/.cache/dcr-install.log" - -PYTHON_DOWNLOAD_URL="https://github.com/astral-sh/python-build-standalone/releases/download/20260203/cpython-3.10.19+20260203-x86_64-unknown-linux-gnu-install_only.tar.gz" +REPO_URL="https://github.com/dexoron/dcr" GITHUB_API="https://api.github.com/repos/dexoron/dcr/releases/latest" RED='\033[0;31m' @@ -27,118 +24,89 @@ error() { echo -e "${RED}✖ $1${NC}"; } trap 'error "Ошибка на строке $LINENO"; exit 1' ERR -check_dependencies() { - log "Проверка зависимостей..." - - command -v curl >/dev/null 2>&1 || { error "curl не установлен"; exit 1; } - command -v tar >/dev/null 2>&1 || { error "tar не установлен"; exit 1; } - - success "Зависимости в порядке" -} - check_os() { - if [[ "$(uname -s)" != "Linux" ]]; then - error "Поддерживается только Linux" - exit 1 - fi + case "$(uname -s)" in + Linux|Darwin) ;; + *) error "Поддерживаются только Linux и macOS"; exit 1 ;; + esac } -get_latest_version() { - curl -fsSL "$GITHUB_API" | sed -n 's/.*"tag_name": "\(.*\)".*/\1/p' +detect_target() { + local os arch + os="$(uname -s)" + arch="$(uname -m)" + + case "$os:$arch" in + Linux:x86_64) TARGET_TRIPLE="x86_64-unknown-linux-gnu" ;; + Darwin:x86_64) TARGET_TRIPLE="x86_64-apple-darwin" ;; + Darwin:arm64|Darwin:aarch64) TARGET_TRIPLE="aarch64-apple-darwin" ;; + *) error "Неподдерживаемая платформа: $os/$arch"; exit 1 ;; + esac + + ASSET_NAME="dcr-${TARGET_TRIPLE}" } -get_installed_version() { - if [[ -f "$INSTALL_PATH/VERSION" ]]; then - cat "$INSTALL_PATH/VERSION" - else - echo "" - fi +check_common_dependencies() { + command -v curl >/dev/null 2>&1 || { error "curl не установлен"; exit 1; } } -install_python() { - log "Python не найден, установка встроенного Python..." - - mkdir -p "$PYTHON_DIR" - mkdir -p "$TMPDIR" - - curl -fsSL "$PYTHON_DOWNLOAD_URL" -o "$TMPDIR/python.tar.gz" - tar -xf "$TMPDIR/python.tar.gz" -C "$PYTHON_DIR" --strip-components=1 - rm -f "$TMPDIR/python.tar.gz" - - success "Встроенный Python установлен" +check_build_dependencies() { + command -v git >/dev/null 2>&1 || { error "git не установлен"; exit 1; } + command -v cargo >/dev/null 2>&1 || { error "cargo не установлен"; exit 1; } } -install_app() { +prepare_sources() { + log "Получение исходников..." + rm -rf "$TMPDIR" + git clone --depth 1 "$REPO_URL" "$TMPDIR" + success "Исходники получены" +} + +build_binary() { + log "Сборка release-бинарника..." + (cd "$TMPDIR" && cargo build --release) + success "Сборка завершена" +} + +fetch_latest_release_json() { + curl -fsSL "$GITHUB_API" +} + +download_binary() { log "Получение последнего релиза..." - TAG=$(get_latest_version) + local release_json download_url tag + release_json="$(fetch_latest_release_json)" - if [[ -z "$TAG" ]]; then + tag="$(printf '%s\n' "$release_json" | sed -n 's/.*"tag_name": "\([^"]*\)".*/\1/p' | head -n1)" + if [[ -z "$tag" ]]; then error "Не удалось определить версию релиза" exit 1 fi - INSTALLED_VERSION=$(get_installed_version) - - if [[ -n "$INSTALLED_VERSION" ]]; then - log "Установленная версия: $INSTALLED_VERSION" - log "Последняя версия: $TAG" - - if [[ "$INSTALLED_VERSION" == "$TAG" ]]; then - success "Установлена последняя версия ($TAG), обновление не требуется" - exit 0 - else - warn "Найдена устаревшая версия, выполняется обновление" - fi - - else - log "DCR не установлен, выполняется установка" + download_url="$(printf '%s\n' "$release_json" | sed -n "s#.*\"browser_download_url\": \"\([^\"]*${ASSET_NAME}\)\".*#\1#p" | head -n1)" + if [[ -z "$download_url" ]]; then + error "Не найден ассет ${ASSET_NAME} в релизе ${tag}" + exit 1 fi - mkdir -p "$TMPDIR" - curl -fsSL -o "$TMPDIR/sources.tar.gz" \ - "https://github.com/dexoron/dcr/archive/refs/tags/$TAG.tar.gz" - - rm -rf "$INSTALL_PATH" mkdir -p "$INSTALL_PATH" - - tar -xf "$TMPDIR/sources.tar.gz" -C "$INSTALL_PATH" --strip-components=1 - rm -f "$TMPDIR/sources.tar.gz" - - echo "$TAG" > "$INSTALL_PATH/VERSION" - - success "Приложение установлено (версия $TAG)" + curl -fL "$download_url" -o "$INSTALL_PATH/dcr" + chmod +x "$INSTALL_PATH/dcr" + success "Скачан бинарник ${ASSET_NAME} (${tag})" } -install_wrapper() { - log "Создание launcher..." - +install_built_binary() { mkdir -p "$INSTALL_PATH" - - cat > "$INSTALL_PATH/dcr" <<'EOF' -#!/usr/bin/env bash -BASE_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" - -if command -v python3 >/dev/null 2>&1; then - PYTHON_BIN="python3" -else - PYTHON_BIN="$BASE_DIR/Python/bin/python3" -fi - -PYTHONPATH="$BASE_DIR/src" exec "$PYTHON_BIN" -m dcr.main "$@" -EOF - + cp "$TMPDIR/target/release/dcr" "$INSTALL_PATH/dcr" chmod +x "$INSTALL_PATH/dcr" - - success "Launcher создан" + success "Установлен бинарник из исходников" } install_link() { log "Создание симлинка..." - mkdir -p "$BINPATH" ln -sf "$INSTALL_PATH/dcr" "$BINPATH/dcr" - success "Команда 'dcr' добавлена в $BINPATH" } @@ -154,23 +122,37 @@ cleanup() { rm -rf "$TMPDIR" 2>/dev/null || true } +select_install_mode() { + echo "Выбери способ установки:" + echo " 1) Скачать готовый бинарник из GitHub Release (рекомендуется)" + echo " 2) Собрать из git" + read -r -p "Введите 1 или 2 [1]: " choice + + case "${choice:-1}" in + 1) INSTALL_MODE="release" ;; + 2) INSTALL_MODE="build" ;; + *) error "Неизвестный вариант"; exit 1 ;; + esac +} + main() { log "Запуск установки DCR" check_os - check_dependencies - + detect_target + check_common_dependencies + select_install_mode cleanup - install_app - - if ! command -v python3 >/dev/null 2>&1; then - install_python + if [[ "$INSTALL_MODE" == "build" ]]; then + check_build_dependencies + prepare_sources + build_binary + install_built_binary else - success "Найден системный Python" + download_binary fi - install_wrapper install_link check_path cleanup diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 4dcb171..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,11 +0,0 @@ -[project] -name = "dcr" -version = "0.1.0" -description = "Утилита для управления проектами для C/C++" -readme = "README.md" -license = "LICENSE" -authors = [ - { name = "Dexoron" }, - { name = "F1F1FISHKA" } -] -requires-python = ">=3.10" \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/cli/build.rs b/src/cli/build.rs new file mode 100644 index 0000000..c284510 --- /dev/null +++ b/src/cli/build.rs @@ -0,0 +1,82 @@ +use crate::config::{PROFILE, PROJECT_COMPILER, flags}; +use crate::utils::fs::check_dir; +use crate::utils::text::{BOLD, BRIGHT_GREEN, BRIGHT_RED, colored}; +use std::fs; +use std::process::Command; +use std::time::Instant; + +pub fn build(args: &[String]) -> i32 { + let mut active_profile = PROFILE.to_string(); + let red_bold = BRIGHT_RED.to_owned() + BOLD; + let green_bold = BRIGHT_GREEN.to_owned() + BOLD; + let project_name = std::env::current_dir() + .ok() + .and_then(|p| p.file_name().map(|v| v.to_string_lossy().to_string())) + .unwrap_or_else(|| "project".to_string()); + + let items = check_dir(None).unwrap_or_default(); + if !items.contains(&"dcr.toml".to_string()) { + println!("{}: не найден файл dcr.toml", colored("error", &red_bold)); + return 1; + } + + if let Some(first_arg) = args.first() { + if first_arg.starts_with("--") { + let candidate = first_arg.trim_start_matches("--"); + if flags(candidate).is_some() { + active_profile = candidate.to_string(); + } else { + println!("{}: неизвестный флаг сборки", colored("error", &red_bold)); + return 1; + } + } else { + println!("{}: неизвестный аргумент", colored("error", &red_bold)); + return 1; + } + } + + println!( + " Сборка проекта `{}`\n Профиль: {}\n Компилятор: {}\n", + colored(&project_name, &green_bold), + colored(&active_profile, &green_bold), + colored(PROJECT_COMPILER, &green_bold) + ); + + if !items.contains(&"target".to_string()) { + let _ = fs::create_dir("./target"); + } + let target_items = check_dir(Some("target")).unwrap_or_default(); + if !target_items.contains(&active_profile) { + let _ = fs::create_dir(format!("./target/{active_profile}")); + } + + let src_items = check_dir(Some("src")).unwrap_or_default(); + if src_items.contains(&"main.c".to_string()) { + let compile_flags = flags(&active_profile).unwrap_or(&[]); + let start_time = Instant::now(); + let mut cmd = Command::new(PROJECT_COMPILER); + cmd.arg("./src/main.c"); + for flag in compile_flags { + cmd.arg(flag); + } + cmd.arg("-o").arg(format!("./target/{active_profile}/main")); + + match cmd.status() { + Ok(status) if status.success() => { + let times = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0; + println!( + " {} Сборка завершена успешно, за {} секунд", + colored("✔", &green_bold), + colored(×.to_string(), &green_bold) + ); + return 0; + } + _ => { + println!("{}: сборка не удалась", colored("error", &red_bold)); + return 1; + } + } + } + + 0 +} diff --git a/src/cli/clean.rs b/src/cli/clean.rs new file mode 100644 index 0000000..c6c4455 --- /dev/null +++ b/src/cli/clean.rs @@ -0,0 +1,72 @@ +use crate::config::flags; +use crate::utils::fs::check_dir; +use crate::utils::text::{BOLD, BRIGHT_GREEN, BRIGHT_RED, BRIGHT_YELLOW, colored}; +use std::fs; + +pub fn clean(args: &[String]) -> i32 { + let red_bold = BRIGHT_RED.to_owned() + BOLD; + let green_bold = BRIGHT_GREEN.to_owned() + BOLD; + let yellow_bold = BRIGHT_YELLOW.to_owned() + BOLD; + + let project_name = std::env::current_dir() + .ok() + .and_then(|p| p.file_name().map(|v| v.to_string_lossy().to_string())) + .unwrap_or_else(|| "project".to_string()); + let items = check_dir(None).unwrap_or_default(); + + if args.len() > 1 { + println!("{}: неизвестный аргумент", colored("error", &red_bold)); + return 1; + } + if !items.contains(&"dcr.toml".to_string()) { + println!("{}: не найден файл dcr.toml", colored("error", &red_bold)); + return 1; + } + + println!( + " Очистка проекта `{}`", + colored(&project_name, &green_bold) + ); + if !items.contains(&"target".to_string()) { + println!( + "{}: директория target не найдена", + colored("warn", &yellow_bold) + ); + return 1; + } + + if let Some(arg) = args.first() { + let mut profile = arg.clone(); + if profile.starts_with("--") { + profile = profile.trim_start_matches("--").to_string(); + } + if flags(&profile).is_none() { + println!("{}: неизвестный профиль", colored("error", &red_bold)); + return 1; + } + + let target_items = check_dir(Some("target")).unwrap_or_default(); + if !target_items.contains(&profile) { + println!( + "{}: директория target/{profile} не найдена", + colored("warn", &yellow_bold) + ); + return 1; + } + + println!(" Профиль: {}", colored(&profile, &green_bold)); + let _ = fs::remove_dir_all(format!("target/{profile}")); + println!( + "{} Удалена директория target/{profile}", + colored("\n ✔", &green_bold) + ); + return 0; + } + + let _ = fs::remove_dir_all("target"); + println!( + "{} Удалена директория target", + colored("\n ✔", &green_bold) + ); + 0 +} diff --git a/src/cli/flag_update.rs b/src/cli/flag_update.rs new file mode 100644 index 0000000..91cc8d8 --- /dev/null +++ b/src/cli/flag_update.rs @@ -0,0 +1,161 @@ +use reqwest::blocking::Client; +use serde::Deserialize; +use std::fs; +use std::path::{Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; + +const LATEST_RELEASE_URL: &str = "https://api.github.com/repos/dexoron/dcr/releases/latest"; + +#[derive(Debug, Deserialize)] +struct Release { + tag_name: String, + assets: Vec, +} + +#[derive(Debug, Deserialize)] +struct ReleaseAsset { + name: String, + browser_download_url: String, +} + +pub fn flag_update(args: &[String]) -> i32 { + if !args.is_empty() { + println!("Ошибка: команда не поддежривает аргументы"); + return 1; + } + + let current_version = env!("CARGO_PKG_VERSION"); + let target = env!("DCR_TARGET"); + let client = match Client::builder().user_agent("dcr-updater").build() { + Ok(client) => client, + Err(_) => { + println!("Ошибка: не удалось инициализировать HTTP клиент"); + return 1; + } + }; + + let release = match fetch_latest_release(&client) { + Ok(release) => release, + Err(err) => { + println!("Ошибка: не удалось проверить обновления: {err}"); + return 1; + } + }; + + let latest_version = release.tag_name.trim_start_matches('v'); + if latest_version == current_version { + println!("Установлена последняя версия: {current_version}"); + return 0; + } + + let candidate_names = asset_candidates(target); + let Some(asset) = release + .assets + .iter() + .find(|asset| candidate_names.iter().any(|name| name == &asset.name)) + else { + println!("Ошибка: не найден бинарник для таргета {target}"); + return 1; + }; + + let bytes = match download_asset(&client, &asset.browser_download_url) { + Ok(bytes) => bytes, + Err(err) => { + println!("Ошибка: не удалось скачать обновление: {err}"); + return 1; + } + }; + + let current_exe = match std::env::current_exe() { + Ok(path) => path, + Err(_) => { + println!("Ошибка: не удалось определить путь текущего бинарника"); + return 1; + } + }; + let temp_path = temp_binary_path(¤t_exe); + + if fs::write(&temp_path, &bytes).is_err() { + println!("Ошибка: не удалось записать временный бинарник"); + return 1; + } + set_executable_permissions(&temp_path); + + if self_replace::self_replace(&temp_path).is_err() { + let _ = fs::remove_file(&temp_path); + println!("Ошибка: не удалось заменить текущий бинарник"); + return 1; + } + + let _ = fs::remove_file(&temp_path); + println!("Обновление завершено: {current_version} -> {latest_version}"); + 0 +} + +fn fetch_latest_release(client: &Client) -> Result { + let response = client + .get(LATEST_RELEASE_URL) + .send() + .map_err(|_| "запрос к GitHub API завершился ошибкой".to_string())?; + + if !response.status().is_success() { + return Err(format!("GitHub API вернул статус {}", response.status())); + } + + response + .json::() + .map_err(|_| "ответ GitHub API имеет неожиданный формат".to_string()) +} + +fn download_asset(client: &Client, url: &str) -> Result, String> { + let response = client + .get(url) + .send() + .map_err(|_| "запрос на скачивание завершился ошибкой".to_string())?; + + if !response.status().is_success() { + return Err(format!("скачивание вернуло статус {}", response.status())); + } + + response + .bytes() + .map(|bytes| bytes.to_vec()) + .map_err(|_| "не удалось прочитать скачанные данные".to_string()) +} + +fn asset_candidates(target: &str) -> Vec { + let mut names = vec![format!("dcr-{target}")]; + + match target { + "x86_64-pc-windows-msvc" => { + names.push("dcr-x86_64-pc-windows-msvc.exe".to_string()); + } + _ => {} + } + + names +} + +fn temp_binary_path(current_exe: &Path) -> PathBuf { + let stamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|value| value.as_millis()) + .unwrap_or(0); + let mut extension = format!("new-{stamp}"); + if cfg!(windows) { + extension.push_str(".exe"); + } + current_exe.with_extension(extension) +} + +fn set_executable_permissions(path: &Path) { + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + if let Ok(meta) = fs::metadata(path) { + let mut perms = meta.permissions(); + perms.set_mode(0o755); + let _ = fs::set_permissions(path, perms); + } + } +} diff --git a/src/cli/help.rs b/src/cli/help.rs new file mode 100644 index 0000000..b148a2b --- /dev/null +++ b/src/cli/help.rs @@ -0,0 +1,36 @@ +use crate::utils::text::{BOLD, BRIGHT_CYAN, BRIGHT_GREEN, colored}; + +pub fn help() -> i32 { + let green_bold = BRIGHT_GREEN.to_owned() + BOLD; + let cyan_bold = BRIGHT_CYAN.to_owned() + BOLD; + + println!("DCR (Dexoron Cargo Realization)"); + println!("Менеджер C-проектов, вдохновленный Cargo."); + println!(); + println!("{}", colored("ИСПОЛЬЗОВАНИЕ:", &green_bold)); + println!("{}", colored(" dcr <команда> [опции]", &cyan_bold)); + println!(); + println!("{}", colored("КОМАНДЫ:", &green_bold)); + println!(" new Создать новый проект"); + println!(" init Инициализировать текущую директорию как проект"); + println!(" build [--profile] Собрать проект (по умолчанию: --debug)"); + println!(" run [--profile] Собрать и запустить (по умолчанию: --debug)"); + println!(" clean Удалить директорию target"); + println!("{}", colored("ФЛАГИ:", &green_bold)); + println!(" --help Показать справку по команде"); + println!(" --update Обновить dcr до актуальной версии"); + println!(" --version Показать версию dcr"); + println!(); + println!("{}", colored("ОПЦИИ:", &green_bold)); + println!(" --debug Сборка с профилем debug"); + println!(" --release Сборка с профилем release"); + println!(); + println!("{}", colored("ПРИМЕРЫ:", &green_bold)); + println!("{}", colored(" dcr new hello", &cyan_bold)); + println!("{}", colored(" dcr build --release", &cyan_bold)); + println!("{}", colored(" dcr run --debug", &cyan_bold)); + println!(); + println!("{}", colored("ПОДСКАЗКА:", &green_bold)); + println!(" Запусти 'dcr <команда> --help' для справки по команде."); + 0 +} diff --git a/src/cli/init.rs b/src/cli/init.rs new file mode 100644 index 0000000..39699ad --- /dev/null +++ b/src/cli/init.rs @@ -0,0 +1,96 @@ +use crate::config::{FILE_MAIN_C, file_dcr_toml}; +use crate::utils::fs::check_dir; +use crate::utils::text::{BOLD, BRIGHT_CYAN, BRIGHT_GREEN, BRIGHT_RED, colored, printc}; +use std::fs; +use std::io::Write; + +pub fn init(args: &[String]) -> i32 { + let red_bold = BRIGHT_RED.to_owned() + BOLD; + let green_bold = BRIGHT_GREEN.to_owned() + BOLD; + let cyan_bold = BRIGHT_CYAN.to_owned() + BOLD; + + if !args.is_empty() { + println!( + "{}: команда не поддерживает доп. аргументы", + colored("error", &red_bold) + ); + return 1; + } + + let items = check_dir(None).unwrap_or_default(); + let project_name = std::env::current_dir() + .ok() + .and_then(|p| p.file_name().map(|v| v.to_string_lossy().to_string())) + .unwrap_or_else(|| "project".to_string()); + + if !items.is_empty() { + println!("{}: директория не пуста", colored("error", &red_bold)); + return 1; + } + + let cwd = std::env::current_dir() + .map(|v| v.to_string_lossy().to_string()) + .unwrap_or_else(|_| ".".to_string()); + println!("Инициализация проекта в {cwd}"); + + let mut dcr_toml = match fs::File::create("./dcr.toml") { + Ok(file) => file, + Err(_) => { + println!( + "{}: не удалось создать dcr.toml", + colored("error", &red_bold) + ); + return 1; + } + }; + if dcr_toml + .write_all(file_dcr_toml(&project_name).as_bytes()) + .is_err() + { + println!( + "{}: не удалось записать dcr.toml", + colored("error", &red_bold) + ); + return 1; + } + println!( + " {} Создан файл {}", + colored("✔", &green_bold), + colored("dcr.toml", &cyan_bold) + ); + + if fs::create_dir("src").is_err() { + println!("{}: не удалось создать src", colored("error", &red_bold)); + return 1; + } + let mut main_c = match fs::File::create("./src/main.c") { + Ok(file) => file, + Err(_) => { + println!( + "{}: не удалось создать src/main.c", + colored("error", &red_bold) + ); + return 1; + } + }; + if main_c.write_all(FILE_MAIN_C.as_bytes()).is_err() { + println!( + "{}: не удалось записать src/main.c", + colored("error", &red_bold) + ); + return 1; + } + println!( + " {} Создан файл {}\n", + colored("✔", &green_bold), + colored("src/main.c", &cyan_bold) + ); + + println!( + "Проект `{}` успешно создан\n", + colored(&project_name, &green_bold) + ); + printc("Следуюший шаг:", &green_bold); + printc(" dcr run", &cyan_bold); + 0 +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..5b37090 --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,7 @@ +pub mod build; +pub mod clean; +pub mod flag_update; +pub mod help; +pub mod init; +pub mod new; +pub mod run; diff --git a/src/cli/new.rs b/src/cli/new.rs new file mode 100644 index 0000000..c2bc7a2 --- /dev/null +++ b/src/cli/new.rs @@ -0,0 +1,123 @@ +use crate::config::{FILE_MAIN_C, file_dcr_toml}; +use crate::utils::fs::check_dir; +use crate::utils::text::{BOLD, BRIGHT_CYAN, BRIGHT_GREEN, BRIGHT_RED, colored, printc}; +use std::fs; +use std::io::Write; + +pub fn new(args: &[String]) -> i32 { + let items = check_dir(None).unwrap_or_default(); + let red_bold = BRIGHT_RED.to_owned() + BOLD; + let green_bold = BRIGHT_GREEN.to_owned() + BOLD; + let cyan_bold = BRIGHT_CYAN.to_owned() + BOLD; + + if args.is_empty() { + println!( + "{}: не указанно название проекта", + colored("error", &red_bold) + ); + return 1; + } + if args.len() > 1 { + println!( + "{}: команда не поддерживает доп. аргументы", + colored("error", &red_bold) + ); + return 1; + } + + let project_name = &args[0]; + println!( + "Создание проекта `{}`...", + colored(project_name, &green_bold) + ); + + if items.contains(project_name) { + println!( + "{}: директория `{}` уже существует\n", + colored("error", &red_bold), + colored(project_name, &green_bold) + ); + printc("Подсказка:", &green_bold); + println!( + " Используй `{}` для инициализации существующего проекта\n или задай другое имя проекта", + colored("dcr init", &cyan_bold) + ); + return 1; + } + + if fs::create_dir(project_name).is_err() { + println!( + "{}: не удалось создать директорию", + colored("error", &red_bold) + ); + return 1; + } + println!( + " {} Создана директория {}", + colored("✔", &green_bold), + project_name + ); + + let toml_path = format!("./{project_name}/dcr.toml"); + let mut dcr_toml = match fs::File::create(&toml_path) { + Ok(file) => file, + Err(_) => { + println!( + "{}: не удалось создать dcr.toml", + colored("error", &red_bold) + ); + return 1; + } + }; + if dcr_toml + .write_all(file_dcr_toml(project_name).as_bytes()) + .is_err() + { + println!( + "{}: не удалось записать dcr.toml", + colored("error", &red_bold) + ); + return 1; + } + println!( + " {} Создан файл {}", + colored("✔", &green_bold), + colored("dcr.toml", &cyan_bold) + ); + + if fs::create_dir_all(format!("./{project_name}/src")).is_err() { + println!("{}: не удалось создать src", colored("error", &red_bold)); + return 1; + } + let main_c_path = format!("./{project_name}/src/main.c"); + let mut main_c = match fs::File::create(&main_c_path) { + Ok(file) => file, + Err(_) => { + println!( + "{}: не удалось создать src/main.c", + colored("error", &red_bold) + ); + return 1; + } + }; + if main_c.write_all(FILE_MAIN_C.as_bytes()).is_err() { + println!( + "{}: не удалось записать src/main.c", + colored("error", &red_bold) + ); + return 1; + } + println!( + " {} Создан файл {}\n", + colored("✔", &green_bold), + colored("src/main.c", &cyan_bold) + ); + + println!( + "Проект `{}` успешно создан\n", + colored(project_name, &green_bold) + ); + printc("Следуюший шаг:", &green_bold); + printc(&format!(" cd {project_name}\n dcr run"), &cyan_bold); + 0 +} diff --git a/src/cli/run.rs b/src/cli/run.rs new file mode 100644 index 0000000..cbf69c8 --- /dev/null +++ b/src/cli/run.rs @@ -0,0 +1,55 @@ +use crate::cli::build::build; +use crate::config::{PROFILE, flags}; +use crate::utils::fs::check_dir; +use crate::utils::text::{BOLD, BRIGHT_RED, colored}; +use std::process::Command; + +pub fn run(args: &[String]) -> i32 { + let red_bold = BRIGHT_RED.to_owned() + BOLD; + let mut active_profile = PROFILE.to_string(); + + let items = check_dir(None).unwrap_or_default(); + if !items.contains(&"dcr.toml".to_string()) { + println!("{}: не найден файл dcr.toml", colored("error", &red_bold)); + return 1; + } + + if let Some(first_arg) = args.first() { + if first_arg.starts_with("--") { + let candidate = first_arg.trim_start_matches("--"); + if flags(candidate).is_some() { + active_profile = candidate.to_string(); + } else { + println!("{}: неизвестный флаг сборки", colored("error", &red_bold)); + return 1; + } + } else { + println!("{}: неизвестный аргумент", colored("error", &red_bold)); + return 1; + } + } + + let build_status = build(args); + if build_status == 0 { + println!("\n Запуск target/{active_profile}/main"); + println!("--------------------------------"); + return Command::new(format!("./target/{active_profile}/main")) + .status() + .map(|status| status.code().unwrap_or(1)) + .unwrap_or(1); + } + + if check_dir(Some(&format!("target/{active_profile}"))) + .unwrap_or_default() + .contains(&active_profile) + { + println!("Запуск последнего релиза"); + return Command::new(format!("./target/{active_profile}/main")) + .status() + .map(|status| status.code().unwrap_or(1)) + .unwrap_or(1); + } + + println!("Исправьте ошибки в коде для запуска проекта"); + 0 +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..d293c69 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,43 @@ +pub const PROJECT_VERSION: &str = "0.1.0"; +pub const PROJECT_LANGUAGE: &str = "c"; +pub const PROJECT_COMPILER: &str = "clang"; +pub const PROFILE: &str = "debug"; + +pub fn file_dcr_toml(project_name: &str) -> String { + format!( + r#"[package] +name = "{project_name}" +version = "{project_version}" +language = "{project_language}" +compiler = "{project_compiler}" + +[dependencies] +"#, + project_version = PROJECT_VERSION, + project_language = PROJECT_LANGUAGE, + project_compiler = PROJECT_COMPILER + ) +} + +pub const FILE_MAIN_C: &str = r#"#include + +int main(void) { + printf("Hello World!\n"); + return 0; +} +"#; + +pub fn flags(profile: &str) -> Option<&'static [&'static str]> { + match profile { + "release" => Some(&["-O3", "-DNDEBUG", "-march=native"]), + "debug" => Some(&[ + "-O0", + "-g", + "-Wall", + "-Wextra", + "-fno-omit-frame-pointer", + "-DDEBUG", + ]), + _ => None, + } +} diff --git a/src/dcr/__init__.py b/src/dcr/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/dcr/cli/__init__.py b/src/dcr/cli/__init__.py deleted file mode 100644 index f7ee317..0000000 --- a/src/dcr/cli/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from .help import help -from .init import init -from .new import new -from .run import run -from .build import build -from .clean import clean -from .flag_update import flag_update - - -__all__ = [ - "help", - "init", - "new", - "run", - "build", - "clean", - "flag_update", -] diff --git a/src/dcr/cli/build.py b/src/dcr/cli/build.py deleted file mode 100644 index d9e9b30..0000000 --- a/src/dcr/cli/build.py +++ /dev/null @@ -1,70 +0,0 @@ -import time -import os -import subprocess -from ..config import flags, profile, project_compiler -from ..utils.fs import check_dir -from ..utils.text import colored, BRIGHT_GREEN, BOLD, BRIGHT_RED - - -def build(args: list[str] | None = None) -> int: - active_profile: str = profile - project_name = os.path.basename(os.getcwd()) - if "dcr.toml" not in check_dir(): - print(colored("error", BRIGHT_RED + BOLD) + ": не найден файл dcr.toml") - return 1 - if args: - if len(args) >= 1 and args[0].startswith("--"): - candidate: str = args[0][2:] - if candidate in flags: - active_profile: str = candidate - else: - print(colored("error", BRIGHT_RED + BOLD) + ": неизвестный флаг сборки") - return 1 - else: - print(colored("error", BRIGHT_RED + BOLD) + ": неизвестный аргумент") - return 1 - - print( - " Сборка проекта `" - + colored(project_name, BRIGHT_GREEN + BOLD) - + "`\n Профиль: " - + colored(active_profile, BRIGHT_GREEN + BOLD) - + "\n Компилятор: " - + colored(project_compiler, BRIGHT_GREEN + BOLD) - + "\n" - ) - - if "target" not in check_dir(): - os.mkdir("./target") - if active_profile not in check_dir("target"): - os.mkdir(f"./target/{active_profile}") - if "main.c" in check_dir("src"): - compile_flags = flags[active_profile] - start_time: float = time.time() - try: - subprocess.run( - [ - project_compiler, - "./src/main.c", - *compile_flags, - "-o", - f"./target/{active_profile}/main", - ], - check=True, - ) - end_time: float = time.time() - times: float = end_time - start_time - times: float = int(times * 100) / 100 - print( - " " - + colored("✔", BRIGHT_GREEN + BOLD) - + " Сборка завершена успешно, за " - + colored(str(times), BRIGHT_GREEN + BOLD) - + " секунд" - ) - return 0 - except: # noqa: E722 - print(colored("error", BRIGHT_RED + BOLD) + ": сборка не удалась") - return 1 - - return 0 diff --git a/src/dcr/cli/clean.py b/src/dcr/cli/clean.py deleted file mode 100644 index 6542773..0000000 --- a/src/dcr/cli/clean.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import shutil -from ..utils.fs import check_dir -from ..utils.text import colored, BRIGHT_RED, BOLD, BRIGHT_GREEN, BRIGHT_YELLOW -from ..config import flags - - -def clean(args: list[str] | None = None) -> int: - project_name = os.path.basename(os.getcwd()) - items: list[str] = check_dir() - - if args and len(args) > 1: - print(colored("error", BRIGHT_RED + BOLD) + ": неизвестный аргумент") - return 1 - - if "dcr.toml" not in items: - print(colored("error", BRIGHT_RED + BOLD) + ": не найден файл dcr.toml") - return 1 - - print(" Очистка проекта `" + colored(project_name, BRIGHT_GREEN + BOLD) + "`") - - if "target" not in items: - print(colored("warn", BRIGHT_YELLOW + BOLD) + ": директория target не найдена") - return 1 - - if args: - profile = args[0] - - if profile.startswith("--"): - profile = profile[2:] - - if profile not in flags: - print(colored("error", BRIGHT_RED + BOLD) + ": неизвестный профиль") - return 1 - - if profile not in check_dir("target"): - print( - colored("warn", BRIGHT_YELLOW + BOLD) - + f": директория target/{profile} не найдена" - ) - return 1 - - print(" Профиль: " + colored(profile, BRIGHT_GREEN + BOLD)) - shutil.rmtree(os.path.join("target", profile)) - print( - colored("\n ✔", BRIGHT_GREEN + BOLD) - + f" Удалена директория target/{profile}" - ) - return 0 - - shutil.rmtree("target") - print(colored("\n ✔", BRIGHT_GREEN + BOLD) + " Удалена директория target") - return 0 diff --git a/src/dcr/cli/flag_update.py b/src/dcr/cli/flag_update.py deleted file mode 100644 index 4a6aa54..0000000 --- a/src/dcr/cli/flag_update.py +++ /dev/null @@ -1,23 +0,0 @@ -import platform -import shutil -import subprocess - - -def flag_update(args: list[str] | None = None) -> int: - if args and len(args) > 0: - print("Ошибка: команда не поддежривает аргументы") - return 1 - if platform.system() != "Linux": - print("Ошибка: обновление поддерживается только на Linux") - return 1 - if shutil.which("curl") is None: - print("Ошибка: не найден curl") - return 1 - try: - subprocess.run( - ["bash", "-c", "curl -fsSL dcr.zov.tatar | bash"], check=True - ) - return 0 - except subprocess.CalledProcessError: - print("Ошибка: не удалось выполнить обновление") - return 1 diff --git a/src/dcr/cli/help.py b/src/dcr/cli/help.py deleted file mode 100644 index eeceba6..0000000 --- a/src/dcr/cli/help.py +++ /dev/null @@ -1,39 +0,0 @@ -from ..utils.text import colored, BRIGHT_GREEN, BRIGHT_CYAN, BOLD - - -def help() -> int: - print( - "\n".join( - [ - "DCR (Dexoron Cargo Realization)", - "Менеджер C-проектов, вдохновленный Cargo.", - "", - colored("ИСПОЛЬЗОВАНИЕ:", BRIGHT_GREEN + BOLD), - colored(" dcr <команда> [опции]", BRIGHT_CYAN + BOLD), - "", - colored("КОМАНДЫ:", BRIGHT_GREEN + BOLD), - " new Создать новый проект", - " init Инициализировать текущую директорию как проект", - " build [--profile] Собрать проект (по умолчанию: --debug)", - " run [--profile] Собрать и запустить (по умолчанию: --debug)", - " clean Удалить директорию target", - colored("ФЛАГИ:", BRIGHT_GREEN + BOLD), - " --help Показать справку по команде", - " --update Обновить dcr до актуальной версии", - " --version Показать версию dcr", - "", - colored("ОПЦИИ:", BRIGHT_GREEN + BOLD), - " --debug Сборка с профилем debug", - " --release Сборка с профилем release", - "", - colored("ПРИМЕРЫ:", BRIGHT_GREEN + BOLD), - colored(" dcr new hello", BRIGHT_CYAN + BOLD), - colored(" dcr build --release", BRIGHT_CYAN + BOLD), - colored(" dcr run --debug", BRIGHT_CYAN + BOLD), - "", - colored("ПОДСКАЗКА:", BRIGHT_GREEN + BOLD), - " Запусти 'dcr <команда> --help' для справки по команде.", - ] - ) - ) - return 0 diff --git a/src/dcr/cli/init.py b/src/dcr/cli/init.py deleted file mode 100644 index 25afa83..0000000 --- a/src/dcr/cli/init.py +++ /dev/null @@ -1,61 +0,0 @@ -from ..utils.text import colored, BRIGHT_RED, BOLD, printc, BRIGHT_GREEN, BRIGHT_CYAN -from ..config import ( - file_dcr_toml, - file_main_c, - project_version, - project_language, - project_compiler, -) -import os -from ..utils.fs import check_dir - - -def init(args: list[str] | None = None) -> int: - if args: - print( - colored("error", BRIGHT_RED + BOLD) - + ": команда не поддерживает доп. аргументы" - ) - return 1 - items: list[str] = check_dir() - project_name = os.path.basename(os.getcwd()) - - if items: - print(colored("error", BRIGHT_RED + BOLD) + ": директория не пуста") - return 1 - - print("Инициализация проекта в " + os.getcwd()) - - with open("./dcr.toml", "w") as file: - file.write( - file_dcr_toml.format( - project_name=project_name, - project_version=project_version, - project_language=project_language, - project_compiler=project_compiler, - ) - ) - print( - " " - + colored("✔", BRIGHT_GREEN + BOLD) - + " Создан файл " - + colored("dcr.toml", BRIGHT_CYAN + BOLD) - ) - - os.mkdir("src") - with open("./src/main.c", "w") as file: - file.write(file_main_c) - print( - " " - + colored("✔", BRIGHT_GREEN + BOLD) - + " Создан файл " - + colored("src/main.c\n", BRIGHT_CYAN + BOLD) - ) - - print( - "Проект `" + colored(project_name, BRIGHT_GREEN + BOLD) + "` успешно создан\n" - ) - printc("Следуюший шаг:", BRIGHT_GREEN + BOLD) - printc(" dcr run", BRIGHT_CYAN + BOLD) - - return 0 diff --git a/src/dcr/cli/new.py b/src/dcr/cli/new.py deleted file mode 100644 index a6b417c..0000000 --- a/src/dcr/cli/new.py +++ /dev/null @@ -1,89 +0,0 @@ -from ..utils.text import ( - colored, - printc, - BRIGHT_GREEN, - BOLD, - BRIGHT_CYAN, - BRIGHT_RED, -) -from ..config import ( - file_dcr_toml, - file_main_c, - project_version, - project_language, - project_compiler, -) -import os -from ..utils.fs import check_dir - - -def new(args: list[str]) -> int: - items: list[str] = check_dir() - if len(args) == 0: - print(colored("error", BRIGHT_RED + BOLD) + ": не указанно название проекта") - return 1 - if len(args) > 1: - print( - colored("error", BRIGHT_RED + BOLD) - + ": команда не поддерживает доп. аргументы" - ) - return 1 - project_name: str = args[0] - print("Создание проекта `" + colored(project_name, BRIGHT_GREEN + BOLD) + "`...") - - if project_name in items: - print( - colored("error", BRIGHT_RED + BOLD) - + ": директория `" - + colored(project_name, BRIGHT_GREEN + BOLD) - + "` уже существует\n" - ) - printc("Подсказка:", BRIGHT_GREEN + BOLD) - print( - " Используй `" - + colored("dcr init", BRIGHT_CYAN + BOLD) - + "` для инициализации существующего проекта\n или задай другое имя проекта" - ) - return 1 - - os.mkdir(project_name) - print( - " " - + colored("✔", BRIGHT_GREEN + BOLD) - + " Создана директория " - + project_name - ) - - with open(f"./{project_name}/dcr.toml", "w") as file: - file.write( - file_dcr_toml.format( - project_name=project_name, - project_version=project_version, - project_language=project_language, - project_compiler=project_compiler, - ) - ) - print( - " " - + colored("✔", BRIGHT_GREEN + BOLD) - + " Создан файл " - + colored("dcr.toml", BRIGHT_CYAN + BOLD) - ) - - os.makedirs(f"./{project_name}/src") - with open(f"./{project_name}/src/main.c", "w") as file: - file.write(file_main_c) - print( - " " - + colored("✔", BRIGHT_GREEN + BOLD) - + " Создан файл " - + colored("src/main.c\n", BRIGHT_CYAN + BOLD) - ) - - print( - "Проект `" + colored(project_name, BRIGHT_GREEN + BOLD) + "` успешно создан\n" - ) - printc("Следуюший шаг:", BRIGHT_GREEN + BOLD) - printc(" cd " + project_name + "\n dcr run", BRIGHT_CYAN + BOLD) - - return 0 diff --git a/src/dcr/cli/run.py b/src/dcr/cli/run.py deleted file mode 100644 index 145137b..0000000 --- a/src/dcr/cli/run.py +++ /dev/null @@ -1,37 +0,0 @@ -import subprocess - -from ..utils.fs import check_dir -from ..utils.text import colored, BRIGHT_RED, BOLD -from ..config import flags, profile -from .build import build - - -def run(args: list[str] | None = None) -> int: - active_profile: str = profile - if "dcr.toml" not in check_dir(): - print(colored("error", BRIGHT_RED + BOLD) + ": не найден файл dcr.toml") - return 1 - if args: - if len(args) >= 1 and args[0].startswith("--"): - candidate: str = args[0][2:] - if candidate in flags: - active_profile: str = candidate - else: - print(colored("error", BRIGHT_RED + BOLD) + ": неизвестный флаг сборки") - return 1 - else: - print(colored("error", BRIGHT_RED + BOLD) + ": неизвестный аргумент") - return 1 - - build_status: int = build(args) - if build_status == 0: - print("\n Запуск " + "target/" + active_profile + "/main") - print("--------------------------------") - return subprocess.run([f"./target/{active_profile}/main"]).returncode - else: - if active_profile in check_dir("target/" + active_profile): - print("Запуск последнего релиза") - return subprocess.run([f"./target/{active_profile}/main"]).returncode - else: - print("Исправьте ошибки в коде для запуска проекта") - return 0 diff --git a/src/dcr/config.py b/src/dcr/config.py deleted file mode 100644 index 032d7d4..0000000 --- a/src/dcr/config.py +++ /dev/null @@ -1,43 +0,0 @@ -file_dcr_toml: str = """[package] -name = "{project_name}" -version = "{project_version}" -language = "{project_language}" -compiler = "{project_compiler}" - -[dependencies] -""" -file_main_c: str = """#include - -int main(void) { - printf("Hello World!\\n"); - return 0; -} -""" - -file_dcr_lock: str = """# This file is automatically @generated by dcr. -# It is not intended for manual editing. -version = 1 - -[[package]] -name = "{project_name}" -version = "{project_version}" -""" - -"""[[package]] -name = "{package_name}" -version = "{package_version}" -source = "{{package_source}" -checksum = "{package_checksum}" -dependencies = [{package_dependencies}]""" - -project_version: str = "0.1.0" -project_language: str = "c" -project_compiler: str = "clang" - -profile: str = "debug" -flags: dict[str, list[str]] = { - "release": ["-O3", "-DNDEBUG", "-march=native"], - "debug": ["-O0", "-g", "-Wall", "-Wextra", "-fno-omit-frame-pointer", "-DDEBUG"], -} - -VERSION = "v0.1.1" diff --git a/src/dcr/main.py b/src/dcr/main.py deleted file mode 100644 index 338cbc9..0000000 --- a/src/dcr/main.py +++ /dev/null @@ -1,38 +0,0 @@ -from .config import VERSION -import dcr.cli as cli # ty:ignore[unresolved-import] -import sys - - -def main() -> int: - if len(sys.argv) < 2: - cli.help() - return 0 - - cmd: str = sys.argv[1] - args: str | list[str] = sys.argv[2:] - - match cmd: - case "new": - cli.new(args) - case "init": - cli.init(args) - case "build": - cli.build(args) - case "run": - cli.run(args) - case "clean": - cli.clean(args) - case "--version": - print(f"dcr {VERSION} (GNU/Linux)") - case "--help": - cli.help() - case "--update": - cli.flag_update(args) - case _: - print("Неизвестная команда или аргумент") - - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/src/dcr/utils/__init__.py b/src/dcr/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/dcr/utils/fs.py b/src/dcr/utils/fs.py deleted file mode 100644 index 72ebfcf..0000000 --- a/src/dcr/utils/fs.py +++ /dev/null @@ -1,8 +0,0 @@ -import os - - -def check_dir(dir: str | None = None) -> list[str]: - if dir in (None, ".", "./"): - return os.listdir(os.getcwd()) - else: - return os.listdir(os.getcwd() + "/" + dir) diff --git a/src/dcr/utils/text.py b/src/dcr/utils/text.py deleted file mode 100644 index 29a38a8..0000000 --- a/src/dcr/utils/text.py +++ /dev/null @@ -1,40 +0,0 @@ -# RESET -RESET = "\033[0m" - -# TEXT STYLES -BOLD = "\033[1m" -DIM = "\033[2m" -ITALIC = "\033[3m" -UNDERLINE = "\033[4m" -BLINK = "\033[5m" -REVERSE = "\033[7m" -HIDDEN = "\033[8m" -STRIKETHROUGH = "\033[9m" - -# STANDARD COLORS (Foreground) -BLACK = "\033[30m" -RED = "\033[31m" -GREEN = "\033[32m" -YELLOW = "\033[33m" -BLUE = "\033[34m" -MAGENTA = "\033[35m" -CYAN = "\033[36m" -WHITE = "\033[37m" - -# BRIGHT COLORS -BRIGHT_BLACK = "\033[90m" -BRIGHT_RED = "\033[91m" -BRIGHT_GREEN = "\033[92m" -BRIGHT_YELLOW = "\033[93m" -BRIGHT_BLUE = "\033[94m" -BRIGHT_MAGENTA = "\033[95m" -BRIGHT_CYAN = "\033[96m" -BRIGHT_WHITE = "\033[97m" - - -def colored(msg: str, style: str) -> str: - return style + msg + RESET - - -def printc(msg: str, style: str) -> None: - print(style + msg + RESET) diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d9d390b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,34 @@ +mod cli; +mod config; +mod utils; + +fn main() { + let args: Vec = std::env::args().collect(); + + if args.len() < 2 { + std::process::exit(cli::help::help()); + } + + let cmd = args[1].as_str(); + let rest = &args[2..]; + + let code = match cmd { + "new" => cli::r#new::new(rest), + "init" => cli::init::init(rest), + "build" => cli::build::build(rest), + "run" => cli::run::run(rest), + "clean" => cli::clean::clean(rest), + "--version" => { + println!("dcr {} ({})", env!("CARGO_PKG_VERSION"), env!("DCR_TARGET")); + 0 + } + "--help" => cli::help::help(), + "--update" => cli::flag_update::flag_update(rest), + _ => { + println!("Неизвестная команда или аргумент"); + 0 + } + }; + + std::process::exit(code); +} diff --git a/src/test/__init__.py b/src/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/utils/fs.rs b/src/utils/fs.rs new file mode 100644 index 0000000..7b0741f --- /dev/null +++ b/src/utils/fs.rs @@ -0,0 +1,17 @@ +use std::io; +use std::path::PathBuf; + +pub fn check_dir(dir: Option<&str>) -> io::Result> { + let path: PathBuf = match dir { + None | Some(".") | Some("./") => std::env::current_dir()?, + Some(value) => std::env::current_dir()?.join(value), + }; + + let mut items = Vec::new(); + for entry in std::fs::read_dir(path)? { + let entry = entry?; + items.push(entry.file_name().to_string_lossy().to_string()); + } + + Ok(items) +} diff --git a/src/utils/log.rs b/src/utils/log.rs new file mode 100644 index 0000000..5b9d138 --- /dev/null +++ b/src/utils/log.rs @@ -0,0 +1,30 @@ +use crate::utils::text::{BOLD, BRIGHT_GREEN, BRIGHT_RED, BRIGHT_YELLOW, colored}; + +#[allow(dead_code)] +pub fn error(msg: &str) { + println!( + "{}: {msg}", + colored("error", &(BRIGHT_RED.to_owned() + BOLD)) + ); +} + +#[allow(dead_code)] +pub fn warn(msg: &str) { + println!( + "{}: {msg}", + colored("warn", &(BRIGHT_YELLOW.to_owned() + BOLD)) + ); +} + +#[allow(dead_code)] +pub fn info(msg: &str) { + println!("{}: {msg}", colored("info", BOLD)); +} + +#[allow(dead_code)] +pub fn ok(msg: &str) { + println!( + "{}: {msg}", + colored("ok", &(BRIGHT_GREEN.to_owned() + BOLD)) + ); +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..98c5e61 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,3 @@ +pub mod fs; +pub mod log; +pub mod text; diff --git a/src/utils/text.rs b/src/utils/text.rs new file mode 100644 index 0000000..0ef1a2e --- /dev/null +++ b/src/utils/text.rs @@ -0,0 +1,16 @@ +pub const RESET: &str = "\x1b[0m"; + +pub const BOLD: &str = "\x1b[1m"; + +pub const BRIGHT_RED: &str = "\x1b[91m"; +pub const BRIGHT_GREEN: &str = "\x1b[92m"; +pub const BRIGHT_YELLOW: &str = "\x1b[93m"; +pub const BRIGHT_CYAN: &str = "\x1b[96m"; + +pub fn colored(msg: &str, style: &str) -> String { + format!("{style}{msg}{RESET}") +} + +pub fn printc(msg: &str, style: &str) { + println!("{style}{msg}{RESET}"); +}