diff --git a/.github/workflows/cargo-bench.yml b/.github/workflows/cargo-bench.yml new file mode 100644 index 00000000..1be22bbf --- /dev/null +++ b/.github/workflows/cargo-bench.yml @@ -0,0 +1,55 @@ +name: Benchmark + +concurrency: + cancel-in-progress: false + group: ${{ github.workflow }}-${{ github.ref }} + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + +on: + repository_dispatch: + types: [ cargo-bench, benchmark ] + workflow_dispatch: + +permissions: + checks: write + contents: write + +jobs: + benchmark: + runs-on: ubuntu-latest + outputs: + digest: ${{ steps.artifacts.outputs.artifact-digest }} + id: ${{ steps.artifacts.outputs.artifact-id }} + url: ${{ steps.artifacts.outputs.artifact-url }} + strategy: + fail-fast: false + matrix: + target: [ x86_64-unknown-linux-gnu ] + toolchain: [ stable ] + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + target: ${{ matrix.target }} + toolchain: ${{ matrix.toolchain }} + override: true + - + name: Benchmark the workspace + run: cargo bench --locked --workspace --target ${{ matrix.target }} --features full --verbose -- + - + name: Upload the benchmarks + id: artifacts + uses: actions/upload-artifact@v4 + with: + name: Benchmark Report (${{ github.event.repository.name }}) + if-no-files-found: error + overwrite: true + path: target/criterion/ diff --git a/.github/workflows/clippy.yml b/.github/workflows/cargo-clippy.yml similarity index 75% rename from .github/workflows/clippy.yml rename to .github/workflows/cargo-clippy.yml index 4fd40b29..e97eca96 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/cargo-clippy.yml @@ -1,6 +1,6 @@ -name: clippy +name: Clippy -concurrency: +concurrency: cancel-in-progress: false group: ${{ github.workflow }}-${{ github.ref }} @@ -10,35 +10,33 @@ env: on: pull_request: - branches: [ main, master ] + branches: + - main + - $default-branch types: [ opened, reopened, synchronize ] - paths: - - "**/clippy.yml" - - "**/*.rs" - - "**/Cargo.*" push: - branches: [ main, master ] - tags: [ latest, v*.*.*, "*-nightly" ] - paths: - - "**/clippy.yml" - - "**/*.rs" - - "**/Cargo.*" + branches: + - main + - $default-branch + tags: + - v*.*.* + - "*-nightly" release: types: [ created, edited ] repository_dispatch: - types: [ clippy ] + types: [ clippy, cargo-clippy ] workflow_dispatch: jobs: clippy: runs-on: ubuntu-latest permissions: - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status contents: read security-events: write statuses: write steps: - - + - name: Checkout uses: actions/checkout@v4 - @@ -47,17 +45,19 @@ jobs: with: cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} components: clippy, rustfmt - - + override: true + toolchain: nightly + - name: Setup the for sarif output run: cargo install clippy-sarif sarif-fmt - - + - name: Run Clippy run: cargo clippy --all-features --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt continue-on-error: true - - + - name: Upload analysis uses: github/codeql-action/upload-sarif@v3 with: diff --git a/.github/workflows/crates-io.yml b/.github/workflows/cargo-publish.yml similarity index 93% rename from .github/workflows/crates-io.yml rename to .github/workflows/cargo-publish.yml index dc56f747..43191d8e 100644 --- a/.github/workflows/crates-io.yml +++ b/.github/workflows/cargo-publish.yml @@ -10,7 +10,7 @@ env: on: repository_dispatch: - types: [ crates-io ] + types: [ cargo-publish, crates-io ] workflow_dispatch: permissions: @@ -31,7 +31,7 @@ jobs: max-parallel: 1 fail-fast: false matrix: - package: + package: - scsys-traits - scsys-util - scsys-core @@ -42,15 +42,15 @@ jobs: - scsys runs-on: ubuntu-latest steps: - - + - uses: actions/checkout@v4 name: Checkout - - + - uses: actions-rust-lang/setup-rust-toolchain@v1 name: Setup Rust with: cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - + - name: Publish (${{ matrix.package }}) run: cargo publish --locked --package ${{ matrix.package }} id: publish diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml new file mode 100644 index 00000000..338cc169 --- /dev/null +++ b/.github/workflows/cleanup.yml @@ -0,0 +1,31 @@ +name: Cleanup + +on: + pull_request: + types: + - closed + +jobs: + cache_cleanup: + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - + name: Cleanup + run: | + echo "Fetching list of cache keys" + cacheKeysForPR=$(gh cache list --ref $BRANCH --limit 100 --json id --jq '.[].id') + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh cache delete $cacheKey + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index feee32da..e7baf207 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: release +name: Release on: release: @@ -19,37 +19,37 @@ on: type: boolean permissions: - actions: read + actions: read contents: write discussions: write jobs: publish: - environment: + environment: name: crates-io - url: https://crates.io/crates/${{ github.event.repository.name }} + url: https://crates.io/crates/scsys runs-on: ubuntu-latest steps: - - + - name: Publish to crates.io - uses: peter-evans/repository-dispatch@v3 + uses: peter-evans/repository-dispatch@v3 with: - event-type: crates-io + event-type: cargo-publish client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' token: ${{ github.token }} release: - needs: publish + needs: publish env: IS_PRERELEASE: ${{ github.event.inputs.prerelease || false }} IS_DRAFT: ${{ github.event.inputs.draft || false }} runs-on: ubuntu-latest steps: - - + - name: Checkout uses: actions/checkout@v4 - - + - name: Create release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: append_body: false draft: ${{ env.IS_DRAFT }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6f90e9d6..1f4d11bc 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,4 +1,4 @@ -name: rust +name: Rust concurrency: cancel-in-progress: false @@ -10,15 +10,17 @@ env: on: pull_request: - branches: [ main, master ] + branches: + - main + - $default-branch types: [ opened, synchronize, reopened ] - paths: - - "**/rust.yml" - - "**/*.rs" - - "**/Cargo.*" push: - branches: [ main, master ] - tags: [ latest, v*, "*-nightly" ] + branches: + - main + - $default-branch + tags: + - v*.*.* + - "*-nightly" repository_dispatch: types: [ rust ] workflow_dispatch: @@ -28,22 +30,19 @@ on: description: 'Run benchmarks' required: true type: boolean - no_std: + nightly: default: false - description: 'Run tests with no_std feature' + description: 'Run tests with the nightly toolchain?' required: true type: boolean -permissions: - contents: write - jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - target: [x86_64-unknown-linux-gnu] # [ x86_64-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2 ] + target: [ x86_64-unknown-linux-gnu ] steps: - name: Checkout @@ -56,53 +55,41 @@ jobs: target: ${{ matrix.target }} - name: Build the workspace - run: cargo build -r --locked --workspace --all-features --target ${{ matrix.target }} + run: cargo build -r --locked --workspace --features full --target ${{ matrix.target }} benchmark: if: github.event_name == 'repository_dispatch' || github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') || github.event.inputs.benchmark == 'true' needs: build runs-on: ubuntu-latest outputs: - results: ${{ steps.artifacts.outputs.artifact-id }} - url: ${{ steps.artifacts.outputs.artifact-url }} + digest: ${{ steps.benchmark.outputs.digest }} + id: ${{ steps.benchmark.outputs.id }} + url: ${{ steps.benchmark.outputs.url }} permissions: contents: write checks: write - strategy: - fail-fast: false - matrix: - target: [ x86_64-unknown-linux-gnu ] - toolchain: [ stable ] steps: - name: Checkout uses: actions/checkout@v4 - - - name: Setup Rust - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - target: ${{ matrix.target }} - toolchain: ${{ matrix.toolchain }} - override: true + fetch-depth: 0 + repository: ${{ github.repository }} + ref: ${{ github.ref }} - name: Benchmark the workspace - run: cargo bench --locked --workspace --target ${{ matrix.target }} --all-features --verbose -- - - - name: Upload the benchmarks - id: artifacts - uses: actions/upload-artifact@v4 + id: benchmark + uses: peter-evans/repository-dispatch@v3 with: - name: benchmarks-scsys:${{ matrix.target }}-${{ matrix.toolchain }}@${{ github.sha }} - if-no-files-found: error - overwrite: true - path: target/criterion/ + event-type: cargo-bench + client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' + token: ${{ github.token }} test: needs: build runs-on: ubuntu-latest strategy: fail-fast: false matrix: - features: [ all, default, full ] + features: [ default, full ] target: [ x86_64-unknown-linux-gnu ] toolchain: [ stable ] steps: @@ -125,18 +112,13 @@ jobs: if: matrix.features == 'default' name: Test (default) run: cargo test -r --locked --workspace --target ${{ matrix.target}} - - - if: matrix.features == 'all' - name: Test (all-features) - run: cargo test -r --locked --workspace --target ${{ matrix.target}} --all-features - test_no_std: + test_nightly: needs: build runs-on: ubuntu-latest strategy: fail-fast: false matrix: - features: [ alloc, no_std ] - package: [ scsys-core ] + features: [ all, no_std, "alloc,nightly" ] toolchain: [ nightly ] steps: - @@ -150,16 +132,20 @@ jobs: toolchain: ${{ matrix.toolchain }} override: true - - name: Test (${{ matrix.features }}) - if: matrix.toolchain == 'nightly' && matrix.features == 'alloc' + name: Test (all) + if: matrix.features == 'all' + run: cargo test -r --locked --workspace --all-features + - + name: Test (no_std) + if: matrix.features == 'no_std' continue-on-error: true - run: cargo test -r --locked --workspace --no-default-features --package ${{ matrix.package }} --features ${{ matrix.features }} + run: cargo test -r --locked --workspace --no-default-features env: RUSTFLAGS: "-C panic=abort -Z panic_abort_tests" - - name: Test (no_std) - if: matrix.toolchain == 'nightly' && matrix.features == 'no_std' + name: Test (${{ matrix.features }}) + if: matrix.features != 'all' && matrix.features != 'no_std' continue-on-error: true - run: cargo test -r --locked --workspace --no-default-features --package ${{ matrix.package }} + run: cargo test -r --locked --workspace --no-default-features --features ${{ matrix.features }} env: RUSTFLAGS: "-C panic=abort -Z panic_abort_tests" diff --git a/.gitignore b/.gitignore index fe5257cb..5d38b7b7 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,9 @@ ### vscode -**/.vscode/ +**/.vscode/* + +!**/.vscode/settings.json # File Extensions @@ -108,4 +110,4 @@ ## Operating Systems ### Windows (WSL2) -**/*:Zone.Identifier \ No newline at end of file +**/*:Zone.Identifier diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0dddf02a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "rust-analyzer.check.features": [ + "default", + "full" + ] +} diff --git a/Cargo.lock b/Cargo.lock index 83492962..2dd70dd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,9 +128,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base64" @@ -205,18 +205,18 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.26" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "chrono" @@ -262,9 +262,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -272,9 +272,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -284,9 +284,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", @@ -296,9 +296,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" @@ -554,7 +554,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -817,9 +817,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libm" @@ -860,9 +860,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "nu-ansi-term" @@ -874,81 +874,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", - "rand 0.8.5", - "serde", -] - [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", - "serde", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1013,9 +944,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "thiserror", @@ -1024,9 +955,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -1034,9 +965,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", @@ -1047,11 +978,10 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -1134,18 +1064,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" - -[[package]] -name = "rand" -version = "0.8.5" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core 0.6.4", -] +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -1154,7 +1075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha", - "rand_core 0.9.3", + "rand_core", "serde", ] @@ -1165,15 +1086,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core", ] -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - [[package]] name = "rand_core" version = "0.9.3" @@ -1191,7 +1106,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ "num-traits", - "rand 0.9.1", + "rand", ] [[package]] @@ -1304,7 +1219,7 @@ dependencies = [ [[package]] name = "scsys" -version = "0.3.0" +version = "0.3.1" dependencies = [ "anyhow", "criterion", @@ -1322,7 +1237,7 @@ dependencies = [ [[package]] name = "scsys-config" -version = "0.3.0" +version = "0.3.1" dependencies = [ "anyhow", "clap", @@ -1342,16 +1257,15 @@ dependencies = [ [[package]] name = "scsys-core" -version = "0.3.0" +version = "0.3.1" dependencies = [ "anyhow", "approx", "chrono", "getrandom 0.3.3", - "num", "num-traits", "paste", - "rand 0.9.1", + "rand", "rand_distr", "serde", "serde_derive", @@ -1367,7 +1281,7 @@ dependencies = [ [[package]] name = "scsys-crypto" -version = "0.3.0" +version = "0.3.1" dependencies = [ "anyhow", "bincode", @@ -1378,7 +1292,7 @@ dependencies = [ "generic-array 1.2.0", "getrandom 0.3.3", "paste", - "rand 0.9.1", + "rand", "rand_distr", "rayon", "scsys-core", @@ -1396,7 +1310,7 @@ dependencies = [ [[package]] name = "scsys-derive" -version = "0.3.0" +version = "0.3.1" dependencies = [ "proc-macro2", "quote", @@ -1405,7 +1319,7 @@ dependencies = [ [[package]] name = "scsys-macros" -version = "0.3.0" +version = "0.3.1" dependencies = [ "proc-macro2", "quote", @@ -1414,18 +1328,18 @@ dependencies = [ [[package]] name = "scsys-traits" -version = "0.3.0" +version = "0.3.1" dependencies = [ "num-traits", ] [[package]] name = "scsys-util" -version = "0.3.0" +version = "0.3.1" dependencies = [ "anyhow", "num-traits", - "rand 0.9.1", + "rand", "serde", "serde_derive", "serde_json", @@ -1559,9 +1473,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", @@ -1601,12 +1515,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -1716,9 +1629,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -1848,9 +1761,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -1997,9 +1910,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-result" @@ -2094,9 +2007,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -2118,9 +2031,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yaml-rust2" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18b783b2c2789414f8bb84ca3318fc9c2d7e7be1c22907d37839a58dedb369d3" +checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" dependencies = [ "arraydeque", "encoding_rs", @@ -2153,18 +2066,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 56c41bd1..47a8d3f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ default-members = [ "scsys", ] -resolver = "3" members = [ "core", "scsys", @@ -11,13 +10,14 @@ members = [ "derive", "macros", "traits", - "utils", + "utils", ] +resolver = "3" [workspace.package] -authors = [ - "FL03 (https://github.com/FL03)", - "Scattered-Systems (https://github.com/scattered-systems)" +authors = [ + "FL03 (https://github.com/FL03)", + "Scattered-Systems (https://github.com/scattered-systems)" ] categories = [ ] description = "scsys is a collection of primitives and utilities for use throughout the ecosystem." @@ -28,34 +28,29 @@ license = "Apache-2.0" readme = "README.md" repository = "https://github.com/scattered-systems/scsys.git" rust-version = "1.85.0" -version = "0.3.0" +version = "0.3.1" [workspace.dependencies] # sdk -scsys = { default-features = false, path = "scsys", version = "0.3.0" } -scsys-core = { default-features = false, path = "core", version = "0.3.0" } -scsys-config = { default-features = false, path = "config", version = "0.3.0" } -scsys-crypto = { default-features = false, path = "crypto", version = "0.3.0" } -scsys-derive = { default-features = false, path = "derive", version = "0.3.0" } -scsys-macros = { default-features = false, path = "macros", version = "0.3.0" } -scsys-traits = { default-features = false, path = "traits", version = "0.3.0" } -scsys-util = { default-features = false, path = "utils", version = "0.3.0" } +scsys = { default-features = false, path = "scsys", version = "0.3.1" } +scsys-core = { default-features = false, path = "core", version = "0.3.1" } +scsys-config = { default-features = false, path = "config", version = "0.3.1" } +scsys-crypto = { default-features = false, path = "crypto", version = "0.3.1" } +scsys-derive = { default-features = false, path = "derive", version = "0.3.1" } +scsys-macros = { default-features = false, path = "macros", version = "0.3.1" } +scsys-traits = { default-features = false, path = "traits", version = "0.3.1" } +scsys-util = { default-features = false, path = "utils", version = "0.3.1" } # async futures = { default-features = false, version = "0.3" } -tokio = { default-features = false, version = "1" } # benchmarking criterion = { version = "0.6" } # concurrency & parallelism -crossbeam = { default-features = false, version = "0.8" } +# crossbeam = { default-features = false, version = "0.8" } rayon = { default-features = false, version = "1" } -# data & serialization -bincode = { default-features = false, features = [ "derive" ], version = "2" } -bincode_derive = { version = "2" } +rayon-core = { default-features = false, version = "1" } +# data bytes = { default-features = false, version = "1" } -serde = { default-features = false, features = [ "derive" ], version = "1" } -serde_derive = { version = "1" } -serde_json = { default-features = false, version = "1" } # error handling anyhow = { default-features = false, version = "1" } thiserror = { default-features = false, version = "2" } @@ -72,6 +67,12 @@ getrandom = { default-features = false, version = "0.3" } rand = { default-features = false, version = "0.9" } rand_distr = { default-features = false, version = "0.5" } uuid = { default-features = false, version = "1" } +# serialization +bincode = { default-features = false, features = [ "derive" ], version = "2" } +bincode_derive = { version = "2" } +serde = { default-features = false, features = [ "derive" ], version = "1" } +serde_derive = { version = "1" } +serde_json = { default-features = false, version = "1" } # time chrono = { default-features = false, version = "0.4" } time = { default-features = false, version = "0.3" } @@ -95,7 +96,7 @@ codegen-units = 256 debug = true debug-assertions = true incremental = true -lto = 'thin' +lto = "thin" opt-level = 2 overflow-checks = true panic = "abort" @@ -107,7 +108,7 @@ codegen-units = 16 debug = false debug-assertions = false incremental = false -lto = false +lto = true opt-level = "z" overflow-checks = false panic = "abort" diff --git a/QUICKSTART.md b/QUICKSTART.md index 0204b1d2..86df9aaf 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -1,6 +1,4 @@ ---- -title: Quickstart ---- +# Quickstart Guide `scsys` Welcome to the quickstart guide for the `scsys` library! This guide will help you get started with the library and provide you with the necessary steps to set up your development environment. @@ -8,6 +6,18 @@ Welcome to the quickstart guide for the `scsys` library! This guide will help yo ### Prerequisites +Before you can start using the `scsys` library, you need to have the following tools installed on your machine: + +- [Git](https://git-scm.com/downloads) - for version control and cloning the repository. +- [Rust](https://www.rust-lang.org/tools/install) - the programming language used to develop the `scsys` library. +- [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) - the Rust package manager and build system, which is included with Rust. + +Optionally, you may also want to install the following tools for development: + +- [Visual Studio Code](https://code.visualstudio.com/) - a popular code editor with Rust support. +- [Rust Analyzer](https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer) - a language server for Rust that provides features like code completion, go to definition, and more. +- [cargo-binstall](https://github.com/cargo-bins/cargo-binstall) - a tool to streamline the installation of Rust binaries. + #### Rust If you don't have rustup installed, you can install it by following the instructions on the [official website](https://www.rust-lang.org/tools/install). diff --git a/README.md b/README.md index 0c3ade14..2a41dafe 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ *** -_**Warning: the library is still in the early stages of development so make sure to use with caution!**_ - Welcome to `scsys`, a collection of useful utilities, types, and other primitives that are used in various projects developed by [Scattered Systems](https://github.com/scattered-systems). The library is designed to be a general-purpose utility library that can be used in any Rust project, aiming to provide a standardized set of tools that can be used to build robust and reliable software. ## Usage @@ -17,12 +15,16 @@ Before you start using `scsys`, make sure to add it as a dependency in your `Car ```toml [dependencies.scsys] default-features = true -features = [] +features = [ + "derive", +] version = "0.2.x" ``` ### Examples +For more detailed examples, please visit the [examples](https://github.com/scattered-systems/scsys/tree/main/examples) directory in the repository. Below are some brief examples highlighting certain features of the library. + #### Example 1: _Using the `VariantConstructors` derive macro_ The `VariantConstructors` derive macro can be used to automatically generate functional accessors for named fields within a given structure. For example, given the following structure: @@ -60,63 +62,7 @@ assert_eq!(c, Sample::C { x: 1.0, y: 2.0 }); ## Getting Started -### Prerequisites - -To build and run the `scsys` library, you will need to have the following tools installed on your machine: - -- [Rust](https://www.rust-lang.org/) (with `rustup` for managing toolchains) -- [Cargo](https://doc.rust-lang.org/cargo/) (the Rust package manager, which comes with Rust) -- [Git](https://git-scm.com/) (for cloning the repository) - -#### Rust - -If you don't have rustup installed, you can install it by following the instructions on the [official website](https://www.rust-lang.org/tools/install). - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -``` - -##### _rustup_ - -Once installed, you can use rustup to manage your Rust toolchain. This includes installing the latest stable version of Rust, as well as any other versions you may need for your projects. For now, we simply recommend using the latest stable version of Rust and making sure that any other toolchains you may have installed are up to date. - -```bash -rustup update -``` - -### Building from the source - -Start by cloning the repository locally to your machine: - -```bash -git clone https://github.com/scattered-systems/scsys.git --branch main -``` - -Then, navigate to the cloned directory: - -```bash -cd scsys -``` - -#### cargo: use the built-in tool to manage the project - -Build the project using the `cargo build` command: - -```bash -cargo build -r --locked --workspace --all-features -``` - -Test the project using the `cargo test` command: - -```bash -cargo test -r --locked --workspace --all-features -``` - -Or, benchmark the project using the `cargo bench` command: - -```bash -cargo bench --verbose --workspace --all-features -- -``` +To get started with `scsys`, you can check out the [QUICKSTART.md](QUICKSTART.md) file, which provides a step-by-step guide on how to set up your development environment and start using the library. ## License diff --git a/SECURITY.md b/SECURITY.md index 67a24389..ccf1c228 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,11 +4,11 @@ This section is used to update intrested parties as to which versions are currently supported, with respect to the current version. -| Version | Supported | -| :-------- | :----------------- | -| 0.3.0 | :white_check_mark: | -| <=0.2.9 | :white_check_mark: | -| <=0.2.3 | :x: | +| Version | Supported | +| :------------- | :----------------- | +| 0.3.1 | :white_check_mark: | +| >0.3.1,<=0.2.9 | :white_check_mark: | +| <0.2.9 | :x: | ## Reporting a Vulnerability diff --git a/config/Cargo.toml b/config/Cargo.toml index 6ff3eef8..4b3f550e 100644 --- a/config/Cargo.toml +++ b/config/Cargo.toml @@ -26,7 +26,7 @@ tag-name = "{{version}}" [lib] crate-type = [ - "cdylib", + "cdylib", "rlib" ] bench = false @@ -80,6 +80,10 @@ full = [ "tracing-subscriber", ] +nightly = [ + "scsys-core/nightly", +] + # ************* [FF:Features] ************* convert-case = ["config?/convert-case"] @@ -177,4 +181,4 @@ url = ["dep:url"] # ************* [Unit Tests] ************* [[test]] -name = "default" \ No newline at end of file +name = "default" diff --git a/config/src/lib.rs b/config/src/lib.rs index 935f52ca..7a963e1b 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -12,6 +12,7 @@ html_favicon_url = "https://raw.githubusercontent.com/scattered-systems/.github/main/assets/favicon.ico" )] +#![cfg(feature = "std")] #[doc(no_inline)] #[cfg(feature = "config")] pub use config; diff --git a/core/Cargo.toml b/core/Cargo.toml index 15371d63..2267a246 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,10 +1,10 @@ [package] build = "build.rs" +description = "core primitives and utilities for the scsys ecosystem" name = "scsys-core" authors.workspace = true categories.workspace = true -description.workspace = true edition.workspace = true homepage.workspace = true keywords.workspace = true @@ -14,17 +14,6 @@ repository.workspace = true rust-version.workspace = true version.workspace = true -[lib] -crate-type = [ - "cdylib", - "rlib", - "staticlib" -] -bench = false -doc = true -doctest = false -test = true - [package.metadata.docs.rs] all-features = false features = ["full"] @@ -35,29 +24,40 @@ version = "v{{version}}" no-dev-version = true tag-name = "{{version}}" +[lib] +crate-type = [ + "cdylib", + "rlib", + "staticlib" +] +bench = false +doc = true +doctest = false +test = true + [dependencies] -num = { workspace = true } -num-traits = { workspace = true } -# data & serialization -serde = { optional = true, workspace = true } -serde_derive = { optional = true, workspace = true } -serde_json = { optional = true, workspace = true } -# errors +# error-handling anyhow = { optional = true, workspace = true } thiserror = { workspace = true } +# mathematics +num-traits = { workspace = true } # randomization getrandom = { optional = true, workspace = true } rand = { optional = true, workspace = true } rand_distr = { optional = true, workspace = true } uuid = { optional = true, workspace = true } -# time -chrono = { optional = true, workspace = true } -time = { optional = true, workspace = true } -# macros +# serialization +serde = { optional = true, workspace = true } +serde_derive = { optional = true, workspace = true } +serde_json = { optional = true, workspace = true } +# macros & utilities paste = { workspace = true } smart-default = { workspace = true } strum = { workspace = true } -# logging +# time +chrono = { optional = true, workspace = true } +time = { optional = true, workspace = true } +# tracing tracing = { optional = true, workspace = true } # wasm wasm-bindgen = { optional = true, workspace = true } @@ -69,23 +69,29 @@ approx = { workspace = true } [features] default = [ "std" ] -full = [ +full = [ "default", "anyhow", "chrono", "json", - "rand", - "serde", + "rand", + "serde", "time", "tracing", ] +nightly = [] + # ************* [FF:Features] ************* -json = [ - "alloc", +json = [ + "alloc", "serde", - "serde_json", + "serde_json", +] + +time = [ + "time-d" ] # ************* [FF:Environments] ************* @@ -93,7 +99,6 @@ json = [ std = [ "alloc", "anyhow?/std", - "num/std", "num-traits/std", "rand?/std", "rand?/std_rng", @@ -107,7 +112,7 @@ std = [ wasi = [] -wasm = [ +wasm = [ "getrandom?/wasm_js", "uuid?/js", "wasm-bindgen", @@ -117,16 +122,15 @@ wasm = [ anyhow = ["dep:anyhow"] -alloc = [ +alloc = [ "chrono?/alloc", - "num/alloc", "serde?/alloc", "serde_json?/alloc", "time?/alloc", ] -chrono = [ - "dep:chrono", +chrono = [ + "dep:chrono", "chrono?/clock", "time", ] @@ -134,7 +138,6 @@ chrono = [ rand = [ "dep:rand", "dep:rand_distr", - "num/rand", "rng", ] @@ -146,25 +149,24 @@ rng = [ "uuid?/v4" ] -serde = [ - "dep:serde", - "dep:serde_derive", +serde = [ + "dep:serde", + "dep:serde_derive", "chrono?/serde", - "num/serde", - "rand?/serde", + "rand?/serde", "uuid?/serde", "time?/serde", ] serde_json = ["dep:serde_json"] -time = ["dep:time"] +time-d = ["dep:time"] tracing = ["dep:tracing"] -uuid = [ - "dep:uuid", - "uuid?/v3" +uuid = [ + "dep:uuid", + "uuid?/v3" ] wasm-bindgen = [ @@ -177,9 +179,6 @@ wasm-bindgen = [ [[test]] name = "default" -[[test]] -name = "state" - [[test]] name = "time" -required-features = ["std", "time"] \ No newline at end of file +required-features = ["std", "time"] diff --git a/core/src/cont/container.rs b/core/src/cont/container.rs new file mode 100644 index 00000000..04fa34d1 --- /dev/null +++ b/core/src/cont/container.rs @@ -0,0 +1,188 @@ +/* + appellation: container + authors: @FL03 +*/ +use super::RawContainer; + +/// the [`ContainerBase`] type materializes the [`RawContainer`] trait, providing a physical +/// implementation of a generic container type +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "snake_case") +)] +pub struct ContainerBase +where + S: RawContainer, +{ + /// the underlying store for the container + pub(crate) repr: S, + #[cfg_attr(feature = "serde", serde(skip))] + pub(crate) _marker: core::marker::PhantomData, +} + +impl ContainerBase +where + S: RawContainer, +{ + /// returns a new instance using the given store + pub const fn from_store(store: S) -> Self { + Self { + repr: store, + _marker: core::marker::PhantomData::, + } + } + /// returns a reference to the underlying store + pub const fn store(&self) -> &S { + &self.repr + } + /// returns a mutable reference to the underlying store + pub const fn store_mut(&mut self) -> &mut S { + &mut self.repr + } + /// [`replace`](core::mem::replace) the store with another and return the previous value + pub const fn replace_store(&mut self, store: S) -> S { + core::mem::replace(self.store_mut(), store) + } + /// set the current store and return a mutable reference to the container + pub fn set_store(&mut self, store: S) -> &mut Self { + self.repr = store; + self + } + /// [`swap`](core::mem::swap) the store another and return a mutable reference to the + /// container + pub const fn swap_store(&mut self, store: &mut S) -> &mut Self { + core::mem::swap(self.store_mut(), store); + self + } + /// [`take`](core::mem::take) the store and return it, leaving the logical default in its + /// place + pub fn take_store(&mut self) -> S + where + S: Default, + { + core::mem::take(self.store_mut()) + } + /// consumes the current instance to create another with the given store + pub fn with_store>(self, repr: T) -> ContainerBase { + ContainerBase { + repr, + _marker: core::marker::PhantomData::, + } + } +} + +impl AsRef for ContainerBase +where + S: RawContainer, +{ + fn as_ref(&self) -> &S { + self.store() + } +} + +impl AsMut for ContainerBase +where + S: RawContainer, +{ + fn as_mut(&mut self) -> &mut S { + self.store_mut() + } +} + +impl core::borrow::Borrow for ContainerBase +where + S: RawContainer, +{ + fn borrow(&self) -> &S { + self.store() + } +} + +impl core::borrow::BorrowMut for ContainerBase +where + S: RawContainer, +{ + fn borrow_mut(&mut self) -> &mut S { + self.store_mut() + } +} + +impl core::ops::Deref for ContainerBase +where + S: RawContainer, +{ + type Target = S; + + fn deref(&self) -> &Self::Target { + self.store() + } +} + +impl core::ops::DerefMut for ContainerBase +where + S: RawContainer, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.store_mut() + } +} + +impl core::ops::Index for ContainerBase +where + S: RawContainer + core::ops::Index, +{ + type Output = S::Item; + + fn index(&self, index: I) -> &Self::Output { + self.store().index(index) + } +} + +impl core::ops::IndexMut for ContainerBase +where + S: RawContainer + core::ops::IndexMut, +{ + fn index_mut(&mut self, index: I) -> &mut Self::Output { + self.store_mut().index_mut(index) + } +} + +impl IntoIterator for ContainerBase +where + S: RawContainer + IntoIterator, +{ + type Item = A; + type IntoIter = S::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.repr.into_iter() + } +} + +impl<'a, A, S> IntoIterator for &'a ContainerBase +where + S: RawContainer, + for<'b> &'b S: IntoIterator, +{ + type Item = A; + type IntoIter = <&'a S as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.store().into_iter() + } +} + +impl<'a, A, S> IntoIterator for &'a mut ContainerBase +where + S: RawContainer, + for<'b> &'b mut S: IntoIterator, +{ + type Item = A; + type IntoIter = <&'a mut S as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.store_mut().into_iter() + } +} diff --git a/core/src/cont/error.rs b/core/src/cont/error.rs new file mode 100644 index 00000000..2b316327 --- /dev/null +++ b/core/src/cont/error.rs @@ -0,0 +1,26 @@ +/* + appellation: error + authors: @FL03 +*/ +//! this module defines the various errors encountered by storage containers. + +/// a type alias for a [`Result`] equipped to handle the [`StoreError`] type +#[allow(dead_code)] +pub(crate) type StoreResult = core::result::Result; +/// the [`StoreError`] type enumerates the possible errors that can occur in the store module. +#[derive(Clone, Copy, Debug, strum::EnumIs, thiserror::Error)] +pub enum StoreError { + #[error(transparent)] + EntryError(#[from] EntryError), + /// An error indicating that the key was not found in the key-value store. + #[error("Key not found in the key-value store")] + KeyNotFound, +} +/// the [`EntryError`] type enumerates the possible errors that can occur when accessing +/// entries in the key-value store. +#[derive(Clone, Copy, Debug, strum::EnumIs, thiserror::Error)] +pub enum EntryError { + /// An error indicating that the value is not present in the key-value store. + #[error("The entry is vacant")] + VacantEntry, +} diff --git a/core/src/cont/mod.rs b/core/src/cont/mod.rs new file mode 100644 index 00000000..d4023c23 --- /dev/null +++ b/core/src/cont/mod.rs @@ -0,0 +1,50 @@ +/* + appellation: store + authors: @FL03 +*/ +//! this module is focused on defining a set of traits and types for abstracting the behaviourds +//! of an entity capable of storing some data. +#[doc(inline)] +pub use self::{container::*, error::*, traits::prelude::*}; + +/// this module implements the [`ContainerBase`] type, which is a base type for containers that +/// use a store to manage their data. +pub mod container; +/// this module defiens the [`StoreError`] enum for handling various errors that can occur with +/// stores +pub mod error; + +pub mod traits { + #[doc(inline)] + pub use self::prelude::*; + /// this module defines the [`Entry`] trait for establishing a common interface for + /// entries in a store, which are key-value pairs. + pub mod entry; + /// this module defines the [`Get`] trait for establishing a common interface for + /// retrieving values from a store. + pub mod get; + /// this module defines the [`HKT`] trait for establishing a common interface + /// for higher-kinded types, which are types that take other types as parameters. + pub mod hkt; + /// this module defines the [`RawContainer`] trait for establishing a core interface for + /// various representations of a container. + pub mod raw_container; + /// this module defines the [`RawStore`] trait that extends the [`RawContainer`] trait to + /// establish an interface for key-value stores. + pub mod raw_store; + + pub(crate) mod prelude { + #[doc(inline)] + pub use super::entry::*; + + #[doc(inline)] + pub use super::raw_container::*; + #[doc(inline)] + pub use super::raw_store::*; + } +} + +pub(crate) mod prelude { + #[doc(inline)] + pub use super::traits::prelude::*; +} diff --git a/core/src/cont/traits/entry.rs b/core/src/cont/traits/entry.rs new file mode 100644 index 00000000..49069a4d --- /dev/null +++ b/core/src/cont/traits/entry.rs @@ -0,0 +1,102 @@ +/* + appellation: key_value + authors: @FL03 +*/ + +/// A [`RawEntry`] represents a single record within a key-value store, providing access to the +/// key alongisde methods for accessing and manipulating the value of the entry. +pub trait RawEntry<'a> { + type Key; + type Value; + + private!(); + /// Returns a reference to the key of the entry. + fn key(&self) -> &Self::Key; + /// Returns a reference to the value of the entry. + fn value(&self) -> Option<&Self::Value>; + /// Returns a mutable reference to the value of the entry. + fn value_mut(&mut self) -> Option<&mut Self::Value>; +} +/// The [`Entry`] trait extends the [`RawEntry`] trait to provide additional methods for +/// +pub trait Entry<'a>: RawEntry<'a> { + /// if the entry is vacant, insert the given value + fn or_insert(self, value: Self::Value) -> &'a mut Self::Value; + /// if the entry does not exist, insert the value returned by the provided function and + /// return a mutable reference to it. + fn or_insert_with(self, f: F) -> &'a mut Self::Value + where + F: FnOnce() -> Self::Value; +} +/* + ************* Implementations ************* +*/ +#[cfg(feature = "alloc")] +macro_rules! entry { + ($($prefix:ident)::* -> $call:ident($($arg:tt),*)) => { + $($prefix)::*::Entry::$call($($arg),*) + }; +} +#[cfg(feature = "alloc")] +macro_rules! impl_entry { + (@raw $($prefix:ident)::* $(where $($preds:tt)*)?) => { + impl<'a, K, V> RawEntry<'a> for $($prefix)::*::Entry<'a, K, V> $(where $($preds)*)? { + type Key = K; + type Value = V; + + seal!(); + + fn key(&self) -> &Self::Key { + entry!($($prefix)::* -> key(self)) + } + + fn value(&self) -> Option<&Self::Value> { + match self { + $($prefix)::*::Entry::Occupied(entry) => Some(entry.get()), + $($prefix)::*::Entry::Vacant(_) => None, + } + } + + fn value_mut(&mut self) -> Option<&mut Self::Value> { + match self { + $($prefix)::*::Entry::Occupied(entry) => Some(entry.get_mut()), + $($prefix)::*::Entry::Vacant(_) => None, + } + } + } + + }; + (@entry $($prefix:ident)::* $(where $($preds:tt)*)?) => { + impl<'a, K, V> Entry<'a> for $($prefix)::*::Entry<'a, K, V> $(where $($preds)*)? { + fn or_insert(self, default: Self::Value) -> &'a mut Self::Value { + entry!($($prefix)::* -> or_insert(self, default)) + } + + fn or_insert_with(self, f: F) -> &'a mut Self::Value + where + F: FnOnce() -> Self::Value, + { + entry!($($prefix)::* -> or_insert_with(self, f)) + } + } + + }; + (@impl $($rest:tt)*) => { + impl_entry!(@raw $($rest)*); + impl_entry!(@entry $($rest)*); + + }; + ($($rest:tt)*) => { + impl_entry!(@impl $($rest)*); + }; +} + +#[cfg(feature = "alloc")] +impl_entry! { + alloc::collections::btree_map where K: Ord +} + +#[cfg(feature = "std")] +impl_entry! { + std::collections::hash_map where K: Eq + core::hash::Hash +} diff --git a/traits/src/cont/get.rs b/core/src/cont/traits/get.rs similarity index 100% rename from traits/src/cont/get.rs rename to core/src/cont/traits/get.rs diff --git a/traits/src/cont/hkt.rs b/core/src/cont/traits/hkt.rs similarity index 98% rename from traits/src/cont/hkt.rs rename to core/src/cont/traits/hkt.rs index 741f99af..59f60bb1 100644 --- a/traits/src/cont/hkt.rs +++ b/core/src/cont/traits/hkt.rs @@ -3,9 +3,6 @@ Contrib: @FL03 */ -#[allow(dead_code)] -mod old; - #[allow(clippy::upper_case_acronyms)] /// The [`HKT`] trait defines an interface for higher-kinded types (HKT). pub trait HKT { diff --git a/core/src/cont/traits/raw_container.rs b/core/src/cont/traits/raw_container.rs new file mode 100644 index 00000000..0761fda5 --- /dev/null +++ b/core/src/cont/traits/raw_container.rs @@ -0,0 +1,146 @@ +/* + appellation: raw_container + authors: @FL03 +*/ + +/// the [`RawContainer`] trait defines the most basic interface for a container composed of +/// elements of type [`Item`](RawContainer::Item). +pub trait RawContainer { + type Item; + + private!(); +} + +/* + ************* Implementations ************* +*/ +#[cfg(feature = "alloc")] +impl RawContainer for alloc::boxed::Box> { + type Item = T; + + seal!(); +} + +macro_rules! impl_raw_container { + (@impl $($p:ident)::*<$K:ident, $V:ident, $($rest:ident),*>) => { + impl<$K, $V, $($rest),*> RawContainer for $($p)::*<$K, $V, $($rest),*> { + type Item = $V; + + seal!(); + } + }; + (@impl $($p:ident)::*<$K:ident, $V:ident>) => { + impl<$K, $V> RawContainer for $($p)::*<$K, $V> { + type Item = $V; + + seal!(); + } + }; + (@impl $($p:ident)::*<$I:ident>) => { + impl<$I> RawContainer for $($p)::*<$I> { + type Item = $I; + + seal!(); + } + }; + ($($($p:ident)::*<$($T:ident),*>),* $(,)?) => { + $( + impl_raw_container!(@impl $($p)::*<$($T),*>); + )* + }; +} + +impl_raw_container! { + Option, + core::cell::Cell, + core::cell::RefCell, + core::marker::PhantomData, +} + +#[cfg(feature = "alloc")] +impl_raw_container! { + alloc::boxed::Box, + alloc::collections::BTreeMap, + alloc::collections::BTreeSet, + alloc::collections::VecDeque, + alloc::sync::Arc, + alloc::vec::Vec, +} + +#[cfg(feature = "std")] +impl_raw_container! { + std::collections::HashMap, + std::sync::Mutex, +} + +#[cfg(feature = "std")] +impl RawContainer for std::collections::HashSet { + type Item = K; + + seal!(); +} + +impl RawContainer for [T] { + type Item = T; + + seal!(); +} + +impl RawContainer for &[T] { + type Item = T; + + seal!(); +} + +impl RawContainer for &mut [T] { + type Item = T; + + seal!(); +} + +impl RawContainer for [T; N] { + type Item = T; + + seal!(); +} + +impl RawContainer for &[T; N] { + type Item = T; + + seal!(); +} + +impl RawContainer for &mut [T; N] { + type Item = T; + + seal!(); +} + +#[allow(unused_macros)] +macro_rules! tuple_cont { + ($($T:ident),+ $(,)?) => { + impl<$($T),+> RawContainer for ($($T,)+) { + type Item = ($($T,)+); + + seal!(); + } + }; +} + +impl RawContainer for (T,) { + type Item = T; + + seal!(); +} + +impl RawContainer for (T, T) { + type Item = T; + + seal!(); +} + +impl RawContainer for (T, T, T) { + type Item = T; + + seal!(); +} diff --git a/core/src/cont/traits/raw_store.rs b/core/src/cont/traits/raw_store.rs new file mode 100644 index 00000000..eb73a8b8 --- /dev/null +++ b/core/src/cont/traits/raw_store.rs @@ -0,0 +1,103 @@ +/* + appellation: store + authors: @FL03 +*/ +use super::{RawContainer, RawEntry}; + +/// [`RawStore`] is a trait extending the [`RawContainer`] trait to establish a common +/// interface for all key-value stores. +pub trait RawStore: RawContainer { + private!(); +} +/// the [`KeyValue`] trait extends the [`RawKeyValue`] trait to provide additional methods for +/// manipulating key-value stores. +pub trait Store: RawStore { + /// the entry for the key-value pair + type Entry<'a>: RawEntry<'a, Key = K, Value = V> + where + Self: 'a; + /// returns the [`Entry`] for the given key + fn entry(&mut self, key: K) -> Self::Entry<'_>; + + fn insert(&mut self, key: K, value: V) -> Option; +} +/// The [`StoreIter`] trait extends the [`RawStore`] trait to provide iteration capabilities +/// over the vertices stored in the edge. +pub trait StoreIter { + type Item<'a, _K, _V> + where + Self: 'a; + /// the iterator for the store + type Iter<'a>: Iterator> + where + Self: 'a; + /// returns an iterator over the vertices in the store. + fn iter(&self) -> Self::Iter<'_>; +} + +/* + ************* Implementations ************* +*/ + +#[cfg(feature = "alloc")] +mod impl_alloc { + use super::*; + use alloc::collections::btree_map::{self, BTreeMap}; + + impl RawStore for BTreeMap + where + K: Ord, + { + seal!(); + } + + impl Store for BTreeMap + where + K: Ord, + { + type Entry<'a> + = btree_map::Entry<'a, K, V> + where + Self: 'a; + + fn entry(&mut self, key: K) -> Self::Entry<'_> { + self.entry(key) + } + fn insert(&mut self, key: K, value: V) -> Option { + self.insert(key, value) + } + } +} + +#[cfg(feature = "std")] +mod impl_std { + use super::*; + use core::hash::{BuildHasher, Hash}; + use std::collections::hash_map::{self, HashMap}; + + impl RawStore for HashMap + where + K: Eq + Hash, + S: BuildHasher, + { + seal!(); + } + + impl Store for HashMap + where + K: Eq + Hash, + S: BuildHasher, + { + type Entry<'a> + = hash_map::Entry<'a, K, V> + where + Self: 'a; + + fn entry(&mut self, key: K) -> Self::Entry<'_> { + self.entry(key) + } + fn insert(&mut self, key: K, value: V) -> Option { + self.insert(key, value) + } + } +} diff --git a/core/src/error/raw_error.rs b/core/src/error/raw_error.rs index 688cd4ba..0cdb5ba2 100644 --- a/core/src/error/raw_error.rs +++ b/core/src/error/raw_error.rs @@ -6,7 +6,8 @@ #[cfg(feature = "alloc")] /// A type alias for a [`Err`] whose generic type is a [`Box`] of a trait object /// implementing [`core::error::Error`]. -pub type BoxErr = ErrorBase>; +pub type DynamicError = + ErrorBase>; #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] #[cfg_attr( diff --git a/core/src/id/identifier.rs b/core/src/id/identifier.rs index 930ca934..3d836af6 100644 --- a/core/src/id/identifier.rs +++ b/core/src/id/identifier.rs @@ -37,15 +37,6 @@ impl Id { { Self::new(T::zero()) } - #[cfg(feature = "rand")] - pub fn random() -> Self - where - rand_distr::StandardUniform: rand_distr::Distribution, - { - use rand::Rng; - let mut rng = rand::rng(); - Self::new(rng.random()) - } /// returns an immutable reference to the inner value pub const fn get(&self) -> &T { &self.0 @@ -119,35 +110,6 @@ impl Id { } } -impl Id { - pub fn atomic() -> Self { - use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; - static COUNTER: AtomicUsize = AtomicUsize::new(1); - Self::new(COUNTER.fetch_add(1, Relaxed)) - } - /// replaces the current id with the atomic-ally next value and returns the previous value. - /// see [`step`](Id::step) for more information - pub fn atomic_step(&mut self) -> usize { - use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; - static COUNTER: AtomicUsize = AtomicUsize::new(1); - self.replace(COUNTER.fetch_add(1, Relaxed)) - } -} - -#[cfg(feature = "uuid")] -impl Id { - pub fn v3(namespace: &uuid::Uuid, name: &[u8]) -> Self { - let id = uuid::Uuid::new_v3(namespace, name); - Self::new(id) - } - - #[cfg(all(feature = "rng", feature = "uuid"))] - pub fn v4() -> Self { - let id = uuid::Uuid::new_v4(); - Self::new(id) - } -} - impl Default for Id where T: Default, diff --git a/core/src/id/impls/impl_id.rs b/core/src/id/impls/impl_id.rs index 9e054f62..fc821724 100644 --- a/core/src/id/impls/impl_id.rs +++ b/core/src/id/impls/impl_id.rs @@ -4,40 +4,6 @@ */ use crate::id::Id; -impl Id<&T> { - /// returns a new identifier with a cloned inner value - pub fn cloned(&self) -> Id - where - T: Clone, - { - Id(self.0.clone()) - } - /// returns a new identifier with the inner value copied - pub fn copied(&self) -> Id - where - T: Copy, - { - Id(*self.0) - } -} - -impl Id<&mut T> { - /// returns a new identifier with a cloned inner value - pub fn cloned(&self) -> Id - where - T: Clone, - { - Id(self.0.clone()) - } - /// returns a new identifier with the inner value copied - pub fn copied(&self) -> Id - where - T: Copy, - { - Id(*self.0) - } -} - impl AsRef for Id { fn as_ref(&self) -> &T { self.get() diff --git a/core/src/id/impls/impl_id_deprecated.rs b/core/src/id/impls/impl_id_deprecated.rs new file mode 100644 index 00000000..8ecc9978 --- /dev/null +++ b/core/src/id/impls/impl_id_deprecated.rs @@ -0,0 +1,8 @@ +/* + appellation: impl_id_deprecated + authors: @FL03 +*/ +use crate::id::Id; + +#[doc(hidden)] +impl Id {} diff --git a/core/src/id/impls/impl_id_ops.rs b/core/src/id/impls/impl_id_ops.rs new file mode 100644 index 00000000..cdcded53 --- /dev/null +++ b/core/src/id/impls/impl_id_ops.rs @@ -0,0 +1,63 @@ +/* + appellation: impl_id_ops + authors: @FL03 +*/ +use crate::id::Id; +use num_traits::{Num, One, Zero}; + +impl Id {} + +impl One for Id +where + T: One, +{ + fn one() -> Self { + Id(T::one()) + } +} + +impl Zero for Id +where + T: Zero, +{ + fn zero() -> Self { + Id(T::zero()) + } + + fn is_zero(&self) -> bool { + self.get().is_zero() + } +} + +impl Num for Id +where + T: Num, +{ + type FromStrRadixErr = T::FromStrRadixErr; + + fn from_str_radix(s: &str, radix: u32) -> Result { + T::from_str_radix(s, radix).map(Id) + } +} + +impl_wrapper_binary! { + Id::<[ + Add.add, + Sub.sub, + Mul.mul, + Div.div, + Rem.rem, + BitAnd.bitand, + BitOr.bitor, + BitXor.bitxor, + Shl.shl, + Shr.shr + ]> +} + +impl_wrapper_unary! { + Id::<[ + Neg.neg, + Not.not + ]> +} diff --git a/core/src/id/impls/impl_id_rand.rs b/core/src/id/impls/impl_id_rand.rs new file mode 100644 index 00000000..fc9dabc3 --- /dev/null +++ b/core/src/id/impls/impl_id_rand.rs @@ -0,0 +1,57 @@ +/* + appellation: impl_id_rand + authors: @FL03 +*/ +use crate::id::Id; +use rand_distr::uniform::{SampleRange, SampleUniform}; +use rand_distr::{Distribution, StandardNormal, StandardUniform}; + +impl Id { + /// generate a new, random identifier from a value of type `T` + pub fn random() -> Self + where + StandardUniform: Distribution, + { + use rand::Rng; + let mut rng = rand::rng(); + Self::new(rng.random()) + } + + pub fn random_between(range: R) -> Self + where + R: SampleRange, + T: SampleUniform, + { + Self::new(rand::random_range(range)) + } + /// generate a random index from a value of type `T` using the provided [`Rng`](rand::Rng) + pub fn random_with(rng: &mut R, distr: Dist) -> Self + where + R: ?Sized + rand::RngCore, + Dist: Distribution, + { + use rand::Rng; + // generate a random u128 and cast it to usize + let rid = rng.sample(distr); + // cast the random u128 to usize + Self::new(rid) + } +} + +impl Distribution> for StandardNormal +where + StandardNormal: Distribution, +{ + fn sample(&self, rng: &mut R) -> Id { + Id::new(rng.sample(StandardNormal)) + } +} + +impl Distribution> for StandardUniform +where + StandardUniform: Distribution, +{ + fn sample(&self, rng: &mut R) -> Id { + Id::new(rng.sample(StandardUniform)) + } +} diff --git a/core/src/id/impls/impl_id_repr.rs b/core/src/id/impls/impl_id_repr.rs new file mode 100644 index 00000000..5ef02c07 --- /dev/null +++ b/core/src/id/impls/impl_id_repr.rs @@ -0,0 +1,85 @@ +/* + appellation: impl_id_repr + authors: @FL03 +*/ +use crate::id::Id; + +use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +#[cfg(feature = "uuid")] +use uuid::Uuid; +/// the static counter used for the [`Id`] representation +static COUNTER: AtomicUsize = AtomicUsize::new(0); + +impl Id<&T> { + /// returns a new identifier with a cloned inner value + pub fn cloned(&self) -> Id + where + T: Clone, + { + Id(self.0.clone()) + } + /// returns a new identifier with the inner value copied + pub fn copied(&self) -> Id + where + T: Copy, + { + Id(*self.0) + } +} + +impl Id<&mut T> { + /// returns a new identifier with a cloned inner value + pub fn cloned(&self) -> Id + where + T: Clone, + { + Id(self.0.clone()) + } + /// returns a new identifier with the inner value copied + pub fn copied(&self) -> Id + where + T: Copy, + { + Id(*self.0) + } +} + +impl Id { + /// initializes a new identifie depending on the available features; more specifically, + /// whenever the `rand` feature is enabled, it will create a random identifier, otherwise + /// it will default to an atomic counter. + pub fn create() -> Self { + let value: usize; + #[cfg(feature = "rand")] + { + value = rand::random::() as usize; + } + #[cfg(not(feature = "rand"))] + { + value = COUNTER.fetch_add(1, Relaxed); + } + Id::new(value) + } + pub fn atomic() -> Self { + Self::new(COUNTER.fetch_add(1, Relaxed)) + } + /// replaces the current id with the atomic-ally next value and returns the previous value. + /// see [`step`](Id::step) for more information + pub fn atomic_step(&mut self) -> usize { + self.replace(COUNTER.fetch_add(1, Relaxed)) + } +} + +#[cfg(feature = "uuid")] +impl Id { + pub fn v3(namespace: &Uuid, name: &[u8]) -> Self { + let id = Uuid::new_v3(namespace, name); + Self::new(id) + } + + #[cfg(all(feature = "rng", feature = "uuid"))] + pub fn v4() -> Self { + let id = Uuid::new_v4(); + Self::new(id) + } +} diff --git a/core/src/id/mod.rs b/core/src/id/mod.rs index 7aaabb6a..242dd7f5 100644 --- a/core/src/id/mod.rs +++ b/core/src/id/mod.rs @@ -2,27 +2,34 @@ Appellation: ids Contrib: FL03 */ -//! # Identity -//! -//! The identity module provides a set of traits and types for generating unique identifiers. +//! This module focuses on the [`Id`] implementation, a generic wrapper type used to define +//! various identifiers. The `Id` type is equipped with additional methods and traits to +//! facilitate the creation and management of identifiers in a type-safe manner. #[doc(inline)] -pub use self::prelude::*; +pub use self::{identifier::Id, traits::*, types::*}; -pub(crate) mod identifier; +mod identifier; mod impls { pub mod impl_id; + pub mod impl_id_ops; + pub mod impl_id_repr; + + #[allow(deprecated)] + mod impl_id_deprecated; + #[cfg(feature = "rand")] + pub mod impl_id_rand; } -pub mod traits { +mod traits { //! this module implements various traits supporting the [`Id`](super::Id) type #[doc(inline)] pub use self::prelude::*; - pub mod id; - pub mod identifier; + mod id; + mod identifier; - pub(crate) mod prelude { + mod prelude { #[doc(inline)] pub use super::id::*; #[doc(inline)] @@ -30,13 +37,13 @@ pub mod traits { } } -pub mod types { +mod types { #[doc(inline)] - pub use self::multi_id::IndexId; + pub use self::prelude::*; - pub mod multi_id; + mod multi_id; - pub(crate) mod prelude { + mod prelude { #[doc(inline)] pub use super::multi_id::IndexId; } @@ -46,9 +53,9 @@ pub(crate) mod prelude { #[doc(inline)] pub use super::identifier::Id; #[doc(inline)] - pub use super::traits::prelude::*; + pub use super::traits::*; #[doc(inline)] - pub use super::types::prelude::*; + pub use super::types::*; } #[cfg(test)] diff --git a/core/src/lib.rs b/core/src/lib.rs index 469024ab..7a5f9c2d 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -20,10 +20,12 @@ //! #![allow(clippy::module_inception)] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(feature = "alloc", feature = "nightly"), feature(allocator_api))] #![doc( html_logo_url = "https://raw.githubusercontent.com/scattered-systems/.github/main/assets/logo.png", html_favicon_url = "https://raw.githubusercontent.com/scattered-systems/.github/main/assets/favicon.ico" )] +#![crate_type = "lib"] #[cfg(feature = "alloc")] extern crate alloc; @@ -41,6 +43,8 @@ pub(crate) mod macros { pub mod seal; #[macro_use] pub mod wrapper; + #[macro_use] + pub mod wrapper_ops; } #[doc(inline)] @@ -51,6 +55,9 @@ pub use self::{ time::{Now, RawTimestamp, Timestamp}, types::prelude::*, }; + +/// this module implements a set of traits and utilities for working with containers +pub mod cont; /// this module implements various error-handling primitives and utilities pub mod error; /// this module defines the generic [`Id`] wrapper and its implementations @@ -92,14 +99,11 @@ pub mod types { #[doc(hidden)] pub mod prelude { - #[doc(no_inline)] pub use crate::error::*; - #[doc(no_inline)] + + pub use crate::cont::prelude::*; pub use crate::id::prelude::*; - #[doc(no_inline)] pub use crate::state::prelude::*; - #[doc(no_inline)] pub use crate::time::prelude::*; - #[doc(no_inline)] pub use crate::types::prelude::*; } diff --git a/core/src/macros/wrapper_ops.rs b/core/src/macros/wrapper_ops.rs new file mode 100644 index 00000000..abd45ac2 --- /dev/null +++ b/core/src/macros/wrapper_ops.rs @@ -0,0 +1,164 @@ +/* + appellation: wrapper_ops + authors: @FL03 +*/ + +/// the [`impl_wrapper_binary!`] macro implements binary operations for a wrapper type. +/// +/// ## Syntax +/// +/// ```no_run +/// impl_wrapper_binary!(WrapperType::<[Op1.call, Op2.call, ...]>); +/// ``` +macro_rules! impl_wrapper_binary { + ($s:ident::<[$($op:ident.$call:ident),* $(,)?]>) => { + $( + impl_wrapper_binary!(@impl $s::$op.$call); + impl_wrapper_binary!(@mut $s::$op.$call); + )* + }; + (@impl $s:ident::$op:ident.$call:ident) => { + impl ::core::ops::$op<$s> for $s + where + A: ::core::ops::$op, + { + type Output = $s; + + fn $call(self, rhs: $s) -> Self::Output { + $s(::core::ops::$op::$call(self.0, rhs.0)) + } + } + + impl<'a, A, B, C> ::core::ops::$op<$s> for &'a $s + where + &'a A: ::core::ops::$op, + { + type Output = $s; + + fn $call(self, rhs: $s) -> Self::Output { + $s(::core::ops::$op::$call(&self.0, rhs.0)) + } + } + + impl<'a, A, B, C> ::core::ops::$op<&'a $s> for &'a $s + where + &'a A: ::core::ops::$op<&'a B, Output = C>, + { + type Output = $s; + + fn $call(self, rhs: &'a $s) -> Self::Output { + $s(::core::ops::$op::$call(&self.0, &rhs.0)) + } + } + + impl<'a, A, B, C> ::core::ops::$op<&'a $s> for $s + where + A: ::core::ops::$op<&'a B, Output = C>, + { + type Output = $s; + + fn $call(self, rhs: &'a $s) -> Self::Output { + $s(::core::ops::$op::$call(self.0, &rhs.0)) + } + } + + impl<'a, A, B, C> ::core::ops::$op<$s> for &'a mut $s + where + &'a A: ::core::ops::$op, + { + type Output = $s; + + fn $call(self, rhs: $s) -> Self::Output { + $s(::core::ops::$op::$call(&self.0, rhs.0)) + } + } + + impl<'a, A, B, C> ::core::ops::$op<&'a mut $s> for $s + where + A: ::core::ops::$op<&'a B, Output = C>, + { + type Output = $s; + + fn $call(self, rhs: &'a mut $s) -> Self::Output { + $s(::core::ops::$op::$call(self.0, &rhs.0)) + } + } + + impl<'a, A, B, C> ::core::ops::$op<&'a mut $s> for &'a mut $s + where + &'a A: ::core::ops::$op<&'a B, Output = C>, + { + type Output = $s; + + fn $call(self, rhs: &'a mut $s) -> Self::Output { + $s(::core::ops::$op::$call(&self.0, &rhs.0)) + } + } + }; + (@mut $s:ident::$op:ident.$call:ident) => { + paste::paste! { + impl_wrapper_binary_mut!(@impl $s::[<$op Assign>].[<$call _assign>]); + } + }; +} + +macro_rules! impl_wrapper_binary_mut { + ($s:ident::<[$($op:ident.$call:ident),* $(,)?]>) => { + $( + impl_wrapper_binary_mut!(@impl $s::$op.$call); + )* + }; + (@impl $s:ident::$op:ident.$call:ident) => { + impl ::core::ops::$op<$s> for &mut $s + where + A: ::core::ops::$op, + { + + fn $call(&mut self, rhs: $s) { + core::ops::$op::$call(&mut self.0, rhs.0) + } + } + }; +} + +macro_rules! impl_wrapper_unary { + ($s:ident::<[$($op:ident.$call:ident),* $(,)?]>) => { + $( + impl_wrapper_unary!(@impl $s::$op.$call); + )* + }; + (@impl $s:ident::$op:ident.$call:ident) => { + impl ::core::ops::$op for $s + where + A: ::core::ops::$op, + { + type Output = $s; + + fn $call(self) -> Self::Output { + $s(core::ops::$op::$call(self.0)) + } + } + + impl<'a, A, B> ::core::ops::$op for &'a $s + where + &'a A: ::core::ops::$op, + { + type Output = $s; + + fn $call(self) -> Self::Output { + $s(core::ops::$op::$call(&self.0)) + } + } + + impl<'a, A, B> ::core::ops::$op for &'a mut $s + where + &'a mut A: ::core::ops::$op, + { + type Output = $s; + + fn $call(self) -> Self::Output { + $s(core::ops::$op::$call(&mut self.0)) + } + } + }; +} diff --git a/core/src/state/impls/impl_wrapper_ops.rs b/core/src/state/impls/impl_wrapper_ops.rs index 6a18f0a7..dd99aef1 100644 --- a/core/src/state/impls/impl_wrapper_ops.rs +++ b/core/src/state/impls/impl_wrapper_ops.rs @@ -38,160 +38,7 @@ where } } -macro_rules! impl_binary_op { - ($s:ident::<[$($op:ident.$call:ident),* $(,)?]>) => { - $( - impl_binary_op!(@impl $s::$op.$call); - impl_binary_op!(@mut $s::$op.$call); - )* - }; - (@impl $s:ident::$op:ident.$call:ident) => { - impl ::core::ops::$op<$s> for $s - where - A: ::core::ops::$op, - { - type Output = $s; - - fn $call(self, rhs: $s) -> Self::Output { - $s(::core::ops::$op::$call(self.0, rhs.0)) - } - } - - impl<'a, A, B, C> ::core::ops::$op<$s> for &'a $s - where - &'a A: ::core::ops::$op, - { - type Output = $s; - - fn $call(self, rhs: $s) -> Self::Output { - $s(::core::ops::$op::$call(&self.0, rhs.0)) - } - } - - impl<'a, A, B, C> ::core::ops::$op<&'a $s> for &'a $s - where - &'a A: ::core::ops::$op<&'a B, Output = C>, - { - type Output = $s; - - fn $call(self, rhs: &'a $s) -> Self::Output { - $s(::core::ops::$op::$call(&self.0, &rhs.0)) - } - } - - impl<'a, A, B, C> ::core::ops::$op<&'a $s> for $s - where - A: ::core::ops::$op<&'a B, Output = C>, - { - type Output = $s; - - fn $call(self, rhs: &'a $s) -> Self::Output { - $s(::core::ops::$op::$call(self.0, &rhs.0)) - } - } - - impl<'a, A, B, C> ::core::ops::$op<$s> for &'a mut $s - where - &'a A: ::core::ops::$op, - { - type Output = $s; - - fn $call(self, rhs: $s) -> Self::Output { - $s(::core::ops::$op::$call(&self.0, rhs.0)) - } - } - - impl<'a, A, B, C> ::core::ops::$op<&'a mut $s> for $s - where - A: ::core::ops::$op<&'a B, Output = C>, - { - type Output = $s; - - fn $call(self, rhs: &'a mut $s) -> Self::Output { - $s(::core::ops::$op::$call(self.0, &rhs.0)) - } - } - - impl<'a, A, B, C> ::core::ops::$op<&'a mut $s> for &'a mut $s - where - &'a A: ::core::ops::$op<&'a B, Output = C>, - { - type Output = $s; - - fn $call(self, rhs: &'a mut $s) -> Self::Output { - $s(::core::ops::$op::$call(&self.0, &rhs.0)) - } - } - }; - (@mut $s:ident::$op:ident.$call:ident) => { - paste::paste! { - impl_binary_op_mut!(@impl $s::[<$op Assign>].[<$call _assign>]); - } - }; -} - -macro_rules! impl_binary_op_mut { - ($s:ident::<[$($op:ident.$call:ident),* $(,)?]>) => { - $( - impl_binary_op!(@impl $s::$op.$call); - )* - }; - (@impl $s:ident::$op:ident.$call:ident) => { - impl ::core::ops::$op<$s> for &mut $s - where - A: ::core::ops::$op, - { - - fn $call(&mut self, rhs: $s) { - core::ops::$op::$call(&mut self.0, rhs.0) - } - } - }; -} - -macro_rules! impl_unary_op { - ($s:ident::<[$($op:ident.$call:ident),* $(,)?]>) => { - $( - impl_unary_op!(@impl $s::$op.$call); - )* - }; - (@impl $s:ident::$op:ident.$call:ident) => { - impl ::core::ops::$op for &mut $s - where - A: Clone + ::core::ops::$op, - { - type Output = $s; - - fn $call(self) -> Self::Output { - $s(core::ops::$op::$call(self.0.clone())) - } - } - - impl<'a, A> ::core::ops::$op for &mut &'a $s - where - A: Clone + ::core::ops::$op, - { - type Output = $s; - - fn $call(self) -> Self::Output { - $s(core::ops::$op::$call(self.0.clone())) - } - } - - impl<'a, A> ::core::ops::$op for &mut &'a mut $s - where - A: Clone + ::core::ops::$op, - { - type Output = $s; - - fn $call(self) -> Self::Output { - $s(core::ops::$op::$call(self.0.clone())) - } - } - }; -} - -impl_binary_op! { +impl_wrapper_binary! { State::<[ Add.add, Sub.sub, @@ -206,7 +53,7 @@ impl_binary_op! { ]> } -impl_unary_op! { +impl_wrapper_unary! { State::<[ Neg.neg, Not.not diff --git a/core/src/state/mod.rs b/core/src/state/mod.rs index be15ce6c..c3952d31 100644 --- a/core/src/state/mod.rs +++ b/core/src/state/mod.rs @@ -114,6 +114,23 @@ mod tests { assert_eq!(state.view().value(), &mut 5_usize); } + #[test] + fn test_option_state() { + let mut state = State::>::none(); + assert_eq!(state.get(), &None); + state.set(Some(5)); + assert_eq!(state.get(), &Some(5)); + } + + #[test] + fn test_nary_state() { + let state = NState::::new(0); + assert!(state.is_kind::>()); + + assert!(!state.is_kind::>()); + assert!(!state.is_kind::>()); + } + #[cfg(feature = "rand")] #[test] fn test_random_state() { diff --git a/core/src/time/impls/impl_timestamp.rs b/core/src/time/impls/impl_timestamp.rs new file mode 100644 index 00000000..c16989b9 --- /dev/null +++ b/core/src/time/impls/impl_timestamp.rs @@ -0,0 +1,91 @@ +/* + appellation: impl_timestamp + authors: @FL03 +*/ +use crate::time::{RawTimestamp, Timestamp}; + +impl AsRef for Timestamp { + fn as_ref(&self) -> &T { + self.get() + } +} + +impl AsMut for Timestamp { + fn as_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl core::borrow::Borrow for Timestamp { + fn borrow(&self) -> &T { + self.get() + } +} + +impl core::borrow::BorrowMut for Timestamp { + fn borrow_mut(&mut self) -> &mut T { + self.get_mut() + } +} + +impl core::ops::Deref for Timestamp { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl core::ops::DerefMut for Timestamp { + fn deref_mut(&mut self) -> &mut Self::Target { + self.get_mut() + } +} + +crate::fmt_wrapper! { + Timestamp( + Binary, + Octal, + LowerHex, + UpperHex, + Display, + Debug, + LowerExp, + UpperExp, + Pointer, + ) +} + +impl From for Timestamp { + fn from(dur: core::time::Duration) -> Self { + Self(dur.as_secs()) + } +} + +impl From for Timestamp { + fn from(dur: core::time::Duration) -> Self { + Self(dur.as_millis()) + } +} + +impl From> for core::time::Duration { + fn from(ts: Timestamp) -> Self { + Self::from_secs(*ts) + } +} + +impl From> for core::time::Duration { + fn from(ts: Timestamp) -> Self { + Self::from_millis(*ts as u64) + } +} + +#[cfg(feature = "chrono")] +impl From> for Timestamp +where + Tz: chrono::TimeZone, +{ + fn from(ts: chrono::DateTime) -> Self { + Self(ts.timestamp()) + } +} diff --git a/core/src/time/impls/impl_timestamp_repr.rs b/core/src/time/impls/impl_timestamp_repr.rs new file mode 100644 index 00000000..aa179b8b --- /dev/null +++ b/core/src/time/impls/impl_timestamp_repr.rs @@ -0,0 +1,45 @@ +/* + appellation: impl_timestamp_repr + authors: @FL03 +*/ +use crate::time::{RawTimestamp, Timestamp}; + +impl Timestamp<&T> +where + T: RawTimestamp, +{ + /// returns a new [`Timestamp`] containing a clone of the current value + pub fn cloned(&self) -> Timestamp + where + T: Clone, + { + Timestamp(self.0.clone()) + } + /// returns a new [`Timestamp`] containing a copy of the current value + pub fn copied(&self) -> Timestamp + where + T: Copy, + { + Timestamp(*self.0) + } +} + +impl Timestamp<&mut T> +where + T: RawTimestamp, +{ + /// returns a new [`Timestamp`] containing a clone of the current value + pub fn cloned(&self) -> Timestamp + where + T: Clone, + { + Timestamp(self.0.clone()) + } + /// returns a new [`Timestamp`] containing a copy of the current value + pub fn copied(&self) -> Timestamp + where + T: Copy, + { + Timestamp(*self.0) + } +} diff --git a/core/src/time/mod.rs b/core/src/time/mod.rs index 8f96b579..7d470d48 100644 --- a/core/src/time/mod.rs +++ b/core/src/time/mod.rs @@ -7,20 +7,26 @@ //! The `time` module provides a set of utilities for working with time and timestamps. #[doc(inline)] #[cfg(feature = "std")] -pub use self::utils::{std_time, systime}; +pub use self::utils::*; #[doc(inline)] -pub use self::{timestamp::Timestamp, types::prelude::*}; +pub use self::{timestamp::Timestamp, traits::*, types::*}; /// this module implements the [`Timestamp`] type pub mod timestamp; -/// this module contains various implementations used to support `time` related features -pub mod types { + +mod impls { + pub mod impl_timestamp; + pub mod impl_timestamp_repr; +} + +mod types { + //! this module contains various implementations used to support `time` related features #[doc(inline)] pub use self::prelude::*; - pub mod datetime; - pub mod epoch; + mod datetime; + mod epoch; - pub(crate) mod prelude { + mod prelude { #[doc(inline)] pub use super::datetime::*; #[doc(inline)] @@ -28,109 +34,43 @@ pub mod types { } } -pub(crate) mod prelude { +mod traits { + //! this moodule implements the core traits supporting the `time` module #[doc(inline)] - pub use super::timestamp::*; - #[doc(inline)] - pub use super::types::prelude::*; - #[cfg(feature = "std")] - pub use super::utils::{std_time, systime}; - #[doc(inline)] - pub use super::{Now, RawTimestamp}; -} - -pub(crate) mod utils { - /// [systime] is a utilitarian function that returns the current system time in milliseconds. - #[cfg(feature = "std")] - #[inline] - pub fn systime() -> core::time::Duration { - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - } - /// [systime] is a utilitarian function that returns the current system time in milliseconds. - #[cfg(feature = "std")] - #[inline] - pub fn std_time() -> u128 { - systime().as_millis() - } -} - -/// The [`Now`] trait provides a common creation routines for all datetime implementations. -pub trait Now { - type Output; - - fn now() -> Self::Output; -} - -/// a private trait used to mark types capable of being uses as a basetype for a [`Timestamp`]. -pub trait RawTimestamp { - private!(); -} - -/* - ************* Implementations ************* -*/ -macro_rules! impl_raw_timestamp { - ($($t:ty),* $(,)?) => { - $( - impl_raw_timestamp!(@impl $t); - )* - }; - (@impl $T:ty) => { - impl RawTimestamp for $T { - seal!(); - } - }; -} - -impl_raw_timestamp! { - u64, - u128, - i64, -} - -impl RawTimestamp for str { - seal!(); -} - -#[cfg(feature = "alloc")] -impl RawTimestamp for alloc::string::String { - seal!(); -} + pub use self::prelude::*; -#[cfg(feature = "std")] -impl Now for u64 { - type Output = Timestamp; + mod now; + mod timestamp; - fn now() -> Self::Output { - Timestamp::new(utils::systime().as_secs()) + mod prelude { + #[doc(inline)] + pub use super::now::*; + #[doc(inline)] + pub use super::timestamp::*; } } -#[cfg(feature = "std")] -impl Now for u128 { - type Output = Timestamp; - - fn now() -> Self::Output { - Timestamp::new(utils::systime().as_millis()) - } +pub(crate) mod prelude { + #[doc(inline)] + pub use super::timestamp::*; + #[doc(inline)] + pub use super::traits::*; + #[doc(inline)] + pub use super::types::*; + #[cfg(feature = "std")] + pub use super::utils::*; } -#[cfg(feature = "chrono")] -impl Now for i64 { - type Output = Timestamp; - - fn now() -> Self::Output { - Timestamp::new(chrono::Local::now().timestamp()) - } -} +mod utils { + #[doc(inline)] + pub use self::prelude::*; -#[cfg(all(feature = "alloc", feature = "chrono"))] -impl Now for alloc::string::String { - type Output = Timestamp; + #[cfg(feature = "std")] + mod base; - fn now() -> Self::Output { - Timestamp::new(chrono::Local::now().to_rfc3339()) + mod prelude { + #[doc(inline)] + #[cfg(feature = "std")] + pub use super::base::*; } } diff --git a/core/src/time/timestamp.rs b/core/src/time/timestamp.rs index 70a403a5..6270e6ad 100644 --- a/core/src/time/timestamp.rs +++ b/core/src/time/timestamp.rs @@ -63,7 +63,7 @@ where } /// consumes the current instance and returns the inner value. #[inline] - pub fn value(self) -> T { + pub fn into_inner(self) -> T { self.0 } /// [`replace`](core::mem::replace) the current value with a new one and return the old one @@ -100,7 +100,7 @@ where F: FnOnce(T) -> U, U: RawTimestamp, { - Timestamp(f(self.value())) + Timestamp(f(self.into_inner())) } /// returns a new instance of the [`Timestamp`] with the current value updated using the given function pub fn map_inplace(&mut self, f: F) -> &mut Self @@ -130,21 +130,32 @@ where Timestamp(self.get_mut()) } } + +#[doc(hidden)] #[allow(deprecated)] impl Timestamp where T: RawTimestamp, { - #[deprecated(since = "0.2.8", note = "use `Timestamp::get` instead")] + #[deprecated( + since = "0.2.8", + note = "use `get` instead; this will be removed in the next major release" + )] pub fn as_ref(&self) -> &T { self.get() } - #[deprecated(since = "0.2.8", note = "use `Timestamp::get_mut` instead")] + #[deprecated( + since = "0.2.8", + note = "use `get_mut` instead; this will be removed in the next major release" + )] pub fn as_mut(&mut self) -> &mut T { self.get_mut() } - #[deprecated(since = "0.2.8", note = "use `Timestamp::value` instead")] - pub fn into_inner(self) -> T { + #[deprecated( + since = "0.3.1", + note = "use `into_innter` instead; this will be removed in the next major release" + )] + pub fn value(self) -> T { self.0 } } @@ -185,89 +196,3 @@ impl Now for Timestamp { Self::new(chrono::Local::now().timestamp()) } } - -impl AsRef for Timestamp { - fn as_ref(&self) -> &T { - self.get() - } -} - -impl AsMut for Timestamp { - fn as_mut(&mut self) -> &mut T { - self.get_mut() - } -} - -impl core::borrow::Borrow for Timestamp { - fn borrow(&self) -> &T { - self.get() - } -} - -impl core::borrow::BorrowMut for Timestamp { - fn borrow_mut(&mut self) -> &mut T { - self.get_mut() - } -} - -impl core::ops::Deref for Timestamp { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl core::ops::DerefMut for Timestamp { - fn deref_mut(&mut self) -> &mut Self::Target { - self.get_mut() - } -} - -crate::fmt_wrapper! { - Timestamp( - Binary, - Octal, - LowerHex, - UpperHex, - Display, - Debug, - LowerExp, - UpperExp, - Pointer, - ) -} - -impl From for Timestamp { - fn from(dur: core::time::Duration) -> Self { - Self(dur.as_secs()) - } -} - -impl From for Timestamp { - fn from(dur: core::time::Duration) -> Self { - Self(dur.as_millis()) - } -} - -impl From> for core::time::Duration { - fn from(ts: Timestamp) -> Self { - Self::from_secs(*ts) - } -} - -impl From> for core::time::Duration { - fn from(ts: Timestamp) -> Self { - Self::from_millis(*ts as u64) - } -} - -#[cfg(feature = "chrono")] -impl From> for Timestamp -where - Tz: chrono::TimeZone, -{ - fn from(ts: chrono::DateTime) -> Self { - Self(ts.timestamp()) - } -} diff --git a/core/src/time/traits/now.rs b/core/src/time/traits/now.rs new file mode 100644 index 00000000..3d21b12d --- /dev/null +++ b/core/src/time/traits/now.rs @@ -0,0 +1,57 @@ +/* + appellation: now + authors: @FL03 +*/ + +/// The [`Now`] trait provides a common creation routines for all datetime implementations. +pub trait Now { + type Output; + + fn now() -> Self::Output; +} + +/* + ************* Implementations ************* +*/ +#[cfg(any(feature = "chrono", feature = "std"))] +use crate::time::Timestamp; +#[cfg(feature = "std")] +use crate::time::utils::systime; +#[cfg(all(feature = "alloc", feature = "chrono"))] +use alloc::string::String; + +#[cfg(feature = "std")] +impl Now for u64 { + type Output = Timestamp; + + fn now() -> Self::Output { + Timestamp::new(systime().as_secs()) + } +} + +#[cfg(feature = "std")] +impl Now for u128 { + type Output = Timestamp; + + fn now() -> Self::Output { + Timestamp::new(systime().as_millis()) + } +} + +#[cfg(feature = "chrono")] +impl Now for i64 { + type Output = Timestamp; + + fn now() -> Self::Output { + Timestamp::new(chrono::Local::now().timestamp()) + } +} + +#[cfg(all(feature = "alloc", feature = "chrono"))] +impl Now for String { + type Output = Timestamp; + + fn now() -> Self::Output { + Timestamp::new(chrono::Local::now().to_rfc3339()) + } +} diff --git a/core/src/time/traits/timestamp.rs b/core/src/time/traits/timestamp.rs new file mode 100644 index 00000000..bac43d92 --- /dev/null +++ b/core/src/time/traits/timestamp.rs @@ -0,0 +1,40 @@ +/* + appellation: timestamp + authors: @FL03 +*/ + +/// a private trait used to mark types capable of being uses as a basetype for a [`Timestamp`]. +pub trait RawTimestamp { + private!(); +} + +/* + ************* Implementations ************* +*/ +macro_rules! impl_raw_timestamp { + ($($t:ty),* $(,)?) => { + $( + impl_raw_timestamp!(@impl $t); + )* + }; + (@impl $T:ty) => { + impl RawTimestamp for $T { + seal!(); + } + }; +} + +impl_raw_timestamp! { + u64, + u128, + i64, +} + +impl RawTimestamp for str { + seal!(); +} + +#[cfg(feature = "alloc")] +impl RawTimestamp for alloc::string::String { + seal!(); +} diff --git a/core/src/time/utils/base.rs b/core/src/time/utils/base.rs new file mode 100644 index 00000000..7fa105d5 --- /dev/null +++ b/core/src/time/utils/base.rs @@ -0,0 +1,12 @@ +/// [systime] is a utilitarian function that returns the current system time in milliseconds. +#[inline] +pub fn systime() -> core::time::Duration { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() +} +/// [systime] is a utilitarian function that returns the current system time in milliseconds. +#[inline] +pub fn std_time() -> u128 { + systime().as_millis() +} diff --git a/core/tests/state.rs b/core/tests/state.rs deleted file mode 100644 index 8c14325f..00000000 --- a/core/tests/state.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - Appellation: state - Contrib: FL03 -*/ -extern crate scsys_core as scsys; - -use scsys::state::{NState, Nary, State}; - -#[test] -fn test_option_state() { - let mut state = State::>::none(); - assert_eq!(state.get(), &None); - state.set(Some(5)); - assert_eq!(state.get(), &Some(5)); -} - -#[test] -fn test_nary_state() { - let state = NState::::new(0); - assert!(state.is_kind::>()); - - assert!(!state.is_kind::>()); - assert!(!state.is_kind::>()); -} diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 8a5f557a..79cef151 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -14,13 +14,6 @@ repository.workspace = true rust-version.workspace = true version.workspace = true -[lib] -crate-type = [ "cdylib", "rlib" ] -bench = false -doc = true -doctest = true -test = true - [package.metadata.docs.rs] all-features = false features = ["full"] @@ -31,6 +24,16 @@ version = "v{{version}}" no-dev-version = true tag-name = "{{version}}" +[lib] +crate-type = [ + "cdylib", + "rlib", +] +bench = false +doc = true +doctest = true +test = true + [dependencies] # sdk scsys-core = { workspace = true } @@ -67,26 +70,30 @@ strum = { workspace = true } tracing = { optional = true, workspace = true } [features] -default = [ +default = [ "blake3", "hash", "std", ] -full = [ +full = [ "default", "anyhow", "bincode", "chrono", "rayon", "json", - "rand", - "serde", + "rand", + "serde", "time", "tracing", "uuid", ] +nightly = [ + "scsys-core/nightly", +] + # ************* [FF:Features] ************* hash = [ "digest", @@ -115,11 +122,11 @@ std = [ "uuid?/std", ] -wasi = [ +wasi = [ "scsys-core/wasi", ] -wasm = [ +wasm = [ "scsys-core/wasm", "chrono?/wasmbind", "getrandom?/wasm_js", @@ -128,12 +135,12 @@ wasm = [ ] # ************* [FF:Dependencies] ************* -anyhow = [ +anyhow = [ "dep:anyhow", "scsys-core/anyhow", ] -alloc = [ +alloc = [ "bincode?/alloc", "chrono?/alloc", "digest?/alloc", @@ -146,13 +153,13 @@ alloc = [ blake3 = ["dep:blake3"] -bincode = [ - "dep:bincode", +bincode = [ + "dep:bincode", "dep:bincode_derive", ] -chrono = [ - "dep:chrono", +chrono = [ + "dep:chrono", "chrono?/clock", "scsys-core/chrono", "time", @@ -183,13 +190,13 @@ rng = [ "uuid?/v4", ] -serde = [ - "dep:serde", - "dep:serde_derive", +serde = [ + "dep:serde", + "dep:serde_derive", "bincode?/serde", "chrono?/serde", "generic-array/serde", - "rand?/serde", + "rand?/serde", "scsys-core/serde", "uuid?/serde", "time?/serde", @@ -197,7 +204,7 @@ serde = [ serde_json = ["dep:serde_json"] -time = [ +time = [ "dep:time", "scsys-core/time", ] @@ -219,4 +226,4 @@ name = "default" [[test]] name = "h256" -required-features = [ "hash", "rand", "std" ] \ No newline at end of file +required-features = [ "hash", "rand", "std" ] diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 1f2d4ba6..f5379ae4 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -14,6 +14,16 @@ repository.workspace = true rust-version.workspace = true version.workspace = true +[package.metadata.docs.rs] +all-features = false +features = ["default"] +rustc-args = ["--cfg", "docsrs"] +version = "v{{version}}" + +[package.metadata.release] +no-dev-version = true +tag-name = "{{version}}" + [lib] bench = false doc = true @@ -21,16 +31,19 @@ doctest = false proc-macro = true test = false -[package.metadata.docs.rs] -all-features = true -rustc-args = ["--cfg", "docsrs"] - - [dependencies] proc-macro2 = "1" quote = "1" syn = { features = ["full"], version = "2" } +[features] +default = [] + +nightly = ["proc-macro2/nightly"] + # ************* [Unit Tests] ************* +[[test]] +name = "gsw" + [[test]] name = "variants" diff --git a/derive/src/impls/getter.rs b/derive/src/impls/getter.rs index dee62a49..6fb8ddb5 100644 --- a/derive/src/impls/getter.rs +++ b/derive/src/impls/getter.rs @@ -3,9 +3,8 @@ authors: @FL03 */ use proc_macro2::TokenStream; -use quote::quote; +use quote::{format_ident, quote}; use syn::DeriveInput; -use syn::spanned::Spanned; pub fn impl_getter(input: &DeriveInput) -> TokenStream { let name = &input.ident; @@ -34,6 +33,35 @@ pub fn impl_getter(input: &DeriveInput) -> TokenStream { } } +pub fn impl_getter_mut(input: &DeriveInput) -> TokenStream { + let name = &input.ident; + let generics = &input.generics; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let fields = match &input.data { + syn::Data::Struct(data) => &data.fields, + _ => panic!("Getter can only be derived for structs"), + }; + + let getters = fields.iter().map(|field| { + let field_name = &field.ident; + let field_type = &field.ty; + + let callback = format_ident!("{}_mut", field_name.as_ref().unwrap()); + quote! { + pub const fn #callback(&mut self) -> &mut #field_type { + &mut self.#field_name + } + } + }); + + quote! { + impl #impl_generics #name #ty_generics #where_clause { + #(#getters)* + } + } +} + pub fn impl_set(input: &DeriveInput) -> TokenStream { let name = &input.ident; let generics = &input.generics; @@ -48,10 +76,7 @@ pub fn impl_set(input: &DeriveInput) -> TokenStream { let field_name = &field.ident; let field_type = &field.ty; - let setter_name = syn::Ident::new( - &format!("set_{}", field_name.as_ref().unwrap()), - field_name.span(), - ); + let setter_name = format_ident!("set_{}", field_name.as_ref().unwrap()); quote! { pub fn #setter_name(&mut self, value: #field_type) { @@ -81,10 +106,7 @@ pub fn impl_with(input: &DeriveInput) -> TokenStream { let field_name = &field.ident; let field_type = &field.ty; - let with_name = syn::Ident::new( - &format!("with_{}", field_name.as_ref().unwrap()), - field_name.span(), - ); + let with_name = format_ident!("with_{}", field_name.as_ref().unwrap()); quote! { pub fn #with_name(mut self, value: #field_type) -> Self { diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 42affde4..747cc37c 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -27,6 +27,44 @@ pub(crate) mod ast; use proc_macro::TokenStream; use syn::{Data, DeriveInput, parse_macro_input}; +/// the [`Display`] macro automatically implements the [`Display`](core::fmt::Display) trait +/// for a struct or enum, using the `scsys` attributes to customize the output. +/// +/// ## Examples +/// +/// ### _Example #1: Using the `json` attribute_ +/// +/// ```rust +/// use scsys_derive::Display; +/// +/// #[derive(Display, serde::Deserialize, serde::Serialize)] +/// #[scsys(json)] +/// pub struct MyStruct { +/// pub name: String, +/// pub age: u32, +/// } +/// +/// fn main() { +/// let my_struct = MyStruct { +/// name: "Alice".to_string(), +/// age: 30, +/// }; +/// +/// // This will print the struct in JSON format +/// println!("{}", my_struct); +/// } +/// ``` +/// +/// #### Output: +/// +/// ```json +/// {"name": "Alice", "age": 30} +/// ``` +/// +/// +/// **note:** for now, the primary use case is to automatically implement the `Display` trait +/// for implementors of both `Deserialize` and `Serialize` from [`serde`](https://serde.rs), +/// #[proc_macro_derive(Display, attributes(scsys))] pub fn display(input: TokenStream) -> TokenStream { // Parse the inputs into the proper struct @@ -62,19 +100,40 @@ pub fn wrapper(input: TokenStream) -> TokenStream { } #[proc_macro_derive(Getter, attributes(scsys))] -pub fn getter_derive(input: TokenStream) -> TokenStream { +pub fn derive_getter(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let get = impls::impl_getter(&input); + let get_mut = impls::impl_getter_mut(&input); + + let merged = quote::quote! { + #get + #get_mut + }; + + merged.into() +} + +#[proc_macro_derive(Get, attributes(scsys))] +pub fn derive_get(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); impls::impl_getter(&input).into() } +#[proc_macro_derive(GetMut, attributes(scsys))] +pub fn derive_get_mut(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + impls::impl_getter_mut(&input).into() +} + #[proc_macro_derive(Set, attributes(scsys))] -pub fn set_derive(input: TokenStream) -> TokenStream { +pub fn derive_set(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); impls::impl_set(&input).into() } #[proc_macro_derive(With, attributes(scsys))] -pub fn with_derive(input: TokenStream) -> TokenStream { +pub fn derive_with(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); impls::impl_with(&input).into() } diff --git a/derive/tests/gsw.rs b/derive/tests/gsw.rs new file mode 100644 index 00000000..f949a2d0 --- /dev/null +++ b/derive/tests/gsw.rs @@ -0,0 +1,63 @@ +/* + Appellation: derive + Contrib: @FL03 +*/ +extern crate scsys_derive as scsys; + +#[test] +fn test_gsw() { + let mut data = Basic::default().with_key("key".to_string()).with_weight(1); + + assert_eq!(data.key(), "key"); + assert_eq!(*data.weight(), 1); + + data.set_key("new_key".to_string()); + data.set_weight(2); + + assert_eq!(data.key(), "new_key"); + assert_eq!(*data.weight(), 2); + + let data: Generic<&str, f64> = Generic::default().with_key("key").with_weight(1.00); + + assert_eq!(*data.key(), "key"); + assert_eq!(*data.weight(), 1.00); +} + +#[derive( + Clone, + Debug, + Default, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + scsys::Get, + scsys::GetMut, + scsys::Set, + scsys::With, +)] +pub struct Basic { + pub key: String, + pub weight: usize, +} + +#[derive( + Clone, + Copy, + Debug, + Default, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + scsys::Get, + scsys::GetMut, + scsys::Set, + scsys::With, +)] +pub struct Generic { + pub key: K, + pub weight: V, +} diff --git a/derive/tests/variants.rs b/derive/tests/variants.rs index 0293c392..314d0f79 100644 --- a/derive/tests/variants.rs +++ b/derive/tests/variants.rs @@ -4,43 +4,6 @@ */ extern crate scsys_derive as scsys; -#[derive( - Clone, - Debug, - Default, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - scsys::Getter, - scsys::Set, - scsys::With, -)] -pub struct Basic { - pub key: String, - pub weight: usize, -} - -#[derive( - Clone, - Copy, - Debug, - Default, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - scsys::Getter, - scsys::Set, - scsys::With, -)] -pub struct Generic { - pub key: K, - pub weight: V, -} - #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, scsys::VariantConstructors)] pub enum Something { A, @@ -48,25 +11,6 @@ pub enum Something { C { x: usize, y: usize }, } -#[test] -fn test_gsw() { - let mut data = Basic::default().with_key("key".to_string()).with_weight(1); - - assert_eq!(data.key(), "key"); - assert_eq!(*data.weight(), 1); - - data.set_key("new_key".to_string()); - data.set_weight(2); - - assert_eq!(data.key(), "new_key"); - assert_eq!(*data.weight(), 2); - - let data: Generic<&str, f64> = Generic::default().with_key("key").with_weight(1.00); - - assert_eq!(*data.key(), "key"); - assert_eq!(*data.weight(), 1.00); -} - #[test] fn test_variant_constructors() { assert_eq!(Something::a(), Something::A); diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 699f5d16..e9b6569c 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -14,6 +14,16 @@ repository.workspace = true rust-version.workspace = true version.workspace = true +[package.metadata.docs.rs] +all-features = false +features = ["default"] +rustc-args = [ "--cfg", "docsrs" ] +version = "v{{version}}" + +[package.metadata.release] +no-dev-version = true +tag-name = "{{version}}" + [lib] bench = false doc = true @@ -21,12 +31,12 @@ doctest = false proc-macro = true test = false -[package.metadata.docs.rs] -all-features = true -rustc-args = ["--cfg", "docsrs"] - [dependencies] proc-macro2 = "1" quote = "1" syn = { features = ["full"], version = "2" } +[features] +default = [] + +nightly = ["proc-macro2/nightly"] diff --git a/scsys/Cargo.toml b/scsys/Cargo.toml index ee98aa84..43dc19da 100644 --- a/scsys/Cargo.toml +++ b/scsys/Cargo.toml @@ -62,7 +62,6 @@ default = [ full = [ "default", - "config", "crypto", "hash", "json", @@ -71,6 +70,16 @@ full = [ "time", ] +nightly = [ + "scsys-core/nightly", + "scsys-config?/nightly", + "scsys-crypto?/nightly", + "scsys-derive?/nightly", + "scsys-macros?/nightly", + "scsys-traits?/nightly", + "scsys-util?/nightly", +] + # ************* [FF:Features] ************* config = [ "dep:scsys-config", @@ -207,16 +216,22 @@ required-features = ["std"] # ************* [Examples] ************* [[example]] name = "derive" -required-features = [ - "derive", - "json", +required-features = [ + "derive", + "std", + "time" ] +[[example]] +name = "variants" +required-features = [ + "derive", +] # ************* [Unit Tests] ************* [[test]] name = "derive" -required-features = [ - "derive", +required-features = [ + "derive", "json", ] diff --git a/scsys/examples/derive.rs b/scsys/examples/derive.rs index c1b7d5a8..fbcdcb18 100644 --- a/scsys/examples/derive.rs +++ b/scsys/examples/derive.rs @@ -5,23 +5,11 @@ use scsys::time::Timestamp; fn main() -> scsys::Result<()> { - let mut unit = Unit::::new(42); - println!("Unit: {unit}"); - let prev = unit.replace(100); - println!("Unit: replaced {prev} with {unit}"); - let value = unit.take(); - println!("Taken unit: {value}"); - let u2 = unit.map(|x| (x + 1) * 2); - println!("Unit value: {}", u2.get()); - let params = Sample::from_value(0.5); - println!("Params: {params}"); - let variant = Something::a(); - println!("Variant: {:?}", variant); - let variant = Something::b(42); - println!("Variant: {:?}", variant); - let variant = Something::c(1, 2); - println!("Variant: {:?}", variant); + // This will print the struct in JSON format (if `serde` feature is enabled) + println!("{}", params); + // use the getter to access the value + println!("Timestamp: {}", params.timestamp()); Ok(()) } @@ -35,19 +23,10 @@ fn main() -> scsys::Result<()> { Ord, PartialEq, PartialOrd, - scsys::Wrapper, scsys::Display, -)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(default, rename_all = "snake_case"), - scsys(display(json)) -)] -pub struct Unit(pub T); - -#[derive( - Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, scsys::Getter, scsys::Display, + scsys::Get, + scsys::GetMut, + scsys::Set, )] #[cfg_attr( feature = "serde", @@ -66,10 +45,3 @@ impl Sample { Self { timestamp, value } } } - -#[derive(Clone, Copy, Debug, scsys::VariantConstructors)] -pub enum Something { - A, - B(usize), - C { x: usize, y: usize }, -} diff --git a/scsys/examples/variants.rs b/scsys/examples/variants.rs new file mode 100644 index 00000000..e55bb2e0 --- /dev/null +++ b/scsys/examples/variants.rs @@ -0,0 +1,21 @@ +/* + Appellation: variants + Contrib: FL03 +*/ + +fn main() -> scsys::Result<()> { + let a = Something::a(); + println!("Variant: {a:?}"); + let b = Something::b(42); + println!("Variant: {b:?}"); + let c = Something::c(1, 2); + println!("Variant: {c:?}"); + Ok(()) +} + +#[derive(Clone, Copy, Debug, scsys::VariantConstructors)] +pub enum Something { + A, + B(usize), + C { x: usize, y: usize }, +} diff --git a/scsys/src/lib.rs b/scsys/src/lib.rs index 75f7817b..0abdcb4f 100644 --- a/scsys/src/lib.rs +++ b/scsys/src/lib.rs @@ -16,16 +16,19 @@ //! its overall footprint through modularization and feature-gating. These characteristics make //! it suitable for use both within the ecosystem and outside of it. //! -#![crate_name = "scsys"] #![crate_type = "lib"] +#![crate_name = "scsys"] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(feature = "alloc", feature = "nightly"), feature(allocator_api))] #![doc( html_logo_url = "https://raw.githubusercontent.com/scattered-systems/.github/main/assets/logo.png", html_favicon_url = "https://raw.githubusercontent.com/scattered-systems/.github/main/assets/favicon.ico" )] - -#[cfg(feature = "alloc")] -extern crate alloc; +#![allow( + clippy::module_inception, + clippy::needless_doctest_main, + clippy::should_implement_trait +)] #[doc(inline)] pub use scsys_core::*; @@ -56,6 +59,7 @@ pub use scsys_traits::prelude::*; #[cfg(feature = "utils")] pub use scsys_util as utils; +#[doc(hidden)] pub mod prelude { pub use scsys_core::prelude::*; diff --git a/scsys/tests/derive.rs b/scsys/tests/derive.rs index 210be0be..5549604e 100644 --- a/scsys/tests/derive.rs +++ b/scsys/tests/derive.rs @@ -55,7 +55,8 @@ mod impls { Ord, PartialEq, PartialOrd, - scsys::Getter, + scsys::Get, + scsys::GetMut, scsys::Set, scsys::With, )] @@ -74,7 +75,8 @@ mod impls { Ord, PartialEq, PartialOrd, - scsys::Getter, + scsys::Get, + scsys::GetMut, scsys::Set, scsys::With, )] @@ -102,8 +104,8 @@ mod impls { Ord, PartialEq, PartialOrd, - scsys::Wrapper, scsys::Display, + scsys::Wrapper, )] #[cfg_attr( feature = "serde", diff --git a/traits/Cargo.toml b/traits/Cargo.toml index f181cd14..31e27cd8 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -26,7 +26,7 @@ tag-name = "{{version}}" [lib] crate-type = [ - "cdylib", + "cdylib", "rlib", ] bench = false @@ -47,6 +47,8 @@ full = [ "default" ] +nightly = [] + # ************* [FF:Environments] ************* std = [ "alloc", @@ -62,4 +64,4 @@ alloc = [] # ************* [Unit Tests] ************* [[test]] -name = "default" \ No newline at end of file +name = "default" diff --git a/traits/src/cont.rs b/traits/src/cont.rs deleted file mode 100644 index 8e6f24dc..00000000 --- a/traits/src/cont.rs +++ /dev/null @@ -1,25 +0,0 @@ -/* - appellation: cont - authors: @FL03 -*/ -//! this module implements a set of traits used to establish a common interface for containers -//! and other generic storage types. -#[doc(inline)] -pub use self::prelude::*; - -pub mod container; -pub mod entry; -pub mod get; -pub mod hkt; -pub mod store; - -pub(crate) mod prelude { - #[doc(inline)] - pub use super::container::*; - #[doc(inline)] - pub use super::get::*; - #[doc(inline)] - pub use super::hkt::*; - #[doc(inline)] - pub use super::store::*; -} diff --git a/traits/src/cont/container.rs b/traits/src/cont/container.rs deleted file mode 100644 index 93e9c8de..00000000 --- a/traits/src/cont/container.rs +++ /dev/null @@ -1,97 +0,0 @@ -/* - Appellation: container - Contrib: @FL03 -*/ -use super::GetMut; - -/// [`RawContainer`] defines a standard interface for all _containers_ that are used to store -/// other entities. -pub trait RawContainer { - type Item; -} - -pub trait KeyedContainer: Container -where - Self::Cont: GetMut, -{ - type Key; -} - -/// The [`Container`] trait is a higher-level abstraction over [`RawContainer`]. -pub trait Container { - type Cont: RawContainer + ?Sized; -} - -/* - ************* Implementations ************* -*/ -impl RawContainer for [T] { - type Item = T; -} - -impl RawContainer for &[T] { - type Item = T; -} - -impl RawContainer for &mut [T] { - type Item = T; -} - -impl Container for [T] { - type Cont = [V]; -} - -macro_rules! impl_container { - ($($($t:ident)::*<$T:ident>),* $(,)?) => { - $( - impl_container!(@impl $($t)::*<$T>); - )* - }; - (@impl $($t:ident)::*<$T:ident>) => { - impl_container!(@raw $($t)::*<$T>); - impl_container!(@cont $($t)::*<$T>); - }; - (@raw $($t:ident)::*<$lt:lifetime, $T:ident>) => { - impl<$T> RawContainer for $($t)::*<$lt, $T> { - type Item = $T; - } - }; - (@raw $($t:ident)::*<$T:ident>) => { - impl<$T> RawContainer for $($t)::*<$T> { - type Item = $T; - } - }; - (@cont $($t:ident)::*<$T:ident>) => { - impl<$T> Container<$T> for $($t)::*<$T> { - type Cont = $($t)::*; - } - }; -} - -impl_container! { - Option -} - -#[cfg(feature = "alloc")] -impl_container! { - alloc::vec::Vec, - alloc::boxed::Box, - alloc::rc::Rc, - alloc::rc::Weak, - alloc::sync::Arc, - alloc::collections::BinaryHeap, - alloc::collections::BTreeSet, - alloc::collections::LinkedList, - alloc::collections::VecDeque, -} - -#[cfg(feature = "std")] -impl_container! { - std::cell::Cell, - std::cell::OnceCell, - std::cell::RefCell, - std::sync::Mutex, - std::sync::RwLock, - std::sync::LazyLock, - std::collections::HashSet, -} diff --git a/traits/src/cont/entry.rs b/traits/src/cont/entry.rs deleted file mode 100644 index 1f0d8f92..00000000 --- a/traits/src/cont/entry.rs +++ /dev/null @@ -1,69 +0,0 @@ -/* - appellation: entry - authors: @FL03 -*/ - -/// The [`Entry`] trait seeks to establish a common interface for all elements within a -/// so-called _keyed_ container; i.e. on in which elements can be accessed by any type of key. -pub trait Entry<'a> { - type Key; - type Value; - /// returns a reference to the key of the entry. - fn key(&self) -> &Self::Key; - /// if the entry does not exist, insert the provided value and return a mutable reference - /// to it - fn or_insert(self, default: Self::Value) -> &'a mut Self::Value; - /// if the entry does not exist, insert the value returned by the provided function and - /// return a mutable reference to it. - fn or_insert_with(self, f: F) -> &'a mut Self::Value - where - F: FnOnce() -> Self::Value; -} -/// The [`OrInsert`] trait is a convenience trait that allows for the insertion of some value -/// whenever the entry does not already exist within the container. -pub trait OrInsert { - fn or_insert(&mut self, key: K, value: V) -> V; -} - -/* - ************* Implementations ************* -*/ - -#[allow(unused_macros)] -macro_rules! entry { - ($($prefix:ident)::* -> $call:ident($($arg:tt),*)) => { - $($prefix)::*::Entry::$call($($arg),*) - }; -} - -#[allow(unused_macros)] -macro_rules! impl_entry { - ($($prefix:ident)::* $(where $($preds:tt)*)?) => { - - impl<'a, K, V> Entry<'a> for $($prefix)::*::Entry<'a, K, V> $(where $($preds)*)? { - type Key = K; - type Value = V; - - fn key(&self) -> &Self::Key { - entry!($($prefix)::* -> key(self)) - } - - fn or_insert(self, default: Self::Value) -> &'a mut Self::Value { - entry!($($prefix)::* -> or_insert(self, default)) - } - - fn or_insert_with(self, f: F) -> &'a mut Self::Value - where - F: FnOnce() -> Self::Value, - { - entry!($($prefix)::* -> or_insert_with(self, f)) - } - } - - }; -} - -#[cfg(feature = "alloc")] -impl_entry!(alloc::collections::btree_map where K: Ord); -#[cfg(feature = "std")] -impl_entry!(std::collections::hash_map where K: Eq + core::hash::Hash); diff --git a/traits/src/cont/hkt/old.rs b/traits/src/cont/hkt/old.rs deleted file mode 100644 index 6182759d..00000000 --- a/traits/src/cont/hkt/old.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* - appellation: old - authors: @FL03 -*/ - -#[allow(clippy::upper_case_acronyms)] -pub trait HKT { - type C; // Current Type - type T; // Type C swapped with U -} - -pub trait Functor: HKT { - fn fmap(self, f: F) -> Self::T - where - F: Fn(Self::C) -> U; -} - -pub trait Applicative: Functor { - fn pure_(value: U) -> Self::T - where - Self: HKT; - fn seq(&self, fs: >::T) -> >::T - where - F: Fn(&>::C) -> U, - Self: HKT; -} - -pub trait Monad: Applicative { - fn return_(x: U) -> Self::T - where - Self: HKT, - { - Self::pure_(x) - } - - fn bind(&self, fs: F) -> Self::T - where - F: FnMut(&Self::C) -> Self::T; - - fn join(&self) -> T - where - Self: HKT, - T: Clone, - { - self.bind(|x| x.clone()) - } -} diff --git a/traits/src/cont/store.rs b/traits/src/cont/store.rs deleted file mode 100644 index cbe3267f..00000000 --- a/traits/src/cont/store.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* - Appellation: store - Contrib: FL03 -*/ - -pub trait Store { - fn get(&self, key: &K) -> Option<&V>; - - fn get_mut(&mut self, key: &K) -> Option<&mut V>; - - fn insert(&mut self, key: K, value: V) -> Option; - - fn remove(&mut self, key: &K) -> Option; -} - -/* - ********* Implementations ********* -*/ -#[allow(unused_macros)] -macro_rules! impl_store { - ($t:ty, where $($preds:tt)* ) => { - - impl Store for $t where $($preds)* { - fn get(&self, key: &K) -> Option<&V> { - <$t>::get(self, &key) - } - - fn get_mut(&mut self, key: &K) -> Option<&mut V> { - <$t>::get_mut(self, &key) - } - - fn insert(&mut self, key: K, value: V) -> Option { - <$t>::insert(self, key, value) - } - - fn remove(&mut self, key: &K) -> Option { - <$t>::remove(self, &key) - } - } - - }; -} - -#[cfg(feature = "alloc")] -impl_store!(alloc::collections::BTreeMap, where K: Ord); -#[cfg(feature = "std")] -impl_store!(std::collections::HashMap, where K: Eq + core::hash::Hash); diff --git a/traits/src/dtype.rs b/traits/src/dtype.rs index 082de36c..0c694c7b 100644 --- a/traits/src/dtype.rs +++ b/traits/src/dtype.rs @@ -12,12 +12,19 @@ where { core::any::TypeId::of::() == core::any::TypeId::of::() } - +/// The [`TypeOf`] trait automatically provides a way to check if a type is of a specific type +/// at compile time. This is useful for generic programming and type checking. pub trait TypeOf { + private!(); + fn of() -> bool; } - +/// The [`IsType`] trait provides a method to check if the current type is of a specific type +/// at runtime. This is useful for dynamic type checking in scenarios where the type may not be +/// known at compile time. pub trait IsType { + private!(); + fn is(&self) -> bool where T: 'static, @@ -29,7 +36,7 @@ pub trait IsType { /// [`DType`] is a trait designed to provide additional information regarding the type of a /// particular value. -pub trait DType: Any { +pub trait DType: 'static + IsType + TypeOf { private!(); fn type_id(&self) -> TypeId { @@ -44,15 +51,35 @@ pub trait DType: Any { /* ************* Implementations ************* */ +impl DType for T +where + T: 'static, +{ + seal!(); + + fn type_id(&self) -> TypeId { + Any::type_id(self) + } + + fn type_name(&self) -> &'static str { + core::any::type_name::() + } +} + impl TypeOf for T where - T: Any, + T: 'static, { + seal!(); + fn of() -> bool { type_of::() } } -impl IsType for T {} - -impl dyn DType {} +impl IsType for T +where + T: 'static, +{ + seal!(); +} diff --git a/traits/src/lib.rs b/traits/src/lib.rs index 2a740208..61dd4f0e 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -6,11 +6,13 @@ //! //! A collection of useful traits designed to be used throughout the ecosystem. //! +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(feature = "alloc", feature = "nightly"), feature(allocator_api))] #![doc( html_logo_url = "https://raw.githubusercontent.com/scattered-systems/.github/main/assets/logo.png", html_favicon_url = "https://raw.githubusercontent.com/scattered-systems/.github/main/assets/favicon.ico" )] -#![cfg_attr(not(feature = "std"), no_std)] +#![crate_type = "lib"] #[cfg(feature = "alloc")] extern crate alloc; @@ -24,8 +26,6 @@ pub(crate) mod macros { pub mod seal; } -pub mod cont; - pub mod convert; pub mod dtype; pub mod named; @@ -51,8 +51,6 @@ pub mod ops { #[doc(hidden)] pub mod prelude { - #[doc(inline)] - pub use crate::cont::prelude::*; #[doc(inline)] pub use crate::ops::prelude::*; diff --git a/utils/Cargo.toml b/utils/Cargo.toml index 36854851..a026b647 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -14,8 +14,22 @@ repository.workspace = true rust-version.workspace = true version.workspace = true +[package.metadata.docs.rs] +all-features = false +features = ["full"] +rustc-args = [ "--cfg", "docsrs" ] +version = "v{{version}}" + +[package.metadata.release] +no-dev-version = true +tag-name = "{{version}}" + [lib] -crate-type = ["cdylib", "rlib"] +crate-type = [ + "cdylib", + "rlib", + "staticlib", +] bench = false doc = true doctest = true @@ -39,11 +53,6 @@ strum = { workspace = true } # tracing tracing = { optional = true, workspace = true } -[package.metadata.docs.rs] -all-features = false -features = ["full"] -rustc-args = ["--cfg", "docsrs"] - [features] default = [ "std", @@ -58,6 +67,8 @@ full = [ "tracing", ] +nightly = [] + # ************* [FF:Features] ************* fs = [ "std", @@ -114,4 +125,4 @@ tracing = ["dep:tracing"] # ************* [Unit Tests] ************* [[test]] name = "casing" -required-features = ["alloc"] \ No newline at end of file +required-features = ["alloc"] diff --git a/utils/src/lib.rs b/utils/src/lib.rs index c1ce609d..d0af37b8 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -6,6 +6,7 @@ //! //! utilities for the `scsys` ecosystem #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(all(feature = "alloc", feature = "nightly"), feature(allocator_api))] #![doc( html_logo_url = "https://raw.githubusercontent.com/scattered-systems/.github/main/assets/logo.png", html_favicon_url = "https://raw.githubusercontent.com/scattered-systems/.github/main/assets/favicon.ico"