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}"); +}