├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE.md ├── README.md ├── assets └── templates │ ├── empty │ ├── .gitignore │ ├── project.json │ ├── selene.toml │ └── wally.toml │ ├── model │ ├── .gitignore │ ├── LICENSE.md │ ├── README.md │ ├── project.json │ ├── selene.toml │ ├── src │ │ └── .src.luau │ └── wally.toml │ ├── package │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── project.json │ ├── selene.toml │ ├── src │ │ └── init.luau │ └── wally.toml │ ├── place │ ├── .gitignore │ ├── README.md │ ├── project.json │ ├── selene.toml │ ├── src │ │ ├── Client │ │ │ └── Main.client.luau │ │ ├── Server │ │ │ └── Main.server.luau │ │ └── Shared │ │ │ └── Hello.luau │ └── wally.toml │ ├── plugin │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── README.md │ ├── project.json │ ├── selene.toml │ ├── src │ │ └── .src.server.luau │ └── wally.toml │ └── quick │ ├── .gitignore │ ├── project.json │ ├── selene.toml │ ├── src │ ├── ReplicatedFirst │ │ └── .gitkeep │ ├── ReplicatedStorage │ │ └── .gitkeep │ ├── ServerScriptService │ │ └── .gitkeep │ ├── ServerStorage │ │ └── .gitkeep │ ├── StarterGui │ │ └── .gitkeep │ ├── StarterPack │ │ └── .gitkeep │ ├── StarterPlayer │ │ ├── StarterCharacterScripts │ │ │ └── .gitkeep │ │ └── StarterPlayerScripts │ │ │ └── .gitkeep │ └── Workspace │ │ └── .gitkeep │ └── wally.toml ├── build.rs ├── crates ├── config-derive │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── rustfmt.toml │ ├── src │ │ ├── lib.rs │ │ └── util.rs │ └── tests │ │ └── mod.rs ├── json-formatter │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── rustfmt.toml │ └── src │ │ └── lib.rs ├── notify-debouncer-full │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── rustfmt.toml │ └── src │ │ ├── cache.rs │ │ ├── debounced_event.rs │ │ ├── lib.rs │ │ └── testing.rs ├── profiling │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── profiling-procmacros │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── profiling │ │ ├── Cargo.toml │ │ ├── src │ │ │ └── lib.rs │ │ └── tests │ │ │ └── mod.rs │ └── rustfmt.toml └── self_update │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── LICENSE │ ├── build.rs │ ├── rustfmt.toml │ └── src │ ├── backends │ ├── github.rs │ └── mod.rs │ ├── errors.rs │ ├── lib.rs │ ├── macros.rs │ ├── update.rs │ └── version.rs ├── rustfmt.toml ├── scripts └── release ├── src ├── cli │ ├── build.rs │ ├── config.rs │ ├── debug.rs │ ├── doc.rs │ ├── exec.rs │ ├── init.rs │ ├── mod.rs │ ├── plugin.rs │ ├── serve.rs │ ├── sourcemap.rs │ ├── stop.rs │ ├── studio.rs │ └── update.rs ├── config.rs ├── constants.rs ├── core │ ├── changes.rs │ ├── helpers │ │ ├── migrations.rs │ │ ├── mod.rs │ │ └── syncback.rs │ ├── meta.rs │ ├── mod.rs │ ├── processor │ │ ├── mod.rs │ │ ├── read.rs │ │ └── write.rs │ ├── queue.rs │ ├── snapshot.rs │ └── tree.rs ├── crash_handler.rs ├── ext.rs ├── glob.rs ├── installer.rs ├── integration.rs ├── lib.rs ├── logger.rs ├── main.rs ├── middleware │ ├── csv.rs │ ├── data.rs │ ├── dir.rs │ ├── helpers │ │ ├── markdown.rs │ │ ├── mesh_part.rs │ │ ├── mod.rs │ │ └── snapshot.rs │ ├── json.rs │ ├── json_model.rs │ ├── luau.rs │ ├── md.rs │ ├── mod.rs │ ├── msgpack.rs │ ├── project.rs │ ├── rbxm.rs │ ├── rbxmx.rs │ ├── toml.rs │ ├── txt.rs │ └── yaml.rs ├── program.rs ├── project.rs ├── resolution.rs ├── server │ ├── details.rs │ ├── exec.rs │ ├── home.rs │ ├── mod.rs │ ├── open.rs │ ├── read.rs │ ├── snapshot.rs │ ├── stop.rs │ ├── subscribe.rs │ ├── unsubscribe.rs │ └── write.rs ├── sessions.rs ├── stats.rs ├── studio.rs ├── updater.rs ├── util.rs ├── vfs │ ├── debouncer.rs │ ├── mem_backend.rs │ ├── mod.rs │ └── std_backend.rs └── workspace.rs └── tests └── resolution.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: DervexDev 2 | ko_fi: Dervex 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | time: "06:00" 8 | open-pull-requests-limit: 40 9 | ignore: 10 | - dependency-name: "*" 11 | update-types: 12 | - "version-update:semver-major" 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | env: 13 | RUSTFLAGS: "-D warnings" 14 | 15 | jobs: 16 | build-test: 17 | name: Build and Test 18 | runs-on: ${{ matrix.os }} 19 | 20 | strategy: 21 | matrix: 22 | os: [ubuntu-latest, macos-latest, windows-latest] 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - name: Cache 28 | uses: Swatinem/rust-cache@v2 29 | 30 | - name: Build 31 | run: cargo build --verbose 32 | 33 | - name: Test 34 | run: cargo test --verbose 35 | 36 | lint-format: 37 | name: Lint and Format 38 | runs-on: ubuntu-latest 39 | 40 | steps: 41 | - uses: actions/checkout@v4 42 | 43 | - name: Cache 44 | uses: Swatinem/rust-cache@v2 45 | 46 | - name: Lint 47 | run: cargo clippy 48 | 49 | - name: Format 50 | run: cargo fmt -- --check 51 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: ["*"] 6 | 7 | jobs: 8 | bump: 9 | name: Bump Version 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | ref: main 16 | 17 | - name: Update Changelog 18 | uses: thomaseizinger/keep-a-changelog-new-release@v3 19 | with: 20 | tag: ${{ github.ref_name }} 21 | 22 | - name: Bump Cargo version 23 | id: version-bump 24 | uses: DervexDev/file-version-bumper@v1 25 | with: 26 | path: ./Cargo.toml 27 | 28 | - name: Update Cargo lock 29 | run: cargo update --workspace 30 | 31 | - name: Commit and Push 32 | uses: EndBug/add-and-commit@v9 33 | if: ${{ github.ref_name != steps.version-bump.outputs.old_version }} 34 | with: 35 | message: Bump version to ${{ github.ref_name }} 36 | default_author: github_actions 37 | 38 | - name: Update tag 39 | if: ${{ github.ref_name != steps.version-bump.outputs.old_version }} 40 | run: | 41 | git tag -fa ${{ github.ref_name }} -m "Release ${{ github.ref_name }}" 42 | git push -f --tags 43 | 44 | draft-release: 45 | name: Draft Release 46 | runs-on: ubuntu-latest 47 | needs: bump 48 | 49 | outputs: 50 | upload_url: ${{ steps.create-release.outputs.upload_url }} 51 | release_id: ${{ steps.create-release.outputs.id }} 52 | 53 | steps: 54 | - uses: actions/checkout@v4 55 | with: 56 | ref: main 57 | 58 | - name: Read Changelog 59 | id: read-changes 60 | uses: mindsers/changelog-reader-action@v2 61 | with: 62 | version: ${{ github.ref_name }} 63 | 64 | - name: Get previous Tag 65 | id: previous-tag 66 | uses: WyriHaximus/github-action-get-previous-tag@v1 67 | 68 | - name: Create Release 69 | id: create-release 70 | uses: shogo82148/actions-create-release@v1 71 | with: 72 | release_name: ${{ github.ref_name }} 73 | body: | 74 | ## Changelog 75 | ${{ steps.read-changes.outputs.changes }} 76 | prerelease: ${{ contains(github.ref_name, 'pre') }} 77 | notes_start_tag: ${{ steps.previous-tag.outputs.tag }} 78 | generate_release_notes: true 79 | commitish: main 80 | draft: true 81 | 82 | build: 83 | name: Build (${{ matrix.label }}) 84 | runs-on: ${{ matrix.os }} 85 | needs: draft-release 86 | 87 | strategy: 88 | matrix: 89 | include: 90 | # x86_64 91 | - host: linux 92 | os: ubuntu-22.04 93 | target: x86_64-unknown-linux-gnu 94 | label: linux-x86_64 95 | 96 | - host: macos 97 | os: macos-latest 98 | target: x86_64-apple-darwin 99 | label: macos-x86_64 100 | 101 | - host: windows 102 | os: windows-latest 103 | target: x86_64-pc-windows-msvc 104 | label: windows-x86_64 105 | 106 | # aarch64 107 | - host: macos 108 | os: macos-latest 109 | target: aarch64-apple-darwin 110 | label: macos-aarch64 111 | 112 | steps: 113 | - uses: actions/checkout@v4 114 | with: 115 | ref: main 116 | 117 | - name: Setup Rust 118 | uses: hecrj/setup-rust-action@v2 119 | with: 120 | targets: ${{ matrix.target }} 121 | 122 | - name: Build 123 | run: cargo build --all-features --release --verbose --target ${{ matrix.target }} 124 | env: 125 | ARGON_TOKEN: ${{ secrets.ARGON_TOKEN }} 126 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 127 | 128 | - name: Archive 129 | shell: bash 130 | run: | 131 | mkdir release 132 | 133 | if [ "${{ matrix.host }}" = "windows" ]; then 134 | cp "target/${{ matrix.target }}/release/argon.exe" release/ 135 | cd release 136 | 7z a ../release.zip * 137 | else 138 | cp "target/${{ matrix.target }}/release/argon" release/ 139 | cd release 140 | zip ../release.zip * 141 | fi 142 | 143 | - name: Upload to Artifacts 144 | uses: actions/upload-artifact@v4 145 | with: 146 | name: argon-${{ github.ref_name }}-${{ matrix.label }}.zip 147 | path: release.zip 148 | 149 | - name: Upload to Release 150 | uses: shogo82148/actions-upload-release-asset@v1 151 | with: 152 | upload_url: ${{ needs.draft-release.outputs.upload_url }} 153 | asset_name: argon-${{ github.ref_name }}-${{ matrix.label }}.zip 154 | asset_path: release.zip 155 | 156 | publish-release: 157 | name: Publish Release 158 | runs-on: ubuntu-latest 159 | needs: [build, draft-release] 160 | 161 | steps: 162 | - name: Publish on GitHub 163 | uses: eregon/publish-release@v1 164 | env: 165 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 166 | with: 167 | release_id: ${{ needs.draft-release.outputs.release_id }} 168 | 169 | # - name: Publish on crates.io 170 | # run: cargo publish 171 | # env: 172 | # CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 173 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /target 3 | /sourcemap.json 4 | *.rbxm 5 | *.rbxmx 6 | *.rbxl 7 | *.rbxlx 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "argon-rbx" 3 | authors = ["Dervex"] 4 | description = "Full featured tool for Roblox development" 5 | repository = "https://github.com/argon-rbx/argon" 6 | documentation = "https://argon.wiki/docs" 7 | homepage = "https://argon.wiki" 8 | license = "Apache-2.0" 9 | version = "2.0.24" 10 | edition = "2021" 11 | build = "build.rs" 12 | 13 | [[bin]] 14 | name = "argon" 15 | path = "src/main.rs" 16 | 17 | [lib] 18 | name = "argon" 19 | path = "src/lib.rs" 20 | 21 | [features] 22 | plugin = [] 23 | 24 | [dependencies] 25 | rbx_xml = "1.0.0" 26 | rbx_binary = "1.0.0" 27 | rbx_dom_weak = "3.0.0" 28 | rbx_reflection = "5.0.0" 29 | rbx_reflection_database = "1.0.3" 30 | 31 | config-derive = { version = "*", path = "crates/config-derive" } 32 | json-formatter = { version = "*", path = "crates/json-formatter" } 33 | profiling = { version = "*", path = "crates/profiling/profiling" } 34 | 35 | uuid = { version = "1.17.0", features = ["v4", "fast-rng"] } 36 | serde = { version = "1.0.219", features = ["derive"] } 37 | rmpv = { version = "1.3.0", features = ["with-serde"] } 38 | clap = { version = "4.5.40", features = ["derive", "cargo"] } 39 | reqwest = { version = "0.12.18", default-features = false, features = [ 40 | "blocking", 41 | "rustls-tls", 42 | "json", 43 | ] } 44 | self_update = { version = "0.39.0", default-features = false, features = [ 45 | "compression-zip-deflate", 46 | "rustls", 47 | ] } 48 | 49 | notify-debouncer-full = "0.3.1" 50 | clap-verbosity-flag = "2.2.3" 51 | crossbeam-channel = "0.5.15" 52 | derive-from-one = "0.1.0" 53 | roblox_install = "1.0.0" 54 | panic-message = "0.3.0" 55 | actix-msgpack = "0.1.4" 56 | puffin_http = "0.16.0" 57 | serde_json = "1.0.140" 58 | env_logger = "0.11.6" 59 | include_dir = "0.7.4" 60 | directories = "5.0.1" 61 | lazy_static = "1.5.0" 62 | backtrace = "0.3.75" 63 | documented = "0.9.1" 64 | dialoguer = "0.11.0" 65 | path-clean = "1.0.1" 66 | rmp-serde = "1.3.0" 67 | actix-web = "4.11.0" 68 | multimap = "0.10.1" 69 | optfield = "0.4.0" 70 | markdown = "0.3.0" 71 | 72 | json2lua = "0.1.3" 73 | toml2lua = "0.1.0" 74 | yaml2lua = "0.1.1" 75 | globenv = "0.2.1" 76 | 77 | puffin = "0.19.0" 78 | colored = "2.2.0" 79 | anyhow = "1.0.98" 80 | chrono = "0.4.41" 81 | notify = "6.1.1" 82 | whoami = "1.6.0" 83 | trash = "5.2.2" 84 | ctrlc = "3.4.7" 85 | toml = "0.8.22" 86 | glob = "0.3.2" 87 | open = "5.3.2" 88 | log = "0.4.27" 89 | csv = "1.3.1" 90 | 91 | [target.'cfg(not(target_os = "linux"))'.dependencies] 92 | keybd_event = "0.1.2" 93 | 94 | [target.'cfg(target_os = "windows")'.dependencies] 95 | winsafe = { version = "0.0.23", features = ["user"] } 96 | 97 | [build-dependencies] 98 | anyhow = "1.0.98" 99 | self_update = { version = "0.39.0", default-features = false, features = [ 100 | "rustls", 101 | ] } 102 | 103 | [dev-dependencies] 104 | approx = "0.5.1" 105 | 106 | [patch.crates-io] 107 | notify-debouncer-full = { path = "crates/notify-debouncer-full" } 108 | self_update = { path = "crates/self_update" } 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | Argon 3 | Full featured tool for Roblox development 4 |
5 | 6 | # Argon 7 | 8 | Argon is a powerful CLI that improves Roblox development experience. This is core part of Argon project as this is where all the processing happens. [Argon VS Code](https://github.com/argon-rbx/argon-vscode) extension is user-friendly wrapper of this CLI and [Argon Roblox](https://github.com/argon-rbx/argon-roblox) is a Roblox Studio plugin that is required for live syncing. 9 | 10 | Some of the key features of Argon: 11 | 12 | - Two-Way sync of code and other instances with their properties 13 | - Building projects in Roblox binary or XML format 14 | - Beginner-friendly and professional at the same time 15 | - Extremely customizable and fast 16 | - Lots of useful helper commands 17 | - Workflow automation (CI/CD) 18 | 19 | ## Visit [argon.wiki](https://argon.wiki/) to learn more! 20 | 21 | Or follow one of these direct links to: 22 | 23 | - [Install](https://argon.wiki/docs/installation) Argon 24 | - [Get Started](https://argon.wiki/docs/category/getting-started) with Argon 25 | - Learn about Argon [Commands](https://argon.wiki/docs/category/commands) 26 | - Explore the Argon [API](https://argon.wiki/api/project) 27 | - Follow the latest [Changes](https://argon.wiki/changelog/argon) 28 | -------------------------------------------------------------------------------- /assets/templates/empty/.gitignore: -------------------------------------------------------------------------------- 1 | # Argon 2 | /sourcemap.json 3 | 4 | # Wally 5 | /Packages 6 | /ServerPackages 7 | /DevPackages 8 | 9 | # Artifacts 10 | /*.lock 11 | /*.rbxl 12 | /*.rbxlx 13 | /*.rbxm 14 | /*.rbxmx 15 | 16 | # Misc 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /assets/templates/empty/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$name", 3 | "tree": { 4 | "$className": "DataModel", 5 | "Packages": { 6 | "$path": "Packages" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /assets/templates/empty/selene.toml: -------------------------------------------------------------------------------- 1 | std = "roblox" 2 | 3 | [lints] 4 | shadowing = "allow" 5 | -------------------------------------------------------------------------------- /assets/templates/empty/wally.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "$author/$name" 3 | version = "0.1.0" 4 | registry = "https://github.com/UpliftGames/wally-index" 5 | realm = "shared" 6 | private = true 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /assets/templates/model/.gitignore: -------------------------------------------------------------------------------- 1 | # Argon 2 | /sourcemap.json 3 | 4 | # Wally 5 | /Packages 6 | /ServerPackages 7 | /DevPackages 8 | 9 | # Artifacts 10 | /*.lock 11 | /*.rbxl 12 | /*.rbxlx 13 | /*.rbxm 14 | /*.rbxmx 15 | 16 | # Misc 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /assets/templates/model/LICENSE.md: -------------------------------------------------------------------------------- 1 | Licensed under $license license 2 | Copyright $year $owner 3 | -------------------------------------------------------------------------------- /assets/templates/model/README.md: -------------------------------------------------------------------------------- 1 | # $name 2 | 3 | Model template, generated by Argon 4 | 5 | ## Getting Started 6 | 7 | To build the model use: 8 | 9 | ```bash 10 | argon build 11 | ``` 12 | -------------------------------------------------------------------------------- /assets/templates/model/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$name", 3 | "tree": { 4 | "$path": "src", 5 | "Packages": { 6 | "$path": "Packages" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /assets/templates/model/selene.toml: -------------------------------------------------------------------------------- 1 | std = "roblox" 2 | 3 | [lints] 4 | shadowing = "allow" 5 | -------------------------------------------------------------------------------- /assets/templates/model/src/.src.luau: -------------------------------------------------------------------------------- 1 | return function() 2 | print('Hello world, from model!') 3 | end 4 | -------------------------------------------------------------------------------- /assets/templates/model/wally.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "$author/$name" 3 | version = "0.1.0" 4 | registry = "https://github.com/UpliftGames/wally-index" 5 | realm = "shared" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /assets/templates/package/.gitignore: -------------------------------------------------------------------------------- 1 | # Argon 2 | /sourcemap.json 3 | 4 | # Wally 5 | /Packages 6 | /ServerPackages 7 | /DevPackages 8 | 9 | # Artifacts 10 | /*.lock 11 | /*.rbxl 12 | /*.rbxlx 13 | /*.rbxm 14 | /*.rbxmx 15 | 16 | # Misc 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /assets/templates/package/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # $name Changelog 2 | 3 | ## Unreleased Changes 4 | 5 | - 6 | -------------------------------------------------------------------------------- /assets/templates/package/LICENSE.md: -------------------------------------------------------------------------------- 1 | Licensed under $license license 2 | Copyright $year $owner 3 | -------------------------------------------------------------------------------- /assets/templates/package/README.md: -------------------------------------------------------------------------------- 1 | # $name 2 | 3 | Package template, generated by Argon 4 | 5 | ## Getting Started 6 | 7 | To build the package use: 8 | 9 | ```bash 10 | argon build 11 | ``` 12 | -------------------------------------------------------------------------------- /assets/templates/package/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$name", 3 | "tree": { 4 | "$path": "src", 5 | "DevPackages": { 6 | "$path": "DevPackages" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /assets/templates/package/selene.toml: -------------------------------------------------------------------------------- 1 | std = "roblox" 2 | 3 | [lints] 4 | shadowing = "allow" 5 | -------------------------------------------------------------------------------- /assets/templates/package/src/init.luau: -------------------------------------------------------------------------------- 1 | return { 2 | hello = function() 3 | print('Hello world, from package!') 4 | end, 5 | } 6 | -------------------------------------------------------------------------------- /assets/templates/package/wally.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "$author/$name" 3 | description = "Enter your description here, generated by Argon" 4 | version = "0.1.0" 5 | license = "$license" 6 | authors = ["$author"] 7 | registry = "https://github.com/UpliftGames/wally-index" 8 | realm = "shared" 9 | 10 | [dev-dependencies] 11 | -------------------------------------------------------------------------------- /assets/templates/place/.gitignore: -------------------------------------------------------------------------------- 1 | # Argon 2 | /sourcemap.json 3 | 4 | # Wally 5 | /Packages 6 | /ServerPackages 7 | /DevPackages 8 | 9 | # Artifacts 10 | /*.lock 11 | /*.rbxl 12 | /*.rbxlx 13 | /*.rbxm 14 | /*.rbxmx 15 | 16 | # Misc 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /assets/templates/place/README.md: -------------------------------------------------------------------------------- 1 | # $name 2 | 3 | Game template, generated by Argon 4 | 5 | ## Getting Started 6 | 7 | To build the place in the project root use: 8 | 9 | ```bash 10 | argon build 11 | ``` 12 | 13 | To begin syncing open Roblox Studio and start Argon server using: 14 | 15 | ```bash 16 | argon serve 17 | ``` 18 | -------------------------------------------------------------------------------- /assets/templates/place/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$name", 3 | "tree": { 4 | "$className": "DataModel", 5 | "ReplicatedStorage": { 6 | "$path": "src/Shared", 7 | "Packages": { 8 | "$path": "Packages" 9 | } 10 | }, 11 | "ServerScriptService": { 12 | "$path": "src/Server", 13 | "ServerPackages": { 14 | "$path": "ServerPackages" 15 | } 16 | }, 17 | "StarterPlayer": { 18 | "StarterPlayerScripts": { 19 | "$path": "src/Client" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /assets/templates/place/selene.toml: -------------------------------------------------------------------------------- 1 | std = "roblox" 2 | 3 | [lints] 4 | shadowing = "allow" 5 | -------------------------------------------------------------------------------- /assets/templates/place/src/Client/Main.client.luau: -------------------------------------------------------------------------------- 1 | print('Hello world, from client!') 2 | -------------------------------------------------------------------------------- /assets/templates/place/src/Server/Main.server.luau: -------------------------------------------------------------------------------- 1 | print('Hello world, from server!') 2 | -------------------------------------------------------------------------------- /assets/templates/place/src/Shared/Hello.luau: -------------------------------------------------------------------------------- 1 | return function() 2 | print('Hello, world!') 3 | end 4 | -------------------------------------------------------------------------------- /assets/templates/place/wally.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "$author/$name" 3 | version = "0.1.0" 4 | registry = "https://github.com/UpliftGames/wally-index" 5 | realm = "shared" 6 | private = true 7 | 8 | [dependencies] 9 | 10 | [server-dependencies] 11 | -------------------------------------------------------------------------------- /assets/templates/plugin/.gitignore: -------------------------------------------------------------------------------- 1 | # Argon 2 | /sourcemap.json 3 | 4 | # Wally 5 | /Packages 6 | /ServerPackages 7 | /DevPackages 8 | 9 | # Artifacts 10 | /*.lock 11 | /*.rbxl 12 | /*.rbxlx 13 | /*.rbxm 14 | /*.rbxmx 15 | 16 | # Misc 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /assets/templates/plugin/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # $name Changelog 2 | 3 | ## Unreleased Changes 4 | 5 | - 6 | -------------------------------------------------------------------------------- /assets/templates/plugin/LICENSE.md: -------------------------------------------------------------------------------- 1 | Licensed under $license license 2 | Copyright $year $owner 3 | -------------------------------------------------------------------------------- /assets/templates/plugin/README.md: -------------------------------------------------------------------------------- 1 | # $name 2 | 3 | Plugin template, generated by Argon 4 | 5 | ## Getting Started 6 | 7 | To build the plugin and then import to Roblox Studio use: 8 | 9 | ```bash 10 | argon build -p 11 | ``` 12 | -------------------------------------------------------------------------------- /assets/templates/plugin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$name", 3 | "tree": { 4 | "$path": "src", 5 | "Packages": { 6 | "$path": "Packages" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /assets/templates/plugin/selene.toml: -------------------------------------------------------------------------------- 1 | std = "roblox" 2 | 3 | [lints] 4 | shadowing = "allow" 5 | -------------------------------------------------------------------------------- /assets/templates/plugin/src/.src.server.luau: -------------------------------------------------------------------------------- 1 | print('Hello world, from plugin!') 2 | -------------------------------------------------------------------------------- /assets/templates/plugin/wally.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "$author/$name" 3 | version = "0.1.0" 4 | registry = "https://github.com/UpliftGames/wally-index" 5 | realm = "shared" 6 | private = true 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /assets/templates/quick/.gitignore: -------------------------------------------------------------------------------- 1 | # Argon 2 | /sourcemap.json 3 | 4 | # Wally 5 | /Packages 6 | /ServerPackages 7 | /DevPackages 8 | 9 | # Artifacts 10 | /*.lock 11 | /*.rbxl 12 | /*.rbxlx 13 | /*.rbxm 14 | /*.rbxmx 15 | 16 | # Misc 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /assets/templates/quick/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$name", 3 | "tree": { 4 | "$className": "DataModel", 5 | "Workspace": { 6 | "$path": "src/Workspace" 7 | }, 8 | "ReplicatedFirst": { 9 | "$path": "src/ReplicatedFirst" 10 | }, 11 | "ReplicatedStorage": { 12 | "$path": "src/ReplicatedStorage", 13 | "Packages": { 14 | "$path": "Packages" 15 | } 16 | }, 17 | "ServerScriptService": { 18 | "$path": "src/ServerScriptService", 19 | "ServerPackages": { 20 | "$path": "ServerPackages" 21 | } 22 | }, 23 | "ServerStorage": { 24 | "$path": "src/ServerStorage" 25 | }, 26 | "StarterGui": { 27 | "$path": "src/StarterGui" 28 | }, 29 | "StarterPack": { 30 | "$path": "src/StarterPack" 31 | }, 32 | "StarterPlayer": { 33 | "StarterCharacterScripts": { 34 | "$path": "src/StarterPlayer/StarterCharacterScripts" 35 | }, 36 | "StarterPlayerScripts": { 37 | "$path": "src/StarterPlayer/StarterPlayerScripts" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /assets/templates/quick/selene.toml: -------------------------------------------------------------------------------- 1 | std = "roblox" 2 | 3 | [lints] 4 | shadowing = "allow" 5 | -------------------------------------------------------------------------------- /assets/templates/quick/src/ReplicatedFirst/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argon-rbx/argon/f516ba421631739ed4726e74dbdbdc5d17d6af70/assets/templates/quick/src/ReplicatedFirst/.gitkeep -------------------------------------------------------------------------------- /assets/templates/quick/src/ReplicatedStorage/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argon-rbx/argon/f516ba421631739ed4726e74dbdbdc5d17d6af70/assets/templates/quick/src/ReplicatedStorage/.gitkeep -------------------------------------------------------------------------------- /assets/templates/quick/src/ServerScriptService/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argon-rbx/argon/f516ba421631739ed4726e74dbdbdc5d17d6af70/assets/templates/quick/src/ServerScriptService/.gitkeep -------------------------------------------------------------------------------- /assets/templates/quick/src/ServerStorage/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argon-rbx/argon/f516ba421631739ed4726e74dbdbdc5d17d6af70/assets/templates/quick/src/ServerStorage/.gitkeep -------------------------------------------------------------------------------- /assets/templates/quick/src/StarterGui/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argon-rbx/argon/f516ba421631739ed4726e74dbdbdc5d17d6af70/assets/templates/quick/src/StarterGui/.gitkeep -------------------------------------------------------------------------------- /assets/templates/quick/src/StarterPack/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argon-rbx/argon/f516ba421631739ed4726e74dbdbdc5d17d6af70/assets/templates/quick/src/StarterPack/.gitkeep -------------------------------------------------------------------------------- /assets/templates/quick/src/StarterPlayer/StarterCharacterScripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argon-rbx/argon/f516ba421631739ed4726e74dbdbdc5d17d6af70/assets/templates/quick/src/StarterPlayer/StarterCharacterScripts/.gitkeep -------------------------------------------------------------------------------- /assets/templates/quick/src/StarterPlayer/StarterPlayerScripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argon-rbx/argon/f516ba421631739ed4726e74dbdbdc5d17d6af70/assets/templates/quick/src/StarterPlayer/StarterPlayerScripts/.gitkeep -------------------------------------------------------------------------------- /assets/templates/quick/src/Workspace/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argon-rbx/argon/f516ba421631739ed4726e74dbdbdc5d17d6af70/assets/templates/quick/src/Workspace/.gitkeep -------------------------------------------------------------------------------- /assets/templates/quick/wally.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "$author/$name" 3 | version = "0.1.0" 4 | registry = "https://github.com/UpliftGames/wally-index" 5 | realm = "shared" 6 | private = true 7 | 8 | [dependencies] 9 | 10 | [server-dependencies] 11 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use self_update::backends::github::Update; 3 | use std::{env, fs::File, path::PathBuf}; 4 | 5 | fn main() -> Result<()> { 6 | let out_path = PathBuf::from(env::var("OUT_DIR")?).join("Argon.rbxm"); 7 | 8 | if !cfg!(feature = "plugin") { 9 | File::create(out_path)?; 10 | return Ok(()); 11 | } 12 | 13 | let mut builder = Update::configure(); 14 | 15 | if let Ok(token) = env::var("GITHUB_TOKEN") { 16 | builder.auth_token(&token); 17 | } else { 18 | println!("cargo:warning=GITHUB_TOKEN not set, rate limits may apply!") 19 | } 20 | 21 | builder 22 | .repo_owner("argon-rbx") 23 | .repo_name("argon-roblox") 24 | .bin_name("Argon.rbxm") 25 | .bin_install_path(out_path) 26 | .target(""); 27 | 28 | builder 29 | .build()? 30 | .download() 31 | .context("Failed to download Argon plugin from GitHub!")?; 32 | 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /crates/config-derive/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /crates/config-derive/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "config-derive" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "proc-macro2", 10 | "quote", 11 | "syn", 12 | ] 13 | 14 | [[package]] 15 | name = "proc-macro2" 16 | version = "1.0.76" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" 19 | dependencies = [ 20 | "unicode-ident", 21 | ] 22 | 23 | [[package]] 24 | name = "quote" 25 | version = "1.0.35" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 28 | dependencies = [ 29 | "proc-macro2", 30 | ] 31 | 32 | [[package]] 33 | name = "syn" 34 | version = "2.0.48" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 37 | dependencies = [ 38 | "proc-macro2", 39 | "quote", 40 | "unicode-ident", 41 | ] 42 | 43 | [[package]] 44 | name = "unicode-ident" 45 | version = "1.0.12" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 48 | -------------------------------------------------------------------------------- /crates/config-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config-derive" 3 | authors = ["Dervex"] 4 | description = "Macros to speed up your Config struct processing" 5 | license = "Apache-2.0" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [lib] 10 | proc-macro = true 11 | 12 | [dependencies] 13 | proc-macro2 = "1.0.76" 14 | quote = "1.0.35" 15 | syn = "2.0.48" 16 | 17 | [dev-dependencies] 18 | serde = { version = "1.0.189", features = ["derive"] } 19 | -------------------------------------------------------------------------------- /crates/config-derive/rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /crates/config-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, TokenStream}; 2 | use quote::quote; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | 5 | mod util; 6 | 7 | #[proc_macro_derive(Val)] 8 | pub fn derive_val(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 9 | let input = parse_macro_input!(input as DeriveInput); 10 | 11 | let fields = util::get_fields(&input.data); 12 | 13 | let (variants, fmts, impls) = { 14 | let mut defined = vec![]; 15 | 16 | let mut variants = TokenStream::new(); 17 | let mut fmts = TokenStream::new(); 18 | let mut impls = TokenStream::new(); 19 | 20 | for field in fields { 21 | let ty = &field.ty; 22 | 23 | let ident = util::get_type_ident(ty).unwrap(); 24 | 25 | if defined.contains(&ident) { 26 | continue; 27 | } else { 28 | defined.push(ident.clone()); 29 | } 30 | 31 | let variant = { 32 | let variant = ident.to_string(); 33 | let variant = variant[0..1].to_uppercase() + &variant[1..]; 34 | Ident::new(&variant, ident.span()) 35 | }; 36 | 37 | variants.extend(quote! { 38 | #variant(#ty), 39 | }); 40 | 41 | fmts.extend(quote! { 42 | Value::#variant(v) => write!(f, "{}", v.to_string()), 43 | }); 44 | 45 | impls.extend(quote!( 46 | impl From<#ty> for Value { 47 | fn from(value: #ty) -> Self { 48 | Value::#variant(value) 49 | } 50 | } 51 | )) 52 | } 53 | 54 | (variants, fmts, impls) 55 | }; 56 | 57 | let expanded = quote! { 58 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 59 | #[serde(untagged)] 60 | pub enum Value { 61 | #variants 62 | } 63 | 64 | impl std::fmt::Display for Value { 65 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 66 | match self { 67 | #fmts 68 | } 69 | } 70 | } 71 | 72 | #impls 73 | }; 74 | 75 | proc_macro::TokenStream::from(expanded) 76 | } 77 | 78 | #[proc_macro_derive(Iter)] 79 | pub fn derive_iter(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 80 | let input = parse_macro_input!(input as DeriveInput); 81 | 82 | let name = &input.ident; 83 | let data = input.data; 84 | let fields = util::get_fields(&data); 85 | 86 | let arms = { 87 | let mut arms = TokenStream::new(); 88 | 89 | for (index, field) in fields.iter().enumerate() { 90 | let ident = field.ident.as_ref().unwrap().to_string(); 91 | 92 | arms.extend(quote! { 93 | #index => #ident, 94 | }); 95 | } 96 | 97 | arms 98 | }; 99 | 100 | let expanded = quote! { 101 | pub struct IntoIter<'a> { 102 | inner: &'a #name, 103 | index: usize, 104 | } 105 | 106 | impl<'a> Iterator for IntoIter<'a> { 107 | type Item = (&'a str, Value); 108 | 109 | fn next(&mut self) -> Option { 110 | let index = match self.index { 111 | #arms 112 | _ => return None, 113 | }; 114 | 115 | self.index += 1; 116 | 117 | Some((index, self.inner.get(index).unwrap())) 118 | } 119 | } 120 | 121 | impl<'a> IntoIterator for &'a #name { 122 | type Item = (&'a str, Value); 123 | type IntoIter = IntoIter<'a>; 124 | 125 | fn into_iter(self) -> Self::IntoIter { 126 | IntoIter { 127 | inner: self, 128 | index: 0, 129 | } 130 | } 131 | } 132 | }; 133 | 134 | proc_macro::TokenStream::from(expanded) 135 | } 136 | 137 | #[proc_macro_derive(Get)] 138 | pub fn derive_get(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 139 | let input = parse_macro_input!(input as DeriveInput); 140 | 141 | let name = &input.ident; 142 | let data = input.data; 143 | let fields = util::get_fields(&data); 144 | 145 | let arms = { 146 | let mut arms = TokenStream::new(); 147 | 148 | for field in fields { 149 | let ident = field.ident.as_ref().unwrap(); 150 | let index = ident.to_string(); 151 | 152 | arms.extend(quote! { 153 | #index => Some(self.#ident.clone().into()), 154 | }); 155 | } 156 | 157 | arms 158 | }; 159 | 160 | let expanded = quote! { 161 | impl #name { 162 | pub fn get(&self, index: &str) -> Option { 163 | match index { 164 | #arms 165 | _ => None, 166 | } 167 | } 168 | } 169 | }; 170 | 171 | proc_macro::TokenStream::from(expanded) 172 | } 173 | 174 | #[proc_macro_derive(Set)] 175 | pub fn derive_set(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 176 | let input = parse_macro_input!(input as DeriveInput); 177 | 178 | let name = &input.ident; 179 | let data = input.data; 180 | let fields = util::get_fields(&data); 181 | 182 | let arms = { 183 | let mut arms = TokenStream::new(); 184 | 185 | for field in fields { 186 | let ident = field.ident.as_ref().unwrap(); 187 | let index = ident.to_string(); 188 | 189 | arms.extend(quote! { 190 | #index => self.#ident = value.parse()?, 191 | }); 192 | } 193 | 194 | arms 195 | }; 196 | 197 | let expanded = quote! { 198 | impl #name { 199 | pub fn set(&mut self, index: &str, value: &str) -> Result<(), Box> { 200 | match index { 201 | #arms 202 | _ => return Err(format!("Field: {} does not exist", index).into()), 203 | } 204 | 205 | Ok(()) 206 | } 207 | } 208 | }; 209 | 210 | proc_macro::TokenStream::from(expanded) 211 | } 212 | -------------------------------------------------------------------------------- /crates/config-derive/src/util.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, TokenTree}; 2 | use quote::ToTokens; 3 | use syn::{Data, Field, Fields, Type}; 4 | 5 | pub fn get_fields(data: &Data) -> Vec<&Field> { 6 | match data { 7 | Data::Struct(data) => match &data.fields { 8 | Fields::Named(named) => { 9 | let mut fields = vec![]; 10 | 11 | 'outer: for field in &named.named { 12 | match field.ident { 13 | Some(_) => { 14 | if let Some(attr) = field.attrs.first() { 15 | for tree in attr.meta.to_token_stream() { 16 | if let TokenTree::Ident(ident) = tree { 17 | if ident == "serde" { 18 | continue 'outer; 19 | } 20 | } 21 | } 22 | } 23 | 24 | fields.push(field); 25 | } 26 | None => unimplemented!("Tuples are not supported"), 27 | } 28 | } 29 | 30 | fields 31 | } 32 | _ => unimplemented!("Only named fields are supported"), 33 | }, 34 | _ => { 35 | unimplemented!("Only flat structs are supported") 36 | } 37 | } 38 | } 39 | 40 | pub fn get_type_ident(ty: &Type) -> Option { 41 | for tree in ty.to_token_stream() { 42 | if let TokenTree::Ident(ident) = tree { 43 | return Some(ident); 44 | } 45 | } 46 | 47 | None 48 | } 49 | -------------------------------------------------------------------------------- /crates/config-derive/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use config_derive::{Get, Iter, Set, Val}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[test] 7 | fn it_works() { 8 | #[derive(Debug, Val, Iter, Get, Set)] 9 | struct Test { 10 | a: u16, 11 | b: i32, 12 | c: String, 13 | d: bool, 14 | e: String, 15 | } 16 | 17 | let mut test = Test { 18 | a: 1, 19 | b: 2, 20 | c: "hello".to_string(), 21 | d: true, 22 | e: "world".to_string(), 23 | }; 24 | 25 | println!("{:?}", test.get("c")); 26 | 27 | for (key, value) in &test { 28 | println!("{}: {:?}", key, value); 29 | } 30 | 31 | test.set("c", "goodbye").unwrap(); 32 | test.set("d", "false").unwrap(); 33 | 34 | println!("{:?}", test); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/json-formatter/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /crates/json-formatter/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "itoa" 7 | version = "1.0.15" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 10 | 11 | [[package]] 12 | name = "json-formatter" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "ryu", 16 | "serde_json", 17 | ] 18 | 19 | [[package]] 20 | name = "memchr" 21 | version = "2.7.5" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 24 | 25 | [[package]] 26 | name = "proc-macro2" 27 | version = "1.0.95" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 30 | dependencies = [ 31 | "unicode-ident", 32 | ] 33 | 34 | [[package]] 35 | name = "quote" 36 | version = "1.0.40" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 39 | dependencies = [ 40 | "proc-macro2", 41 | ] 42 | 43 | [[package]] 44 | name = "ryu" 45 | version = "1.0.20" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 48 | 49 | [[package]] 50 | name = "serde" 51 | version = "1.0.219" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 54 | dependencies = [ 55 | "serde_derive", 56 | ] 57 | 58 | [[package]] 59 | name = "serde_derive" 60 | version = "1.0.219" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 63 | dependencies = [ 64 | "proc-macro2", 65 | "quote", 66 | "syn", 67 | ] 68 | 69 | [[package]] 70 | name = "serde_json" 71 | version = "1.0.140" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 74 | dependencies = [ 75 | "itoa", 76 | "memchr", 77 | "ryu", 78 | "serde", 79 | ] 80 | 81 | [[package]] 82 | name = "syn" 83 | version = "2.0.104" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 86 | dependencies = [ 87 | "proc-macro2", 88 | "quote", 89 | "unicode-ident", 90 | ] 91 | 92 | [[package]] 93 | name = "unicode-ident" 94 | version = "1.0.18" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 97 | -------------------------------------------------------------------------------- /crates/json-formatter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "json-formatter" 3 | authors = ["Dervex"] 4 | description = "Custom serde JSON formatter" 5 | license = "Apache-2.0" 6 | version = "0.1.0" 7 | edition = "2021" 8 | 9 | [dependencies] 10 | ryu = "1.0.20" 11 | serde_json = "1.0.117" 12 | -------------------------------------------------------------------------------- /crates/json-formatter/rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /crates/json-formatter/src/lib.rs: -------------------------------------------------------------------------------- 1 | use serde_json::ser::Formatter; 2 | use std::io; 3 | 4 | macro_rules! tri { 5 | ($e:expr $(,)?) => { 6 | match $e { 7 | core::result::Result::Ok(val) => val, 8 | core::result::Result::Err(err) => return core::result::Result::Err(err), 9 | } 10 | }; 11 | } 12 | 13 | /// This structure pretty prints a JSON value to make it human readable. 14 | #[derive(Clone, Debug)] 15 | pub struct JsonFormatter<'a> { 16 | current_indent: usize, 17 | has_value: bool, 18 | indent: &'a [u8], 19 | array_breaks: bool, 20 | extra_newline: bool, 21 | max_decimals: usize, 22 | } 23 | 24 | impl<'a> JsonFormatter<'a> { 25 | /// Construct a pretty printer formatter that defaults to using two spaces for indentation. 26 | pub fn new() -> Self { 27 | JsonFormatter { 28 | current_indent: 0, 29 | has_value: false, 30 | indent: b" ", 31 | array_breaks: true, 32 | extra_newline: false, 33 | max_decimals: 0, 34 | } 35 | } 36 | 37 | /// Construct a pretty printer formatter that uses the `indent` string for indentation. 38 | pub fn with_indent(mut self, indent: &'a [u8]) -> Self { 39 | self.indent = indent; 40 | self 41 | } 42 | 43 | /// Construct a pretty printer formatter that optionally break arrays into multiple lines. 44 | pub fn with_array_breaks(mut self, array_breaks: bool) -> Self { 45 | self.array_breaks = array_breaks; 46 | self 47 | } 48 | 49 | /// Construct a pretty printer formatter that adds an extra newline at the end. 50 | pub fn with_extra_newline(mut self, extra_newline: bool) -> Self { 51 | self.extra_newline = extra_newline; 52 | self 53 | } 54 | 55 | /// Construct a pretty printer formatter that limits the number of decimal places. 56 | pub fn with_max_decimals(mut self, max_decimals: usize) -> Self { 57 | self.max_decimals = max_decimals; 58 | self 59 | } 60 | } 61 | 62 | impl<'a> Default for JsonFormatter<'a> { 63 | fn default() -> Self { 64 | JsonFormatter::new() 65 | } 66 | } 67 | 68 | impl<'a> Formatter for JsonFormatter<'a> { 69 | #[inline] 70 | fn write_f64(&mut self, writer: &mut W, mut value: f64) -> io::Result<()> 71 | where 72 | W: ?Sized + io::Write, 73 | { 74 | if self.max_decimals > 0 { 75 | let multiplier = 10_f64.powi(self.max_decimals as i32); 76 | value = (value * multiplier).round() / multiplier; 77 | } 78 | 79 | let mut buffer = ryu::Buffer::new(); 80 | let s = buffer.format_finite(value); 81 | writer.write_all(s.as_bytes()) 82 | } 83 | 84 | #[inline] 85 | fn begin_array(&mut self, writer: &mut W) -> io::Result<()> 86 | where 87 | W: ?Sized + io::Write, 88 | { 89 | self.current_indent += 1; 90 | self.has_value = false; 91 | writer.write_all(b"[") 92 | } 93 | 94 | #[inline] 95 | fn end_array(&mut self, writer: &mut W) -> io::Result<()> 96 | where 97 | W: ?Sized + io::Write, 98 | { 99 | self.current_indent -= 1; 100 | 101 | if self.has_value && self.array_breaks { 102 | tri!(writer.write_all(b"\n")); 103 | tri!(indent(writer, self.current_indent, self.indent)); 104 | } 105 | 106 | writer.write_all(b"]") 107 | } 108 | 109 | #[inline] 110 | fn begin_array_value(&mut self, writer: &mut W, first: bool) -> io::Result<()> 111 | where 112 | W: ?Sized + io::Write, 113 | { 114 | if !self.array_breaks { 115 | if !first { 116 | tri!(writer.write_all(b", ")); 117 | } 118 | 119 | return Ok(()); 120 | } 121 | 122 | tri!(writer.write_all(if first { b"\n" } else { b",\n" })); 123 | indent(writer, self.current_indent, self.indent) 124 | } 125 | 126 | #[inline] 127 | fn end_array_value(&mut self, _writer: &mut W) -> io::Result<()> 128 | where 129 | W: ?Sized + io::Write, 130 | { 131 | self.has_value = true; 132 | Ok(()) 133 | } 134 | 135 | #[inline] 136 | fn begin_object(&mut self, writer: &mut W) -> io::Result<()> 137 | where 138 | W: ?Sized + io::Write, 139 | { 140 | self.current_indent += 1; 141 | self.has_value = false; 142 | writer.write_all(b"{") 143 | } 144 | 145 | #[inline] 146 | fn end_object(&mut self, writer: &mut W) -> io::Result<()> 147 | where 148 | W: ?Sized + io::Write, 149 | { 150 | self.current_indent -= 1; 151 | 152 | if self.has_value { 153 | tri!(writer.write_all(b"\n")); 154 | tri!(indent(writer, self.current_indent, self.indent)); 155 | } 156 | 157 | tri!(writer.write_all(b"}")); 158 | 159 | if self.current_indent == 0 && self.extra_newline { 160 | writer.write_all(b"\n") 161 | } else { 162 | Ok(()) 163 | } 164 | } 165 | 166 | #[inline] 167 | fn begin_object_key(&mut self, writer: &mut W, first: bool) -> io::Result<()> 168 | where 169 | W: ?Sized + io::Write, 170 | { 171 | tri!(writer.write_all(if first { b"\n" } else { b",\n" })); 172 | indent(writer, self.current_indent, self.indent) 173 | } 174 | 175 | #[inline] 176 | fn begin_object_value(&mut self, writer: &mut W) -> io::Result<()> 177 | where 178 | W: ?Sized + io::Write, 179 | { 180 | writer.write_all(b": ") 181 | } 182 | 183 | #[inline] 184 | fn end_object_value(&mut self, _writer: &mut W) -> io::Result<()> 185 | where 186 | W: ?Sized + io::Write, 187 | { 188 | self.has_value = true; 189 | Ok(()) 190 | } 191 | } 192 | 193 | fn indent(wr: &mut W, n: usize, s: &[u8]) -> io::Result<()> 194 | where 195 | W: ?Sized + io::Write, 196 | { 197 | for _ in 0..n { 198 | tri!(wr.write_all(s)); 199 | } 200 | 201 | Ok(()) 202 | } 203 | -------------------------------------------------------------------------------- /crates/notify-debouncer-full/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /crates/notify-debouncer-full/Cargo.toml: -------------------------------------------------------------------------------- 1 | # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO 2 | # 3 | # When uploading crates to the registry Cargo will automatically 4 | # "normalize" Cargo.toml files for maximal compatibility 5 | # with all versions of Cargo and also rewrite `path` dependencies 6 | # to registry (e.g., crates.io) dependencies. 7 | # 8 | # If you are reading this file be aware that the original Cargo.toml 9 | # will likely look very different (and much more reasonable). 10 | # See Cargo.toml.orig for the original contents. 11 | 12 | [package] 13 | edition = "2021" 14 | rust-version = "1.60" 15 | name = "notify-debouncer-full" 16 | version = "0.3.1" 17 | authors = ["Daniel Faust "] 18 | description = "notify event debouncer optimized for ease of use" 19 | homepage = "https://github.com/notify-rs/notify" 20 | documentation = "https://docs.rs/notify-debouncer-full" 21 | readme = "README.md" 22 | keywords = [ 23 | "events", 24 | "filesystem", 25 | "notify", 26 | "watch", 27 | ] 28 | license = "MIT OR Apache-2.0" 29 | repository = "https://github.com/notify-rs/notify.git" 30 | resolver = "1" 31 | 32 | [lib] 33 | name = "notify_debouncer_full" 34 | path = "src/lib.rs" 35 | 36 | [dependencies.crossbeam-channel] 37 | version = "0.5" 38 | optional = true 39 | 40 | [dependencies.file-id] 41 | version = "0.2.1" 42 | 43 | [dependencies.log] 44 | version = "0.4.17" 45 | 46 | [dependencies.notify] 47 | version = "6.1.1" 48 | 49 | [dependencies.parking_lot] 50 | version = "0.12.1" 51 | 52 | [dependencies.walkdir] 53 | version = "2.2.2" 54 | 55 | [dev-dependencies.deser-hjson] 56 | version = "1.1.1" 57 | 58 | [dev-dependencies.mock_instant] 59 | version = "0.3.0" 60 | 61 | [dev-dependencies.pretty_assertions] 62 | version = "1.3.0" 63 | 64 | [dev-dependencies.rand] 65 | version = "0.8.5" 66 | 67 | [dev-dependencies.rstest] 68 | version = "0.17.0" 69 | 70 | [dev-dependencies.serde] 71 | version = "1.0.89" 72 | features = ["derive"] 73 | 74 | [features] 75 | crossbeam = [ 76 | "crossbeam-channel", 77 | "notify/crossbeam-channel", 78 | ] 79 | default = ["crossbeam"] 80 | -------------------------------------------------------------------------------- /crates/notify-debouncer-full/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Notify Contributors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /crates/notify-debouncer-full/rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /crates/notify-debouncer-full/src/cache.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | path::{Path, PathBuf}, 4 | }; 5 | 6 | use file_id::{get_file_id, FileId}; 7 | use notify::RecursiveMode; 8 | use walkdir::WalkDir; 9 | 10 | /// The interface of a file ID cache. 11 | /// 12 | /// This trait can be implemented for an existing cache, if it already holds `FileId`s. 13 | pub trait FileIdCache { 14 | /// Get a `FileId` from the cache for a given `path`. 15 | /// 16 | /// If the path is not cached, `None` should be returned and there should not be any attempt to read the file ID from disk. 17 | fn cached_file_id(&self, path: &Path) -> Option<&FileId>; 18 | 19 | /// Add a new path to the cache or update its value. 20 | /// 21 | /// This will be called if a new file or directory is created or if an existing file is overridden. 22 | fn add_path(&mut self, path: &Path); 23 | 24 | /// Remove a path from the cache. 25 | /// 26 | /// This will be called if a file or directory is deleted. 27 | fn remove_path(&mut self, path: &Path); 28 | 29 | /// Re-scan all paths. 30 | /// 31 | /// This will be called if the notification back-end has dropped events. 32 | fn rescan(&mut self); 33 | } 34 | 35 | /// A cache to hold the file system IDs of all watched files. 36 | /// 37 | /// The file ID cache uses unique file IDs provided by the file system and is used to stich together 38 | /// rename events in case the notification back-end doesn't emit rename cookies. 39 | #[derive(Debug, Clone, Default)] 40 | pub struct FileIdMap { 41 | paths: HashMap, 42 | roots: Vec<(PathBuf, RecursiveMode)>, 43 | } 44 | 45 | impl FileIdMap { 46 | /// Construct an empty cache. 47 | pub fn new() -> Self { 48 | Default::default() 49 | } 50 | 51 | /// Add a path to the cache. 52 | /// 53 | /// If `recursive_mode` is `Recursive`, all children will be added to the cache as well 54 | /// and all paths will be kept up-to-date in case of changes like new files being added, 55 | /// files being removed or renamed. 56 | pub fn add_root(&mut self, path: impl Into, recursive_mode: RecursiveMode) { 57 | let path = path.into(); 58 | 59 | self.roots.push((path.clone(), recursive_mode)); 60 | 61 | self.add_path(&path); 62 | } 63 | 64 | /// Remove a path form the cache. 65 | /// 66 | /// If the path was added with `Recursive` mode, all children will also be removed from the cache. 67 | pub fn remove_root(&mut self, path: impl AsRef) { 68 | self.roots.retain(|(root, _)| !root.starts_with(&path)); 69 | 70 | self.remove_path(path.as_ref()); 71 | } 72 | 73 | fn dir_scan_depth(is_recursive: bool) -> usize { 74 | if is_recursive { 75 | usize::max_value() 76 | } else { 77 | 1 78 | } 79 | } 80 | } 81 | 82 | impl FileIdCache for FileIdMap { 83 | fn cached_file_id(&self, path: &Path) -> Option<&FileId> { 84 | self.paths.get(path) 85 | } 86 | 87 | fn add_path(&mut self, path: &Path) { 88 | let is_recursive = self 89 | .roots 90 | .iter() 91 | .find_map(|(root, recursive_mode)| { 92 | if path.starts_with(root) { 93 | Some(*recursive_mode == RecursiveMode::Recursive) 94 | } else { 95 | None 96 | } 97 | }) 98 | .unwrap_or_default(); 99 | 100 | for (path, file_id) in WalkDir::new(path) 101 | .follow_links(true) 102 | .max_depth(Self::dir_scan_depth(is_recursive)) 103 | .into_iter() 104 | .filter_map(|entry| { 105 | let path = entry.ok()?.into_path(); 106 | let file_id = get_file_id(&path).ok()?; 107 | Some((path, file_id)) 108 | }) { 109 | self.paths.insert(path, file_id); 110 | } 111 | } 112 | 113 | fn remove_path(&mut self, path: &Path) { 114 | self.paths.retain(|p, _| !p.starts_with(path)); 115 | } 116 | 117 | fn rescan(&mut self) { 118 | for (root, _) in self.roots.clone() { 119 | self.add_path(&root); 120 | } 121 | } 122 | } 123 | 124 | /// An implementation of the `FileIdCache` trait that doesn't hold any data. 125 | /// 126 | /// This pseudo cache can be used to disable the file tracking using file system IDs. 127 | pub struct NoCache; 128 | 129 | impl FileIdCache for NoCache { 130 | fn cached_file_id(&self, _path: &Path) -> Option<&FileId> { 131 | None 132 | } 133 | 134 | fn add_path(&mut self, _path: &Path) {} 135 | 136 | fn remove_path(&mut self, _path: &Path) {} 137 | 138 | fn rescan(&mut self) {} 139 | } 140 | -------------------------------------------------------------------------------- /crates/notify-debouncer-full/src/debounced_event.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | #[cfg(test)] 4 | use mock_instant::Instant; 5 | 6 | #[cfg(not(test))] 7 | use std::time::Instant; 8 | 9 | use notify::Event; 10 | 11 | /// A debounced event is emitted after a short delay. 12 | #[derive(Debug, Clone, PartialEq, Eq)] 13 | pub struct DebouncedEvent { 14 | /// The original event. 15 | pub event: Event, 16 | 17 | /// The time at which the event occurred. 18 | pub time: Instant, 19 | } 20 | 21 | impl DebouncedEvent { 22 | pub fn new(event: Event, time: Instant) -> Self { 23 | Self { event, time } 24 | } 25 | } 26 | 27 | impl Deref for DebouncedEvent { 28 | type Target = Event; 29 | 30 | fn deref(&self) -> &Self::Target { 31 | &self.event 32 | } 33 | } 34 | 35 | impl DerefMut for DebouncedEvent { 36 | fn deref_mut(&mut self) -> &mut Self::Target { 37 | &mut self.event 38 | } 39 | } 40 | 41 | impl Default for DebouncedEvent { 42 | fn default() -> Self { 43 | Self { 44 | event: Default::default(), 45 | time: Instant::now(), 46 | } 47 | } 48 | } 49 | 50 | impl From for DebouncedEvent { 51 | fn from(event: Event) -> Self { 52 | Self { 53 | event, 54 | time: Instant::now(), 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /crates/profiling/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /crates/profiling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["profiling", "profiling-procmacros", "."] 3 | -------------------------------------------------------------------------------- /crates/profiling/profiling-procmacros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "profiling-procmacros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | proc-macro = true 8 | 9 | [dependencies] 10 | syn = { version = "2.0.48", features = ["full"] } 11 | proc-macro2 = "1.0.76" 12 | quote = "1.0.35" 13 | -------------------------------------------------------------------------------- /crates/profiling/profiling-procmacros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{quote, ToTokens}; 3 | use syn::{parse, parse_macro_input, Item, LitStr}; 4 | 5 | #[proc_macro_attribute] 6 | pub fn function(attr: TokenStream, item: TokenStream) -> TokenStream { 7 | let mut item = parse_macro_input!(item as Item); 8 | 9 | let item_fn = match &mut item { 10 | Item::Fn(item_fn) => item_fn, 11 | _ => panic!("expected function"), 12 | }; 13 | 14 | let data = parse_macro_input!(attr as Option); 15 | let puffin_macro = quote! { 16 | puffin::profile_function!(#data); 17 | }; 18 | 19 | item_fn 20 | .block 21 | .stmts 22 | .insert(0, parse(puffin_macro.into()).unwrap()); 23 | 24 | item.into_token_stream().into() 25 | } 26 | -------------------------------------------------------------------------------- /crates/profiling/profiling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "profiling" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | profiling-procmacros = { path = "../profiling-procmacros" } 8 | 9 | [dev-dependencies] 10 | puffin_http = "0.16.0" 11 | puffin = "0.19.0" 12 | -------------------------------------------------------------------------------- /crates/profiling/profiling/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use ::profiling_procmacros::function; 2 | 3 | #[macro_export] 4 | macro_rules! scope { 5 | ($name:expr) => { 6 | puffin::profile_scope!($name); 7 | }; 8 | ($name:expr, $data:expr) => { 9 | puffin::profile_scope!($name, $data); 10 | }; 11 | } 12 | 13 | #[macro_export] 14 | macro_rules! start_frame { 15 | () => { 16 | puffin::GlobalProfiler::lock().new_frame(); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /crates/profiling/profiling/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use std::{thread::sleep, time::Duration}; 4 | 5 | #[test] 6 | fn it_works() { 7 | let _server = puffin_http::Server::new("localhost:8888").unwrap(); 8 | puffin::set_scopes_on(true); 9 | 10 | #[profiling::function] 11 | fn test() { 12 | let mut vec = vec![]; 13 | 14 | for i in 0..100 { 15 | profiling::scope!("scope1"); 16 | vec.push(i); 17 | 18 | for i in 0..200 { 19 | profiling::scope!("scope2", "with data"); 20 | vec.push(i); 21 | } 22 | } 23 | } 24 | 25 | #[profiling::function("with data")] 26 | fn test_data() { 27 | let mut vec = vec![]; 28 | 29 | for i in 0..100 { 30 | profiling::scope!("scope3", i.to_string()); 31 | vec.push(i); 32 | } 33 | } 34 | 35 | loop { 36 | profiling::start_frame!(); 37 | 38 | test(); 39 | test_data(); 40 | 41 | sleep(Duration::from_millis(100)); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/profiling/rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /crates/self_update/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /crates/self_update/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "self_update" 3 | version = "0.39.0" 4 | description = "Self updates for standalone executables" 5 | repository = "https://github.com/jaemk/self_update" 6 | keywords = ["update", "upgrade", "download", "release"] 7 | categories = ["command-line-utilities"] 8 | license = "MIT" 9 | readme = "README.md" 10 | authors = ["James Kominick "] 11 | exclude = ["/ci/*", ".travis.yml", "appveyor.yml"] 12 | edition = "2018" 13 | rust = "1.64" 14 | 15 | [dependencies] 16 | serde_json = "1" 17 | tempfile = "3" 18 | flate2 = { version = "1", optional = true } 19 | tar = { version = "0.4", optional = true } 20 | semver = "1.0" 21 | zip = { version = "0.6", default-features = false, features = ["time"], optional = true } 22 | either = { version = "1", optional = true } 23 | reqwest = { version = "0.11", default-features = false, features = ["blocking", "json"] } 24 | hyper = "0.14" 25 | indicatif = "0.17" 26 | quick-xml = "0.23" 27 | regex = "1" 28 | log = "0.4" 29 | urlencoding = "2.1" 30 | self-replace = "1" 31 | zipsign-api = { version = "0.1.0-a.3", default-features = false, optional = true } 32 | 33 | [features] 34 | default = ["reqwest/default-tls"] 35 | archive-zip = ["zip", "zipsign-api?/verify-zip"] 36 | compression-zip-bzip2 = ["archive-zip", "zip/bzip2"] 37 | compression-zip-deflate = ["archive-zip", "zip/deflate"] 38 | archive-tar = ["tar", "zipsign-api?/verify-tar"] 39 | compression-flate2 = ["archive-tar", "flate2", "either"] 40 | rustls = ["reqwest/rustls-tls"] 41 | signatures = ["dep:zipsign-api"] 42 | 43 | [package.metadata.docs.rs] 44 | # Whether to pass `--all-features` to Cargo (default: false) 45 | all-features = true 46 | -------------------------------------------------------------------------------- /crates/self_update/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 James Kominick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /crates/self_update/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!( 3 | "cargo:rustc-env=TARGET={}", 4 | std::env::var("TARGET").unwrap() 5 | ); 6 | } 7 | -------------------------------------------------------------------------------- /crates/self_update/rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | -------------------------------------------------------------------------------- /crates/self_update/src/backends/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Collection of modules supporting various release distribution backends 3 | */ 4 | 5 | pub mod github; 6 | 7 | /// Search for the first "rel" link-header uri in a full link header string. 8 | /// Seems like reqwest/hyper threw away their link-header parser implementation... 9 | /// 10 | /// ex: 11 | /// `Link: ; rel="next"` 12 | /// `Link: ; rel="next"` 13 | /// 14 | /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link 15 | /// header values may contain multiple values separated by commas 16 | /// `Link: ; rel="next", ; rel="next"` 17 | pub(crate) fn find_rel_next_link(link_str: &str) -> Option<&str> { 18 | for link in link_str.split(',') { 19 | let mut uri = None; 20 | let mut is_rel_next = false; 21 | for part in link.split(';') { 22 | let part = part.trim(); 23 | if part.starts_with('<') && part.ends_with('>') { 24 | uri = Some(part.trim_start_matches('<').trim_end_matches('>')); 25 | } else if part.starts_with("rel=") { 26 | let part = part 27 | .trim_start_matches("rel=") 28 | .trim_end_matches('"') 29 | .trim_start_matches('"'); 30 | if part == "next" { 31 | is_rel_next = true; 32 | } 33 | } 34 | 35 | if is_rel_next && uri.is_some() { 36 | return uri; 37 | } 38 | } 39 | } 40 | None 41 | } 42 | 43 | #[cfg(test)] 44 | mod test { 45 | use crate::backends::find_rel_next_link; 46 | 47 | #[test] 48 | fn test_find_rel_link() { 49 | let val = r##" ; rel="next" "##; 50 | let link = find_rel_next_link(val); 51 | assert_eq!(link, Some("https://api.github.com/resource?page=2")); 52 | 53 | let val = r##" ; rel="next" "##; 54 | let link = find_rel_next_link(val); 55 | assert_eq!( 56 | link, 57 | Some("https://gitlab.com/api/v4/projects/13083/releases?id=13083&page=2&per_page=20") 58 | ); 59 | 60 | // returns the first one 61 | let val = r##" ; rel="next", ; rel="next" "##; 62 | let link = find_rel_next_link(val); 63 | assert_eq!(link, Some("https://place.com")); 64 | 65 | // bad format, returns the second one 66 | let val = r##" https://bad-format.com; rel="next", ; rel="next" "##; 67 | let link = find_rel_next_link(val); 68 | assert_eq!(link, Some("https://wow.com")); 69 | 70 | // all bad format, returns none 71 | let val = r##" https://bad-format.com; rel="next",