diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index bed7cd6..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,5 +0,0 @@ -[alias] -xtask = "run --manifest-path ./xtask/Cargo.toml --" - -[registries] -space-crates = { index = "sparse+https://crates.pkg.jetbrains.space/scsys/p/neo/crates/" } diff --git a/.config/default.config.toml b/.config/default.config.toml deleted file mode 100644 index 1459f5d..0000000 --- a/.config/default.config.toml +++ /dev/null @@ -1,29 +0,0 @@ -mode = "development" - -[cache] -database = 0 -host = "localhost" -password = "" -port = 6379 -username = "" - - -[database] # Standard database connection parameters -database = "postgres" -enabled = true -host = "localhost" -provider = "postgres" -port = 5432 - -[logger] -level = "info" - -[network] - -[network.mainnet] - -[network.subnet] -addr = "/ip4/0.0.0.0/tcp/9001" -seed = 9 - -[system] diff --git a/.docker/env.dockerfile b/.docker/env.dockerfile deleted file mode 100644 index c9c623a..0000000 --- a/.docker/env.dockerfile +++ /dev/null @@ -1,48 +0,0 @@ -FROM rust:latest as base - -RUN apt-get update -y && apt-get upgrade -y - -FROM base as builder-base - -RUN apt-get install -y \ - protobuf-compiler - -RUN rustup update && \ - rustup default nightly && \ - rustup target add wasm32-unknown-unknown wasm32-wasi --toolchain nightly - -FROM builder-base as builder - -ENV CARGO_TERM_COLOR=always - -ADD . /workspace -WORKDIR /workspace - -COPY . . -RUN cargo build --release -v --workspace - -FROM debian:buster-slim as runner-base - -ENV RUST_LOG="info" \ - SERVER_PORT=8080 - -RUN apt-get update -y && apt-get upgrade -y - -RUN apt-get install -y \ - protobuf-compiler - -RUN mkdir data -VOLUME [ "/data" ] - -COPY --chown=55 .config /config -VOLUME [ "/config" ] - -COPY --chown=55 --from=builder /workspace/target/release/app /bin/app - -FROM runner-base as runner - -EXPOSE 80 -EXPOSE ${SERVER_PORT} - -ENTRYPOINT [ "app" ] -CMD [ "-h" ] \ No newline at end of file diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index d67ca73..0000000 --- a/.dockerignore +++ /dev/null @@ -1,49 +0,0 @@ -# Directories -**/__pycache__/ -**/__sapper__/ - -**/.DS_STORE/ -**/.artifacts/data/ -**/.docker/data/ -**/.idea/ -**/.pytest_cache/ -**/.svelte-kit/ -**/.vscode/ - -**/artifacts/ -**/build/ -**/debug/ -**/dist/ -**/env/ -**/node_modules/ -**/pkg/ -**/target/ -**/tmp/ -**/venv/ - -# File Extensions -**/*.bk -**/*.bk.* - -**/*.csv -**/*.csv.* - -**/*.db -**/*.db.* - -**/*.db-*.* - -**/*.lock -**/*.lock.* - -**/*-lock.* - -**/*.log -**/*.log.* - -**/*.whl -**/*.whl.* - -**/*.zip -**/*.zip.* - diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e62c742 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,36 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = crlf +indent_style = space +indent_size = 4 +insert_final_newline = false +quote_type = double +trim_trailing_whitespace = false + +[*.{ts,tsx,js,jsx,cjs,mjs}] +indent_size = 2 +quote_type = single + +[*.{json,yaml,yml,toml}] +indent_size = 2 + +[*.md] +insert_final_newline = true +trim_trailing_whitespace = true + +[*.rs] +indent_size = 4 + +[*.{html,htm,xml}] +indent_size = 2 + +[*.{css,scss,sass,less}] +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true + diff --git a/.env.example b/.env.example deleted file mode 100644 index c82910f..0000000 --- a/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -NOTION_SECRET_KEY="" -OPENAI_API_KEY="" \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1b4b5b0..c6d5a32 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,34 +1,20 @@ -version: 2 -updates: - - package-ecosystem: cargo - directory: / - schedule: - interval: daily - - package-ecosystem: docker - directory: /.docker - schedule: - interval: daily - - package-ecosystem: github-actions - directory: / - schedule: - interval: daily - - package-ecosystem: cargo - directory: /contained - schedule: - interval: daily - - package-ecosystem: cargo - directory: /core - schedule: - interval: daily - - package-ecosystem: cargo - directory: /music - schedule: - interval: daily - - package-ecosystem: cargo - directory: /turing - schedule: - interval: daily - - package-ecosystem: cargo - directory: /xtask - schedule: - interval: daily \ No newline at end of file +version: 2 +updates: + - package-ecosystem: docker + directories: + - . + - /.docker + schedule: + interval: monthly + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + - package-ecosystem: cargo + directories: + - /contained + - /core + - /derive + - /macros + schedule: + interval: monthly \ No newline at end of file diff --git a/.github/workflows/cargo-bench.yml b/.github/workflows/cargo-bench.yml new file mode 100644 index 0000000..d562991 --- /dev/null +++ b/.github/workflows/cargo-bench.yml @@ -0,0 +1,52 @@ +name: cargo-bench + +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: + contents: write + checks: 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 ] + steps: + - + name: Checkout + uses: actions/checkout@v5 + - + name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + target: ${{ matrix.target }} + - + name: Benchmark the workspace + run: cargo bench --locked --verbose --workspace --target ${{ matrix.target }} --features full -- + - + 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/cargo-clippy.yml b/.github/workflows/cargo-clippy.yml new file mode 100644 index 0000000..831d16c --- /dev/null +++ b/.github/workflows/cargo-clippy.yml @@ -0,0 +1,58 @@ +name: clippy + +concurrency: + cancel-in-progress: false + group: ${{ github.workflow }}-${{ github.ref }} + +on: + pull_request: + branches: [ main, master, $default-branch ] + types: [ opened, synchronize, reopened ] + push: + branches: [ main, master, $default-branch ] + tags: + - v*.*.* + - "*-nightly" + release: + types: [ created, edited ] + repository_dispatch: + 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 + contents: read + security-events: write + statuses: write + steps: + - + name: Checkout + uses: actions/checkout@v5 + - + name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + components: clippy, rustfmt + toolchain: nightly + override: true + - + name: Setup the for sarif output + run: cargo install clippy-sarif sarif-fmt + - + name: Run Clippy + run: + cargo clippy + --all-features + --workspace + --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt + - + name: Upload analysis + uses: github/codeql-action/upload-sarif@v3 + continue-on-error: true + with: + sarif_file: rust-clippy-results.sarif + wait-for-processing: true diff --git a/.github/workflows/cargo-publish.yml b/.github/workflows/cargo-publish.yml new file mode 100644 index 0000000..bcff4de --- /dev/null +++ b/.github/workflows/cargo-publish.yml @@ -0,0 +1,58 @@ +name: cargo-publish + +concurrency: + cancel-in-progress: false + group: ${{ github.workflow }}-${{ github.ref }} + +on: + repository_dispatch: + types: [ deploy, publish, cargo-publish, crates-io ] + workflow_dispatch: + inputs: + publish: + default: true + description: 'Publish the crate(s) to crates.io?' + type: boolean + +jobs: + crates-io: + runs-on: ubuntu-latest + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + environment: + name: crates-io + outputs: + url: ${{ steps.results.outputs.url }} + permissions: + contents: read + deployments: write + packages: write + strategy: + fail-fast: false + max-parallel: 1 + matrix: + package: + - contained-core + - contained-derive + - contained-macros + - contained + steps: + - + name: Checkout + uses: actions/checkout@v5 + - + name: Setup Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + cache-key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - + name: Publish (${{ matrix.package }}) + id: publish + run: cargo publish --locked --package ${{ matrix.package }} + - + name: Set output(s) + id: results + run: + echo "url=https://crates.io/crates/${{ matrix.package }}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml new file mode 100644 index 0000000..b57dc5e --- /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/clippy.yml b/.github/workflows/clippy.yml deleted file mode 100644 index b25814b..0000000 --- a/.github/workflows/clippy.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Clippy - -on: - pull_request: - branches: [ main ] - push: - branches: [ main ] - tags: [ "nightly*", "v*.*.*" ] - release: - types: [created] - schedule: - - cron: "30 9 * * 5" # Every Friday at 9:30am UTC - workflow_dispatch: - -permissions: - actions: read - contents: read - pull-requests: write - issues: write - security-events: write - -jobs: - clippy: - name: Clippy - permissions: - 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 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup rust toolchain - uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 - with: - profile: minimal - toolchain: stable - components: clippy - override: true - - name: Install required cargo - run: cargo install clippy-sarif sarif-fmt - - name: Run rust-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: - sarif_file: rust-clippy-results.sarif - wait-for-processing: true diff --git a/.github/workflows/crates.yml b/.github/workflows/crates.yml deleted file mode 100644 index ebaf195..0000000 --- a/.github/workflows/crates.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Crates - -env: - CARGO_TERM_COLOR: always - -on: - push: - tags: [ "nightly*", "v*.*.*" ] - release: - types: [ "published" ] - repository_dispatch: - types: [ "publish" ] - workflow_dispatch: - inputs: - publish: - default: true - description: 'Publish' - required: true - type: boolean - -jobs: - features: - name: Publish (features) - runs-on: ubuntu-latest - strategy: - matrix: - features: [ core, music, turing ] - env: - PACKAGE_NAME: ${{ github.event.repository.name }}-${{ matrix.features }} - steps: - - uses: actions/checkout@v3 - - name: Publish (${{env.PACKAGE_NAME}}) - run: cargo publish --all-features -v -p ${{env.PACKAGE_NAME}} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} - publish: - name: Publish (sdk) - needs: features - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Publish (${{ github.event.repository.name }}) - run: cargo publish --all-features -v -p ${{ github.event.repository.name }} --token ${{ secrets.CARGO_REGISTRY_TOKEN }} - diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index fe825d3..0000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Docker - -on: - push: - branches-ignore: [ "dev*", "next", "nightly*" ] - tags: [ "v*.*.*" ] - workflow_dispatch: - -env: - REGISTRY: hub.docker.com - IMAGE_TAG: latest - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: buildx - run: docker buildx build --tag ${{ secrets.DOCKERHUB_USERNAME }}/contained:latest . - - name: cache - uses: actions/cache@v4 - with: - key: ${{ runner.os }}-buildx-${{ github.sha }} - path: /tmp/.buildx-cache - restore-keys: | - ${{ runner.os }}-buildx-${{ github.sha }} - publish: - needs: build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Get cache - uses: actions/cache@v4 - with: - key: ${{ runner.os }}-buildx-${{ github.sha }} - path: /tmp/.buildx-cache - - name: Publish to Registry - uses: elgohr/Publish-Docker-Github-Action@v5 - with: - dockerfile: Dockerfile - name: ${{ secrets.DOCKERHUB_USERNAME }}/contained - password: ${{ secrets.DOCKERHUB_TOKEN }} - snapshot: true - username: ${{ secrets.DOCKERHUB_USERNAME }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..27d890f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,68 @@ +name: release + +on: + release: + types: [ published ] + repository_dispatch: + types: [ release ] + workflow_dispatch: + inputs: + draft: + default: false + description: 'Create a draft release' + required: true + type: boolean + prerelease: + default: false + description: 'Create a prerelease' + required: true + type: boolean + +permissions: + contents: write + discussions: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + ref: ${{ github.ref }} + repository: ${{ github.repository }} + - + name: Publish to crates.io + uses: peter-evans/repository-dispatch@v3 + with: + event-type: cargo-publish + client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' + token: ${{ github.token }} + release: + continue-on-error: true + 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@v5 + - + name: Create release + uses: softprops/action-gh-release@v2 + with: + append_body: false + draft: ${{ env.IS_DRAFT }} + prerelease: ${{ env.IS_PRERELEASE }} + tag_name: ${{ github.event.release.tag_name }} + body: | + ${{ github.event.release.body }} + + ## Links + + - [crates.io](https://crates.io/crates/${{ github.event.repository.name }}) + - [docs.rs](https://docs.rs/${{ github.event.repository.name }}) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0ef27cd..a9785d3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,67 +1,101 @@ -name: Rust - -env: - CARGO_TERM_COLOR: always - -on: - pull_request: - branches: [ main, master ] - push: - branches-ignore: [ "beta*", "dev*", "next*" ] - tags: [ "nightly*", "v*.*.*" ] - release: - repository_dispatch: - types: [ "publish" ] - schedule: - - cron: "30 9 * * 5" # Every Friday at 9:30am UTC - workflow_dispatch: - inputs: - benchmark: - default: false - description: 'Benchmark' - required: true - type: boolean - -jobs: - build: - name: Builder - strategy: - matrix: - platform: [ macos-latest, ubuntu-latest, windows-latest ] - runs-on: ${{ matrix.platform }} - steps: - - uses: actions/checkout@v3 - - name: setup (langspace) - run: | - rustup update - - name: Build - run: cargo build -F full --release -v --workspace - - name: Cache build - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target/release - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - test: - name: Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: setup (langspace) - run: | - rustup update - - name: Test - run: cargo test --all -F full --release -v - bench: - name: Benchmark - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: setup (langspace) - run: | - rustup update - rustup default nightly - - name: Bench - run: cargo bench --all -v \ No newline at end of file +name: rust + +concurrency: + cancel-in-progress: false + group: ${{ github.workflow }}-${{ github.ref }} + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + +on: + pull_request: + branches: [ main, master, $default-branch ] + types: [ opened, synchronize, reopened ] + push: + branches: [ main, master, $default-branch ] + tags: + - v*.*.* + - "*-nightly" + repository_dispatch: + types: [ rust ] + workflow_dispatch: + inputs: + benchmark: + default: false + description: 'Run benchmarks' + required: true + type: boolean + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: [ x86_64-unknown-linux-gnu ] + 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 }} + - + name: Build the workspace + run: cargo build --release --locked --workspace --features full --target ${{ matrix.target }} + benchmark: + if: ${{ inputs.benchmark || github.event_name == 'push' }} + runs-on: ubuntu-latest + outputs: + digest: ${{ steps.cargo-bench.outputs.digest }} + id: ${{ steps.cargo-bench.outputs.id }} + url: ${{ steps.cargo-bench.outputs.url }} + permissions: + actions: read + contents: write + steps: + - + name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.ref }} + repository: ${{ github.repository }} + - + name: Benchmark the workspace + id: cargo-bench + uses: peter-evans/repository-dispatch@v3 + with: + 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: [ full, default ] + target: [ x86_64-unknown-linux-gnu ] + 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 }} + - + name: Test (${{ matrix.features }}) + if: matrix.features != 'default' && matrix.features != 'all' + run: cargo test -r --locked --workspace --target ${{ matrix.target }} --features ${{ matrix.features }} + - + name: Test (default) + if: matrix.features == 'default' + run: cargo test -r --locked --workspace --target ${{ matrix.target }} diff --git a/.gitignore b/.gitignore index ac1ac03..5d38b7b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,55 @@ -# Config +# Artifacts + +**/.artifacts/data/ +**/.docker/data/ + +## Caches + +**/.cache/ + +# Configuration Files + **/config.* **/*.config.* -!**/.cargo/config* - **/*.env **/*.env.* -# Directories -**/.artifacts/data/ -**/.docker/data/ +### Exceptions +!**/default.config.* +!**/*.config.cjs +!**/*.config.js +!**/*.config.mjs +!**/config.py +!**/config.rs + +!**/example.env.* +!**/*.env.example +!**/*.env.default + +# Dev + +### Idea + **/.idea/ -**/.vscode/ -**/artifacts/ -**/build/ -**/debug/ -**/dist/ -**/env/ -**/node_modules/ -**/pkg/ -**/target/ -**/tmp/ -**/venv/ +### vscode + +**/.vscode/* + +!**/.vscode/settings.json # File Extensions -**/*.bk -**/*.bk.* + +**/*.lock +**/*.lock.* + +**/*-lock.* + +**/*.log +**/*.log.* + +### Data Files **/*.csv **/*.csv.* @@ -36,22 +59,55 @@ **/*.db-*.* -**/*.lock -**/*.lock.* +**/*.zip +**/*.zip.* -**/*-lock.* +## Rust +**/debug/ +**/target/ -**/*.log -**/*.log.* +**/*.bk +**/*.bk.* + +!**/Cargo.lock + +## Node +**/build/ +**/debug/ +**/dist/ +**/node_modules/ + +### SvelteKit +**/__sapper__/ +**/.DS_STORE/ +**/.svelte-kit/ + +## Python +**/__pycache__/ +**/.pytest_cache/ +**/venv/ + +**/*.egg +**/*.egg.* + +**/*.egg-info + +**/*.pyc +**/*.pyc.* + +**/*.pyo +**/*.pyo.* + +**/*.pyz +**/*.pyz.* + +**/*.pyzw +**/*.pyzw.* **/*.whl **/*.whl.* -**/*.zip -**/*.zip.* +## Operating Systems -# Exceptions -!**/default.config.* -!**/*.env.example -!**/*.config.js -!**/*.config.cjs +### Windows (WSL2) +**/*:Zone.Identifier diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index fd279fe..0000000 --- a/.gitpod.yml +++ /dev/null @@ -1,11 +0,0 @@ -tasks: - - init: | - sudo apt update -y && sudo apt upgrade -y && sudo apt autoremove -y - sudo apt install -y protobuf-compiler - rustup default nightly - rustup component add clippy rustfmt --toolchain nightly - rustup target add wasm32-unknown-unknown wasm32-wasi --toolchain nightly - cargo build --release --workspace - command: cargo watch -x run - - diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0dddf02 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "rust-analyzer.check.features": [ + "default", + "full" + ] +} diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..92af143 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +# the default owners of all files and directories within the repository +# except those that are explicitly assigned to other owners. +* @FL03 @Scattered-Systems \ No newline at end of file diff --git a/xtask/xtask.yml b/CONTRIBUTING.md similarity index 100% rename from xtask/xtask.yml rename to CONTRIBUTING.md diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6181a1d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,646 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "contained" +version = "0.2.0" +dependencies = [ + "contained-core", + "contained-derive", + "contained-macros", + "criterion", +] + +[[package]] +name = "contained-core" +version = "0.2.0" +dependencies = [ + "paste", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "wasm-bindgen", +] + +[[package]] +name = "contained-derive" +version = "0.2.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "contained-macros" +version = "0.2.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "criterion" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.143" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" diff --git a/Cargo.toml b/Cargo.toml index 16f3474..757bdf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,66 +1,67 @@ -[workspace.package] -authors = ["FL03 (https://github.com/FL03)"] -categories = [] -description = "contained is a research project implementing the proposed harmonic runtime for orchestrating cloud-native systems" -edition = "2021" -homepage = "https://github.com/FL03/contained/wiki" -keywords = [] -license = "Apache-2.0" -readme = "README.md" -repository = "https://github.com/FL03/contained" -version = "0.1.6" - -[workspace.dependencies] -# Custom crates -# decanter = { features = ["derive"], version = "0.1.6" } - -# Standard dependencies -anyhow = "1" -async-trait = "0.1" -bytes = "1" -chrono = { features = ["serde"], version = "0.4" } -futures = "0.3" -itertools = "0.12" -serde = { features = ["derive"], version = "1" } -serde_json = "1" -smart-default = "0.7" -strum = { features = ["derive"], version = "0.26" } - - - -[workspace] -default-members = [ - "contained" -] -exclude = [ - "xtask" -] -members = [ - "contained", - "core", - "music", - "turing" -] -resolver = "2" - -[profile.dev] -codegen-units = 256 -debug = true -debug-assertions = true -incremental = true -lto = false -panic = "unwind" -rpath = false -opt-level = 0 -overflow-checks = true - -[profile.release] -codegen-units = 16 -debug = false -debug-assertions = false -incremental = false -lto = false -panic = "unwind" -rpath = false -opt-level = "z" -overflow-checks = false +[workspace] +default-members = ["contained"] +members = [ + "contained", + "core", + "derive", + "macros", +] +resolver = "3" + +[workspace.package] +authors = [ + "FL03 (https://github.com/FL03)" +] +categories = [] +description = "contained is a zero-cost Rust library for creating transparent wrapper types." +edition = "2024" +homepage = "https://github.com/FL03/contained/wiki" +keywords = ["macros", "transparent", "wrapper"] +license = "Apache-2.0" +readme = "README.md" +repository = "https://github.com/FL03/contained.git" +rust-version = "1.85.0" +version = "0.2.0" + +[workspace.dependencies] +contained = { default-features = false, path = "contained", version = "0.2.0" } +contained-core = { default-features = false, path = "core", version = "0.2.0" } +contained-derive = { default-features = false, path = "derive", version = "0.2.0" } +contained-macros = { default-features = false, path = "macros", version = "0.2.0" } + +# error handling +anyhow = { default-features = false, version = "1" } +thiserror = { default-features = false, version = "2" } +# serialization +serde = { default-features = false, features = ["derive"], version = "1" } +serde_derive = { version = "1" } +serde_json = { default-features = false, version = "1" } +# macros & utilities +paste = { version = "1" } +smart-default = { version = "0.7" } +strum = { default-features = false, features = ["derive"], version = "0.27" } +# WebAssembly +wasm-bindgen = { default-features = false, version = "0.2" } + +# ********* Profiles ********* +[profile.dev] +codegen-units = 256 +debug = true +debug-assertions = true +incremental = true +lto = "thin" +opt-level = 2 +overflow-checks = true +panic = "unwind" +rpath = false + +[profile.release] +codegen-units = 16 +debug = false +debug-assertions = false +incremental = false +lto = true +opt-level = 0 +overflow-checks = false +panic = "unwind" +rpath = false diff --git a/LICENSE b/LICENSE index 622e1d9..40293f7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2024 Scattered-Systems, LLC - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 Joe McCain III + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..b439290 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,67 @@ +# Quickstart Guide `contained` + +Welcome to the quickstart guide for the `contained` library! This guide will help you get started with the library and provide you with the necessary steps to set up your development environment. + +## Getting Started + +### Prerequisites + +Before you can start using the `contained` 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 `contained` 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). + +```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/FL03/contained.git --branch main +``` + +Then, navigate to the cloned directory: + +```bash +cd contained +``` + +To build the project, use `cargo build` command: + +```bash +cargo build --all-features --locked --release --workspace +``` + +Test the project using the `cargo test` command: + +```bash +cargo test --all-features --locked --release --workspace +``` + +Or, benchmark the project using the `cargo bench` command: + +```bash +cargo bench --all-features --verbose --workspace +``` diff --git a/README.md b/README.md index cc831bd..6b8e5bd 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,37 @@ -# Contained - -[![crates.io](https://img.shields.io/crates/v/contained.svg)](https://crates.io/crates/contained) -[![docs.rs](https://docs.rs/contained/badge.svg)](https://docs.rs/contained) -[![Clippy](https://github.com/FL03/contained/actions/workflows/clippy.yml/badge.svg)](https://github.com/FL03/contained/actions/workflows/clippy.yml) -[![Docker](https://github.com/FL03/contained/actions/workflows/docker.yml/badge.svg)](https://github.com/FL03/contained/actions/workflows/docker.yml) -[![Rust](https://github.com/FL03/contained/actions/workflows/rust.yml/badge.svg)](https://github.com/FL03/contained/actions/workflows/rust.yml) - -*** - -Contained is a research oriented project focusing on implementing the proposed harmonic computational framework. Contained considers a harmonic framework capable of efficiently orchestrating clusters of devices following a set of instructions broadcast from valid actors preserving only the I/O and required metadata. The metadata stored typically pertains to the temporality or ordering of events which lead to the completion of any particular task and generally distributed as a part of a unique proof. In order to do so, we consider the neo-Riemannian theory and its potential implications for computational systems. Consequentially, several more traditional notions of harmonic analysis are additionally introduced to complete the theorem and solidify the framework. The resulting compositional procedures suggest novel means of generating ephemeral computational spaces capable of supporting interactions across each of the four permutations facilitated by digital systems. Typically these spaces are leveraged in conjunction with one another to elegantly orchestrate complex workloads. - -## Getting Started - -### Building from the source - -#### _Clone the repository_ - -```bash -git clone https://github.com/FL03/contained -``` - -### Docker - -#### _Build the image locally_ - -```bash -docker buildx build --tag contained:alpha . -``` - -#### _Pull the pre-built image_ - -```bash -docker pull jo3mccain/contained:latest -``` - -#### _Run the image_ - -```bash -docker run -p 8080:8080 jo3mccain/contained:latest system --up -``` - -### Usage - -```rust - -``` - -## Contributing - -Pull requests are welcome. For major changes, please open an issue first -to discuss what you would like to change. - -Please make sure to update tests as appropriate. - -## License - -- [Apache-2.0](https://choosealicense.com/licenses/apache-2.0/) -- [MIT](https://choosealicense.com/licenses/mit/) +# contained + +[![crates.io](https://img.shields.io/crates/v/contained?style=for-the-badge&logo=rust)](https://crates.io/crates/contained) +[![docs.rs](https://img.shields.io/docsrs/contained?style=for-the-badge&logo=docs.rs)](https://docs.rs/contained) +[![GitHub License](https://img.shields.io/github/license/fl03/contained?style=for-the-badge&logo=github)](./LICENSE) + +*** + +Welcome to `contained`, a collection of macros and other utilities designed to facilitate the creation and manipulation of so-called wrapper types in Rust. Here, a wrapper type is essentially any object capable of implementing the `#[repr(transparent)]` attribute, such as newtypes, tuple structs, and single-field enums. + +## Usage + +Before you start using `contained`, make sure to add it as a dependency in your `Cargo.toml` file. You can do this by adding the following lines: + +```toml +[dependencies.contained] +features = [ + "derive", +] +version = "0.2.x" +``` + +### Examples + +For more detailed examples, please visit the [examples](https://github.com/FL03/contained/tree/main/contained/examples) directory in the repository. Below are some brief examples highlighting certain features of the library. + +## Getting Started + +To get started with `contained`, 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 + +Licensed under the Apache License, Version 2.0, ([LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0)) + +## Contribution + +Contributions are welcome, however, ensure that you have read the [CONTRIBUTING.md](CONTRIBUTING.md) file before submitting a pull request. diff --git a/contained/Cargo.toml b/contained/Cargo.toml index 57be3e3..8e5d6fc 100644 --- a/contained/Cargo.toml +++ b/contained/Cargo.toml @@ -1,76 +1,98 @@ -[package] -authors.workspace = true -categories.workspace = true -default-run = "neo" -description.workspace = true -edition.workspace = true -homepage.workspace = true -keywords.workspace = true -license.workspace = true -name = "contained" -readme.workspace = true -repository.workspace = true -version.workspace = true - -[[bin]] -bench = true -name = "neo" -test = false - -[features] -default = ["core", "music", "turing"] -full = ["core", "music", "turing"] - -core = [ - "dep:contained-core" -] -music = [ - "dep:contained-music" -] -turing = [ - "dep:contained-turing" -] - -[lib] -bench = true -crate-type = ["cdylib", "rlib"] -doctest = true -test = true - -[build-dependencies] - -[dependencies] -contained-core = { features = [], optional = true, path = "../core", version = "0.1.6" } -contained-music = { features = [], optional = true, path = "../music", version = "0.1.6" } -contained-turing = { features = [], optional = true, path = "../turing", version = "0.1.6" } - -# Standard dependencies -anyhow.workspace = true -async-trait.workspace = true -bytes.workspace = true -decanter = { features = ["derive"], version = "0.1.6" } -futures.workspace = true -glob = "0.3" -lazy_static = "1" -petgraph = "0.6" -serde.workspace = true -serde_json.workspace = true -smart-default.workspace = true -strum.workspace = true -tokio = { features = ["macros", "rt", "signal", "sync", "time"], version = "1" } -tokio-stream = "0.1" -tracing = { features = ["log"], version = "0.1" } -tracing-subscriber = { features = ["env-filter", "fmt"], version = "0.3" } -wasmer = { features = [], version = "4" } - - -[dev-dependencies] - -[package.metadata.docs.rs] -all-features = true -rustc-args = ["--cfg", "docsrs"] - -[target.wasm32-unknown-unknown] - -[target.wasm32-wasi.dependencies] -tokio_wasi = { features = ["full"], version = "1" } +[package] +build = "build.rs" +name = "contained" + +authors.workspace = true +categories.workspace = true +description.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] +bench = true +doctest = true +test = 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}}" + +[[bench]] +name = "default" +harness = false + +[[test]] +name = "default" + +[dependencies] +contained-core = { workspace = true } +contained-derive = { optional = true, workspace = true } +contained-macros = { optional = true, workspace = true } + +[dev-dependencies] +criterion = { features = ["plotters"], version = "0.7" } + +[features] +default = [ + "std" +] + +full = [ + "default", + "derive", + "macros", +] + +# ********* [FF] Features ********* +derive = ["dep:contained-derive"] + +macros = ["dep:contained-macros"] + +nightly = [ + "contained-core/nightly", + "contained-derive?/nightly", + "contained-macros?/nightly", +] + +json = [ + "alloc", + "contained-core/json", +] + +# ********* [FF] Environments ********* +std = [ + "alloc", + "contained-core/std", +] + +wasi = [ + "alloc", + "contained-core/wasi", +] + +wasm = [ + "alloc", + "contained-core/wasm", +] + +# ********* [FF] Dependencies ********* +alloc = [ + "contained-core/alloc", +] + +serde = [ + "contained-core/serde", +] diff --git a/contained/benches/default.rs b/contained/benches/default.rs index 937f238..0480a28 100644 --- a/contained/benches/default.rs +++ b/contained/benches/default.rs @@ -1,52 +1,188 @@ -// bench.rs -#![feature(test)] +/* + Appellation: default + Contrib: @FL03 +*/ +use core::hint::black_box; +use criterion::{BatchSize, BenchmarkId, Criterion, criterion_group, criterion_main}; +use std::time::Duration; -extern crate test; +const SAMPLES: usize = 50; +/// the default number of iterations to benchmark a method for +const N: usize = 20; +/// the default number of seconds a benchmark should complete in +const DEFAULT_DURATION_SECS: u64 = 10; -use std::mem::replace; -use test::Bencher; +fn bench_fib_func(c: &mut Criterion) { + c.bench_function("fibonacci", |b| b.iter(|| fib::fibonacci(black_box(N)))); +} + +fn bench_fib_recursive(c: &mut Criterion) { + c.bench_function("recursive_fibonacci", |b| { + b.iter(|| fib::recursive_fibonacci(black_box(N))) + }); +} -// bench: find the `BENCH_SIZE` first terms of the fibonacci sequence -static BENCH_SIZE: usize = 20; +fn bench_fib_iter(c: &mut Criterion) { + let measure_for = Duration::from_secs(DEFAULT_DURATION_SECS); + // create a benchmark group for the Fibonacci iterator + let mut group = c.benchmark_group("Fibonacci Iter"); + // set the measurement time for the group + group.measurement_time(measure_for); + //set the sample size + group.sample_size(SAMPLES); -// recursive fibonacci -fn fibonacci(n: usize) -> u32 { - if n < 2 { - 1 - } else { - fibonacci(n - 1) + fibonacci(n - 2) + for &n in &[10, 50, 100, 500, 1000] { + group.bench_with_input(BenchmarkId::new("Fibonacci::compute", n), &n, |b, &x| { + b.iter_batched( + fib::Fibonacci::new, + |mut fib| { + black_box(fib.compute(x)); + }, + BatchSize::SmallInput, + ); + }); } -} -// iterative fibonacci -struct Fibonacci { - curr: u32, - next: u32, + group.finish(); } +// initialize the benchmark group +criterion_group! { + benches, + bench_fib_func, + bench_fib_iter, + bench_fib_recursive, +} +// This macro expands to a function named `benches`, which uses the given config +criterion_main!(benches); -impl Iterator for Fibonacci { - type Item = u32; - fn next(&mut self) -> Option { - let new_next = self.curr + self.next; - let new_curr = replace(&mut self.next, new_next); +pub mod fib { + //! various implementations of the fibonacci sequence + //! + //! ##_Definition_: + //! + //! $F(0) = F(1) = 1 \text{ and } F(n+1) = F(n) + F(n-1) | \forall: n > 0$ - Some(replace(&mut self.curr, new_curr)) + /// a simple implementation of the fibonacci sequence for benchmarking purposes + /// **Warning:** This will overflow the 128-bit unsigned integer at n=186 + #[inline] + pub fn fibonacci(n: usize) -> u128 { + // Use a and b to store the previous two values in the sequence + let mut a = 0; + let mut b = 1; + for _ in 0..n { + // As we iterate through, move b's value into a and the new computed + // value into b. + let c = a + b; + a = b; + b = c; + } + b + } + /// a recursive implementation of the fibonacci sequence + pub const fn recursive_fibonacci(n: usize) -> u128 { + const fn _inner(n: usize, previous: u128, current: u128) -> u128 { + if n == 0 { + current + } else { + _inner(n - 1, current, current + previous) + } + } + // Call the actual tail recursive implementation, with the extra + // arguments set up. + _inner(n, 0, 1) + } + /// A structural implementation of the fibonacci sequence that leverages the + /// [`iter`](core::iter) as its backend + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct Fibonacci { + curr: u32, + next: u32, } -} -fn fibonacci_sequence() -> Fibonacci { - Fibonacci { curr: 1, next: 1 } -} + impl Fibonacci { + /// returns a new instance of the fibonacci sequence, with `curr` set to 0 and `next` + /// set to 1 + pub const fn new() -> Fibonacci { + Fibonacci { curr: 0, next: 1 } + } + /// returns a copy of the current value + pub const fn curr(&self) -> u32 { + self.curr + } + /// returns a mutable reference to the current value + pub const fn curr_mut(&mut self) -> &mut u32 { + &mut self.curr + } + /// returns a copy of the next value + pub const fn next(&self) -> u32 { + self.next + } + /// returns a mutable reference to the next value + pub const fn next_mut(&mut self) -> &mut u32 { + &mut self.next + } + /// computes the nth value of the fibonacci sequence + #[inline] + pub fn compute(&mut self, n: usize) -> u32 { + if let Some(res) = self.nth(n + 1) { + return res; + } + panic!("Unable to compute the nth value of the fibonacci sequence...") + } + /// reset the instance to its default state, with `curr` set to 0 and `next` set to 1 + pub const fn reset(&mut self) -> &mut Self { + self.set_curr(0).set_next(1) + } + /// compute the next value in the fibonacci sequence, using the current and next values + #[inline] + const fn compute_next(&self) -> u32 { + self.curr() + self.next() + } + /// [`replace`](core::mem::replace) the current value with the given value, returning the + /// previous value + const fn replace_curr(&mut self, curr: u32) -> u32 { + core::mem::replace(self.curr_mut(), curr) + } + /// [`replace`](core::mem::replace) the next value with the given value, returning the + /// previous value + const fn replace_next(&mut self, next: u32) -> u32 { + core::mem::replace(self.next_mut(), next) + } + /// update the current value and return a mutable reference to the instance + const fn set_curr(&mut self, curr: u32) -> &mut Self { + self.curr = curr; + self + } + /// update the next value and return a mutable reference to the instance + const fn set_next(&mut self, next: u32) -> &mut Self { + self.next = next; + self + } + /// replace the next value with the given, using the previous next as the new current + /// value, and returning the previous current value + const fn update(&mut self, next: u32) -> u32 { + let new = self.replace_next(next); + self.replace_curr(new) + } + } -// function to benchmark must be annotated with `#[bench]` -#[bench] -fn recursive_fibonacci(b: &mut Bencher) { - // exact code to benchmark must be passed as a closure to the iter - // method of Bencher - b.iter(|| (0..BENCH_SIZE).map(fibonacci).collect::>()) -} + impl Default for Fibonacci { + fn default() -> Self { + Self::new() + } + } + + impl Iterator for Fibonacci { + type Item = u32; -#[bench] -fn iterative_fibonacci(b: &mut Bencher) { - b.iter(|| fibonacci_sequence().take(BENCH_SIZE).collect::>()) + fn next(&mut self) -> Option { + // compute the new next value + let new_next = self.compute_next(); + // replaces the current next with the new value and replaces the current value with + // the previous next + let prev = self.update(new_next); + // return the previous current value + Some(prev) + } + } } diff --git a/contained/build.rs b/contained/build.rs new file mode 100644 index 0000000..940a4ce --- /dev/null +++ b/contained/build.rs @@ -0,0 +1,8 @@ +/* + Appellation: build + Contrib: FL03 +*/ + +fn main() { + println!("cargo::rustc-check-cfg=cfg(no_std)"); +} diff --git a/contained/examples/agents.rs b/contained/examples/agents.rs deleted file mode 100644 index bde1823..0000000 --- a/contained/examples/agents.rs +++ /dev/null @@ -1,157 +0,0 @@ -extern crate contained; - -use contained::agents::client::{AgentManager, Client}; -use contained::agents::{layer::Command, Agent, Context, Stack, WasmEnv}; -use contained::prelude::{AsyncResult, BoxedWasmValue, Shared}; -use std::sync::{Arc, Mutex}; -use tokio::sync::mpsc; -use tracing::instrument; -use wasmer::{wat2wasm, Imports, Store}; -use wasmer::{Function, FunctionEnv, FunctionEnvMut}; - -/// A sample Wasm module that exports a function called `increment`. -static COUNTER_MODULE: &[u8] = br#" -(module - (func $get_counter (import "env" "get_counter") (result i32)) - (func $add_to_counter (import "env" "add_to_counter") (param i32) (result i32)) - (type $increment_t (func (param i32) (result i32))) - (func $increment_f (type $increment_t) (param $x i32) (result i32) - (block - (loop - (call $add_to_counter (i32.const 1)) - (set_local $x (i32.sub (get_local $x) (i32.const 1))) - (br_if 1 (i32.eq (get_local $x) (i32.const 0))) - (br 0))) - call $get_counter) - (export "sample" (func $increment_f))) -"#; - -fn get_counter(env: FunctionEnvMut) -> i32 { - *env.data().value.lock().unwrap() -} -fn add_to_counter(env: FunctionEnvMut, add: i32) -> i32 { - let mut counter_ref = env.data().value.lock().unwrap(); - - *counter_ref += add; - *counter_ref -} - -pub fn counter_module() -> std::borrow::Cow<'static, [u8]> { - wat2wasm(COUNTER_MODULE).unwrap() -} - -#[tokio::main] -async fn main() -> AsyncResult { - // Initialize the tracing layer - std::env::set_var("RUST_LOG", "info"); - tracing_subscriber::fmt::fmt() - .compact() - .with_line_number(false) - .with_target(false) - .init(); - // Initialize a new virtual environment - let venv = CounterVenv::new(0); - - let ctx = Context::new(wasmer::Engine::default(), Box::new(venv), Stack::new()); - agents(Box::new([15.into()]), ctx, None).await?; - Ok(()) -} - -pub struct CounterAgent { - context: Context, - store: Store, -} - -impl CounterAgent { - pub fn new() -> Self { - let store = Store::default(); - let venv = CounterVenv::new(0); - let context = Context::new(&store, Box::new(venv), Stack::new()); - Self { context, store } - } - pub fn build(&self, capacity: Option) -> (Agent, Client) { - let (tx, rx) = mpsc::channel(capacity.unwrap_or(100)); - let agent = Agent::new(rx, self.context.clone()); - let client = Client::new(tx); - (agent, client) - } - pub fn channels(&self) -> (mpsc::Sender, mpsc::Receiver) { - mpsc::channel(100) - } -} - -#[instrument( - err, - skip(ctx, imports), - fields(function = "sample", module = "COUNTER_MODULE"), - name = "example" -)] -async fn agents( - args: BoxedWasmValue, - ctx: Context, - imports: Option, -) -> AsyncResult { - // Initialize a new channel - let (tx, rx) = tokio::sync::mpsc::channel(100); - let mut agent = Agent::new(rx, ctx.clone()); - let mut client = Client::new(tx); - let imports = agent - .context() - .env() - .lock() - .unwrap() - .imports(&mut agent.store_mut(), imports); - let func = "sample"; - // Initialize a new agent; set the environment; then spawn it on a new thread - - agent.spawn(tokio::runtime::Handle::current()); - // Send the module to the agent - let cid = client.include(COUNTER_MODULE.to_vec()).await?; - // Execute the module - let res = client - .execute(cid.clone(), func.to_string(), args, Some(imports)) - .await?; - tracing::info!("Success: executed the function and got back {:?}", res); - Ok(res) -} - -pub fn counter_imports(env: &FunctionEnv, store: &mut Store) -> Imports { - let get_counter_func = Function::new_typed_with_env(store, env, get_counter); - let add_to_counter_func = Function::new_typed_with_env(store, env, add_to_counter); - wasmer::imports! { - "env" => { - "get_counter" => get_counter_func, - "add_to_counter" => add_to_counter_func, - } - } -} - -#[derive(Clone, Debug)] -pub struct CounterVenv { - pub value: Shared, -} - -impl CounterVenv { - pub fn new(value: i32) -> Self { - Self { - value: Arc::new(Mutex::new(value)), - } - } -} - -impl Default for CounterVenv { - fn default() -> Self { - Self::new(0) - } -} - -impl WasmEnv for CounterVenv { - fn imports(&self, store: &mut Store, with: Option) -> Imports { - let env = FunctionEnv::new(store, self.clone()); - let mut base = counter_imports(&env, store); - if let Some(with) = with { - base.extend(&with); - } - base - } -} diff --git a/contained/examples/neo.rs b/contained/examples/neo.rs deleted file mode 100644 index e016c94..0000000 --- a/contained/examples/neo.rs +++ /dev/null @@ -1,12 +0,0 @@ -/* - Appellation: basic - Contrib: FL03 -*/ -extern crate contained; - -use contained::music::neo::triads::*; - -fn main() { - // Initialize a new triad - let _triad = Triad::new(0.into(), Triads::Major); -} diff --git a/contained/examples/turing.rs b/contained/examples/turing.rs deleted file mode 100644 index bab9297..0000000 --- a/contained/examples/turing.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - Appellation: basic - Contrib: FL03 -*/ -extern crate contained; - -use contained::prelude::{Resultant, State}; -use contained::turing::{ - instructions::{Instruction, Move}, - machine::{Driver, Machine}, - Program, Tape, Turing, -}; - -pub const TEST_ALPHABET: [&str; 3] = ["a", "b", "c"]; - -fn main() -> Resultant { - let alphabet = vec!["a", "b", "c"]; - - let tape = alphabet.clone(); - let scope = Driver::from(Tape::norm(tape)); - - let instructions: Vec> = vec![ - (State::default(), "a", State::default(), "c", Move::Right).into(), - (State::default(), "b", State::default(), "a", Move::Right).into(), - (State::default(), "c", State::invalid(), "a", Move::Stay).into(), - ]; - - // Setup the program - let mut program = Program::new(alphabet, State::invalid()); - // Instruction set; turn ["a", "b", "c"] into ["c", "a", "a"] - program.extend(instructions); - - let mut machine = Machine::new(scope, program); - - assert!(machine.execute().is_ok()); - assert_eq!(machine.tape().clone(), Tape::norm(["c", "a", "a"])); - println!("{:?}", machine); - - Ok(()) -} diff --git a/contained/examples/wasm/counter.wasm b/contained/examples/wasm/counter.wasm deleted file mode 100644 index 89da3ae..0000000 --- a/contained/examples/wasm/counter.wasm +++ /dev/null @@ -1,13 +0,0 @@ -(module - (func $get_counter (import "env" "get_counter") (result i32)) - (func $add_to_counter (import "env" "add_to_counter") (param i32) (result i32)) - (type $increment_t (func (param i32) (result i32))) - (func $increment_f (type $increment_t) (param $x i32) (result i32) - (block - (loop - (call $add_to_counter (i32.const 1)) - (set_local $x (i32.sub (get_local $x) (i32.const 1))) - (br_if 1 (i32.eq (get_local $x) (i32.const 0))) - (br 0))) - call $get_counter) - (export "sample" (func $increment_f))) \ No newline at end of file diff --git a/contained/src/agents/agent.rs b/contained/src/agents/agent.rs deleted file mode 100644 index e1efd6a..0000000 --- a/contained/src/agents/agent.rs +++ /dev/null @@ -1,116 +0,0 @@ -/* - Appellation: agent - Contrib: FL03 -*/ -//! # Agent -//! -//! An agent is an intelligent entity that acts autonomously, directed by its own internal state. -//! An agent is typically a computer system that is situated in some environment, and that is capable of autonomous action in this environment in order to meet its design objectives. -//! Here, agents are described by their topological execution environments and are capable of executing arbitrary WebAssembly modules. -use super::layer::Command; -use super::Context; - -use tokio::{runtime as rt, sync::mpsc, task}; -use tracing::instrument; -use wasmer::{Instance, Module, Store}; - -pub struct AgentBuilder { - params: Option, -} - -pub struct AgentParams { - pub name: String, -} - -pub struct Agent { - cmd: mpsc::Receiver, - context: Context, - store: Store, -} - -impl Agent { - pub fn new(cmd: mpsc::Receiver, context: Context) -> Self { - let store = context.clone().store(); - Self { - cmd, - context, - store, - } - } - pub fn with_capacity(capacity: usize, context: Context) -> (Self, mpsc::Sender) { - let (tx, cmd) = mpsc::channel(capacity); - (Self::new(cmd, context), tx) - } - - pub fn context(&self) -> Context { - self.context.clone() - } - - #[instrument(err, skip(self, cmd), name = "process", target = "agent")] - pub async fn process(&mut self, cmd: Command) -> anyhow::Result<()> { - match cmd { - Command::Execute { - module, - function, - args, - with, - tx, - } => { - let stack = &self.context.stack(); - let modules = stack.modules().read().unwrap(); - tracing::debug!("Fetching the program..."); - let module = modules.get(&module).unwrap(); - tracing::debug!("Importing host functions"); - let imports = self - .context - .env() - .lock() - .unwrap() - .imports(&mut self.store_mut(), with); - tracing::info!("Instantiating module with the imported host functions"); - let instance = Instance::new(&mut self.store_mut(), &module, &imports) - .expect("Failed to instantiate module"); - tracing::info!("Fetching the function"); - let func = instance.exports.get_function(&function)?; - tracing::info!("Executing the function with the provided arguments"); - let result = func.call(&mut self.store_mut(), &args)?; - tx.send(Ok(result)).unwrap(); - Ok(()) - } - Command::Include { bytes, tx } => { - let module = Module::new(self.store(), bytes)?; - let hash = self.context.stack().add_module(module); - tx.send(Ok(hash.into())).unwrap(); - Ok(()) - } - Command::Transform { .. } => todo!(), - } - } - #[instrument(skip(self), name = "run", target = "agent")] - pub async fn run(mut self) { - loop { - tokio::select! { - Some(cmd) = self.cmd.recv() => { - tracing::debug!("Processing command"); - self.process(cmd).await.expect("Failed to process command"); - } - _ = tokio::signal::ctrl_c() => { - tracing::warn!("Signal received, shutting down"); - break; - } - } - } - } - #[instrument(skip(self, handle), name = "run", target = "agent")] - pub fn spawn(self, handle: rt::Handle) -> task::JoinHandle<()> { - handle.spawn(self.run()) - } - - pub fn store(&self) -> &Store { - &self.store - } - - pub fn store_mut(&mut self) -> &mut Store { - &mut self.store - } -} diff --git a/contained/src/agents/client/mod.rs b/contained/src/agents/client/mod.rs deleted file mode 100644 index 84b4a84..0000000 --- a/contained/src/agents/client/mod.rs +++ /dev/null @@ -1,68 +0,0 @@ -/* - Appellation: client - Contrib: FL03 - Description: This module implements the client for engaging with an actor -*/ -//! # Client -use super::layer::Command; -use crate::music::neo::LPR; -use crate::prelude::{AsyncResult, BoxedWasmValue}; -use decanter::prelude::H256; -use tokio::sync::{mpsc, oneshot}; - -#[async_trait::async_trait] -pub trait AgentManager: Send + Sync { - fn sender(&self) -> &mpsc::Sender; - async fn execute( - &mut self, - module: H256, - function: String, - args: BoxedWasmValue, - imports: Option, - ) -> AsyncResult { - let (tx, rx) = oneshot::channel(); - self.sender() - .send(Command::execute(module, function, args, imports, tx)) - .await?; - rx.await? - } - async fn include(&mut self, bytes: Vec) -> AsyncResult { - let (tx, rx) = oneshot::channel(); - self.sender().send(Command::include(bytes, tx)).await?; - rx.await? - } - async fn transform(&mut self, id: H256, dirac: LPR) -> AsyncResult { - let (tx, rx) = oneshot::channel(); - self.sender() - .send(Command::transform(id, dirac, tx)) - .await?; - rx.await? - } -} - -impl AgentManager for mpsc::Sender { - fn sender(&self) -> &mpsc::Sender { - self - } -} - -#[derive(Debug)] -pub struct Client { - cmd: mpsc::Sender, -} - -impl Client { - pub fn new(cmd: mpsc::Sender) -> Self { - Self { cmd } - } - pub fn with_capacity(capacity: usize) -> (Self, mpsc::Receiver) { - let (cmd, rx) = mpsc::channel(capacity); - (Self::new(cmd), rx) - } -} - -impl AgentManager for Client { - fn sender(&self) -> &mpsc::Sender { - &self.cmd - } -} diff --git a/contained/src/agents/context.rs b/contained/src/agents/context.rs deleted file mode 100644 index 1f3c77b..0000000 --- a/contained/src/agents/context.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* - Appellation: context - Contrib: FL03 -*/ -use super::{Stack, WasmEnv}; -use std::sync::{Arc, Mutex}; -use tracing::instrument; -use wasmer::{AsEngineRef, Engine, Store}; - -#[derive(Clone)] -pub struct Context { - engine: Engine, - env: Arc>>, - - stack: Stack, -} - -impl Context { - pub fn new(engine: impl AsEngineRef, env: Box, stack: Stack) -> Self { - Self { - engine: engine.as_engine_ref().engine().clone(), - env: Arc::new(Mutex::new(env)), - stack, - } - } - - pub fn engine(&self) -> Engine { - self.engine.clone() - } - - pub fn env(&self) -> Arc>> { - self.env.clone() - } - - pub fn stack(&self) -> Stack { - self.stack.clone() - } - - pub fn stack_mut(&mut self) -> &mut Stack { - &mut self.stack - } - - pub fn store(&self) -> Store { - Store::new(self.engine()) - } - - pub fn with_engine(mut self, engine: Engine) -> Self { - self.engine = engine; - self - } - - #[instrument(skip(self, env), name = "environment", target = "context")] - pub fn with_environment(mut self, env: Box) -> Self { - self.env = Arc::new(Mutex::new(env)); - self - } - - pub fn with_stack(mut self, stack: Stack) -> Self { - self.stack = stack; - self - } -} diff --git a/contained/src/agents/environment.rs b/contained/src/agents/environment.rs deleted file mode 100644 index fae3ebb..0000000 --- a/contained/src/agents/environment.rs +++ /dev/null @@ -1,77 +0,0 @@ -/* - Appellation: environment - Contrib: FL03 - Description: Implements a virtual wasm environment; each environment describes a set of capabilities and is responsible for tracing the various results -*/ -//! # Environments -//! -//! Environments are the primary means of interacting with the WASM runtime. Each environment describes a set of capabilities and is responsible for tracing the various results. -use crate::music::prelude::triads::Triadic; -use wasmer::{imports, FunctionEnv, Imports, Store}; - -pub trait ThreadSafe: Send + Sync {} - -impl ThreadSafe for T where T: Send + Sync {} - -pub trait Venv { - type Env: Clone + WasmEnv; - - /// Returns a reference to the store - fn store(&self) -> &Store; - /// Returns a mutable reference to the store - fn store_mut(&mut self) -> &mut Store; - /// Returns a to the environment - fn venv(&self) -> Self::Env; - /// Returns a mutable reference to the environment - fn venv_mut(&mut self) -> &mut Self::Env; -} - -pub trait FunctionalVenv: Venv { - fn function_env(&mut self) -> FunctionEnv; -} - -pub trait WasmEnv: Send + Sync { - fn imports(&self, store: &mut Store, with: Option) -> Imports; -} - -impl WasmEnv for T -where - T: Triadic, -{ - fn imports(&self, _store: &mut Store, with: Option) -> Imports { - let mut imports = imports! { - "env" => { - } - }; - if let Some(w) = with { - imports.extend(&w); - } - imports - } -} - -pub struct VirtualEnv { - env: Box, - store: Store, -} - -impl VirtualEnv { - pub fn new(env: Box) -> Self { - Self { - env, - store: Store::default(), - } - } - - pub fn imports(&mut self, with: Option) -> Imports { - self.env.imports(&mut self.store, with) - } - - pub fn store(&self) -> &Store { - &self.store - } - - pub fn store_mut(&mut self) -> &mut Store { - &mut self.store - } -} diff --git a/contained/src/agents/layer/command.rs b/contained/src/agents/layer/command.rs deleted file mode 100644 index ebabc0d..0000000 --- a/contained/src/agents/layer/command.rs +++ /dev/null @@ -1,58 +0,0 @@ -/* - Appellation: command - Contrib: FL03 - Description: explicit commands for agents - Commands: - Execute: execute a function in a module - Include: include a module - Transform: transform a module -*/ -use super::OneshotSender; -use crate::music::neo::LPR; -use crate::prelude::BoxedWasmValue; -use decanter::prelude::H256; -use wasmer::Imports; - -#[derive(Debug)] -pub enum Command { - Execute { - module: H256, - function: String, - args: BoxedWasmValue, - with: Option, - tx: OneshotSender, - }, - Include { - bytes: Vec, - tx: OneshotSender, - }, - Transform { - id: H256, - dirac: LPR, - tx: OneshotSender, - }, -} - -impl Command { - pub fn execute( - module: H256, - function: String, - args: BoxedWasmValue, - with: Option, - tx: OneshotSender, - ) -> Self { - Self::Execute { - module, - function, - args, - with, - tx, - } - } - pub fn include(bytes: Vec, tx: OneshotSender) -> Self { - Self::Include { bytes, tx } - } - pub fn transform(id: H256, dirac: LPR, tx: OneshotSender) -> Self { - Self::Transform { id, dirac, tx } - } -} diff --git a/contained/src/agents/layer/event.rs b/contained/src/agents/layer/event.rs deleted file mode 100644 index 04b4457..0000000 --- a/contained/src/agents/layer/event.rs +++ /dev/null @@ -1,129 +0,0 @@ -/* - Appellation: event - Contrib: FL03 - Description: ... summary ... -*/ -use crate::BoxedWasmValue; -// use decanter::prelude::H256; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumString, EnumVariantNames}; - -#[derive(Debug)] -pub enum CommandEvent { - Executed { cid: String, result: BoxedWasmValue }, - Included { cid: String }, - Transformed, -} - -pub enum RequestResponse { - Request, - Response, -} - -pub enum AgentEvent { - Command(CommandEvent), - Connection(ConnectionEvent), -} - -#[derive( - Clone, - Debug, - Deserialize, - Display, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, -)] -pub enum AgentError { - ConnectionError(String), - Error(String), - ExecutionError(String), - InitError(String), - IOError(String), -} - -impl std::error::Error for AgentError {} - -impl Default for AgentError { - fn default() -> Self { - Self::Error("".to_string()) - } -} - -impl From> for AgentError { - fn from(error: Box) -> Self { - Self::Error(error.to_string()) - } -} - -impl From> for AgentError { - fn from(error: Box) -> Self { - Self::Error(error.to_string()) - } -} - -impl From for AgentError { - fn from(error: std::io::Error) -> Self { - Self::IOError(error.to_string()) - } -} - -#[derive( - Clone, - Debug, - Deserialize, - Display, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, -)] -pub enum Event { - Error(AgentError), -} - -#[derive( - Clone, - Debug, - Deserialize, - Display, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, -)] -pub enum ConnectionEvent { - #[default] - Connecting, - Disonnecting, -} - -pub enum RegistrationEvent { - Registered, - Registering, - Unregistered, - Unregistering, -} - -pub enum Controls { - Connect, - Disconnect, - Register, - Start, - Terminate, -} diff --git a/contained/src/agents/layer/mod.rs b/contained/src/agents/layer/mod.rs deleted file mode 100644 index f345206..0000000 --- a/contained/src/agents/layer/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -/* - Appellation: layer - Contrib: FL03 - Description: This module implements the async layer for the agents -*/ -pub use self::{command::*, event::*}; - -mod command; -mod event; - -use crate::prelude::AsyncResult; -use tokio::sync::oneshot; - -pub type OneshotSender = oneshot::Sender>; diff --git a/contained/src/agents/mod.rs b/contained/src/agents/mod.rs deleted file mode 100644 index 7cc3eae..0000000 --- a/contained/src/agents/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -/* - Appellation: agents - Contrib: FL03 -*/ -//! # Agents -pub use self::{agent::*, context::*, environment::*, stack::*}; - -mod agent; -mod context; -mod environment; -mod stack; - -pub mod client; -pub mod layer; - -pub trait Actor {} diff --git a/contained/src/agents/stack.rs b/contained/src/agents/stack.rs deleted file mode 100644 index 4c2a0c9..0000000 --- a/contained/src/agents/stack.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - Appellation: stack - Contrib: FL03 - Description: The stack is a collection of modules and environments that are availible to the agent. -*/ -//! # Stack -//! -//! The stack is a collection of modules and environments that are availible to the agent. -use crate::prelude::hash_module; -use decanter::prelude::H256; -use std::collections::HashMap; -use std::sync::{Arc, RwLock}; -use wasmer::Module; - -#[derive(Clone)] -pub struct Stack { - modules: Arc>>, -} - -impl Stack { - pub fn new() -> Self { - Self { - modules: Arc::new(RwLock::new(HashMap::new())), - } - } - pub fn modules(&self) -> &Arc>> { - &self.modules - } - - pub fn add_module(&self, module: Module) -> H256 { - let hash = hash_module(&module); - self.modules.write().unwrap().insert(hash.clone(), module); - hash - } -} diff --git a/contained/src/bin/neo.rs b/contained/src/bin/neo.rs deleted file mode 100644 index 84a9e1f..0000000 --- a/contained/src/bin/neo.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - Appellation: neo - Contrib: FL03 -*/ -use contained::music::neo::triads::{Triad, Triads}; -use contained::prelude::State; -use wasmer::{imports, FunctionEnv, Imports, Store}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let _triad = Triad::new(0.into(), Triads::Major); - - Ok(()) -} - -pub struct Conduit { - state: State, - store: Store, - triad: Triad, -} diff --git a/contained/src/cluster/mod.rs b/contained/src/cluster/mod.rs deleted file mode 100644 index d817d9d..0000000 --- a/contained/src/cluster/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -/* - Appellation: cluster - Contrib: FL03 - Description: A cluster describes a user-owned set of nodes, which are used to orchestrate various workloads and implement a personal cloud. - The cluster implements a subnet to synchronize activities between the systems and to provide a secure, private, and reliable network. - Doing so also allows the clusters to support the implementation of a distributed file system, which can be used to store and share data. -*/ -//! # Clusters -//! -//! Clusters describe the physical networking layer of the system. Each cluster is composed of a set of user-owned nodes -//! which are used to orchestrate various workloads and implement a personal cloud. Additionally, each cluster is -//! used to abstraclty describe a type of virtual node used to empower the mainnet. -pub use self::stack::*; - -mod stack; diff --git a/contained/src/cluster/stack.rs b/contained/src/cluster/stack.rs deleted file mode 100644 index 4ee190e..0000000 --- a/contained/src/cluster/stack.rs +++ /dev/null @@ -1,30 +0,0 @@ -/* - Appellation: stack - Contrib: FL03 - Description: The stack is a collection of modules and environments that are availible to the cluster -*/ -use crate::agents::VirtualEnv; -use decanter::prelude::H256; -use std::collections::HashMap; -use std::sync::{Arc, RwLock}; -use wasmer::Module; - -pub struct Stack { - pub envs: Arc>>, - pub modules: Arc>>, -} - -impl Stack { - pub fn new() -> Self { - Self { - envs: Arc::new(RwLock::new(HashMap::new())), - modules: Arc::new(RwLock::new(HashMap::new())), - } - } - pub fn envs(&self) -> &Arc>> { - &self.envs - } - pub fn modules(&self) -> &Arc>> { - &self.modules - } -} diff --git a/contained/src/lib.rs b/contained/src/lib.rs index 3d3ed67..9b51042 100644 --- a/contained/src/lib.rs +++ b/contained/src/lib.rs @@ -1,33 +1,32 @@ -/* - Appellation: contained - Contrib: FL03 -*/ -/// # Contained -/// -/// A novel harmonic orchestration mechanism derived from the neo-Riemannian theory of music. -#[cfg(feature = "core")] -pub use contained_core as core; -#[cfg(feature = "music")] -pub use contained_music as music; -#[cfg(feature = "turing")] -pub use contained_turing as turing; - -pub use self::{primitives::*, utils::*}; - -mod primitives; -mod utils; - -pub mod agents; -pub mod cluster; - -pub mod prelude { - pub use super::primitives::*; - pub use super::utils::*; - - #[cfg(feature = "core")] - pub use super::core::prelude::*; - #[cfg(feature = "music")] - pub use super::music::prelude::*; - #[cfg(feature = "turing")] - pub use super::turing::prelude::*; -} +/* + Appellation: contained + Contrib: FL03 +*/ +//! # contained +//! +//! Welcome to `contained`! A library focused on providing useful abstractions, macros, and +//! utilities for handling so-called wrapper types. In short, a wrapper type is any implemented +//! object capable of using `#[repr(transparent)]`. +#![allow( + clippy::missing_safety_doc, + clippy::module_inception, + clippy::needless_doctest_main, + clippy::upper_case_acronyms +)] +#![cfg_attr(not(feature = "std"), no_std)] + +pub use contained_core::*; + +#[cfg(feature = "derive")] +pub use contained_derive::*; +#[cfg(feature = "macros")] +pub use contained_macros::*; + +#[allow(unused_imports)] +pub mod prelude { + pub use contained_core::prelude::*; + #[cfg(feature = "derive")] + pub use contained_derive::*; + #[cfg(feature = "macros")] + pub use contained_macros::*; +} diff --git a/contained/src/primitives.rs b/contained/src/primitives.rs deleted file mode 100644 index f7aca12..0000000 --- a/contained/src/primitives.rs +++ /dev/null @@ -1,15 +0,0 @@ -/* - Appellation: primitives - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::{constants::*, types::*}; - -mod constants { - pub const CONFIG_FNAME_PATTERN: &str = "*.config.toml"; -} - -mod types { - /// A type alias for a [Box] slice of [wasmer::Value]s - pub type BoxedWasmValue = Box<[wasmer::Value]>; -} diff --git a/contained/src/utils.rs b/contained/src/utils.rs deleted file mode 100644 index b755930..0000000 --- a/contained/src/utils.rs +++ /dev/null @@ -1,11 +0,0 @@ -/* - Appellation: utils - Contrib: FL03 -*/ -use decanter::prelude::{hasher, H256}; -use wasmer::Module; - -/// [hash_module] is a simple utility function that takes a [Module] and returns a [H256] hash. -pub fn hash_module(module: &Module) -> H256 { - hasher(module.serialize().unwrap().as_ref()).into() -} diff --git a/contained/tests/default.rs b/contained/tests/default.rs index 806d989..b8f130e 100644 --- a/contained/tests/default.rs +++ b/contained/tests/default.rs @@ -1,7 +1,17 @@ -#[cfg(test)] -#[test] -fn compiles() { - let f = |i: usize| i * i; - - assert_eq!(f(2), 4) -} +/* + appellation: default + authors: @FL03 +*/ + +fn adder(a: A, b: B) -> C +where + A: core::ops::Add, +{ + a + b +} + +#[test] +fn compiles() { + assert_eq!(adder(1, 100), 101); + assert_eq!(adder(1.0, 100.0), 101.0); +} diff --git a/core/Cargo.toml b/core/Cargo.toml index 15277ee..d444de8 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,52 +1,93 @@ -[package] -authors.workspace = true -categories = [] -description.workspace = true -edition.workspace = true -homepage.workspace = true -keywords = [] -license.workspace = true -name = "contained-core" -readme.workspace = true -repository.workspace = true -version.workspace = true - -[lib] -crate-type = ["cdylib", "rlib"] -test = true - -[features] -default = [] - -wasm = [] - -[build-dependencies] - -[dependencies] -# decanter.workspace = true - -anyhow.workspace = true -async-trait.workspace = true -atoi = "2" -bytes.workspace = true -chrono.workspace = true -futures.workspace = true -itertools.workspace = true -petgraph ={ features = [], version = "0.6" } -predicates = "3.0" -serde.workspace = true -serde_json.workspace = true -smart-default.workspace = true -strum.workspace = true -tokio = { features = ["full"], version = "1"} - -[dev-dependencies] -tokio = { features = ["macros", "rt"], version = "1" } - -[package.metadata.docs.rs] -all-features = true -rustc-args = ["--cfg", "docsrs"] - -[target.wasm32-unknown-unknown] - -[target.wasm32-wasi] +[package] +build = "build.rs" +description = "The core crate for the contained project, providing essential abstractions and utilities." +name = "contained-core" + +authors.workspace = true +categories.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +rust-version.workspace = true +version.workspace = true + +[lib] +crate-type = ["cdylib", "rlib"] +bench = false +doctest = true +test = true + +[package.metadata.docs.rs] +all-features = false +features = ["full"] +rustc-args = ["--cfg", "docsrs"] + +[[test]] +name = "default" + +[[test]] +name = "macros" + +[dependencies] +# error handling +thiserror = { workspace = true } +# macros & utilities +paste = { workspace = true } +# serialization +serde = { optional = true, features = ["derive"], workspace = true } +serde_derive = { optional = true, workspace = true } +serde_json = { optional = true, workspace = true } +# WebAssembly +wasm-bindgen = { optional = true, workspace = true } + +[features] +default = [ + "std" +] + +full = [ + "default" +] + +# ********* [FF] Features ********* +json = [ + "alloc", + "serde_json", +] + +nightly = [] + +# ********* [FF] Environments ********* +std = [ + "alloc", + "serde?/std", + "serde_json?/std", + "thiserror/std", +] + +wasi = [ + "alloc", +] + +wasm = [ + "alloc", + "wasm_bindgen", +] + +# ********* [FF] Dependencies ********* +alloc = [ + "serde?/alloc", + "serde_json?/alloc", +] + +serde = [ + "dep:serde", + "dep:serde_derive", +] + +serde_json = ["dep:serde_json"] + +wasm_bindgen = ["dep:wasm-bindgen"] diff --git a/core/build.rs b/core/build.rs new file mode 100644 index 0000000..940a4ce --- /dev/null +++ b/core/build.rs @@ -0,0 +1,8 @@ +/* + Appellation: build + Contrib: FL03 +*/ + +fn main() { + println!("cargo::rustc-check-cfg=cfg(no_std)"); +} diff --git a/core/src/actors/actor.rs b/core/src/actors/actor.rs deleted file mode 100644 index 1a69185..0000000 --- a/core/src/actors/actor.rs +++ /dev/null @@ -1,9 +0,0 @@ -/* - Appellation: actor - Contrib: FL03 -*/ -//! # Actor - -pub trait Actor { - fn name(&self) -> String; -} diff --git a/core/src/actors/mod.rs b/core/src/actors/mod.rs deleted file mode 100644 index aa73966..0000000 --- a/core/src/actors/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -/* - Appellation: actors - Contrib: FL03 -*/ -//! # Actors -pub use self::actor::*; - -pub(crate) mod actor; - -pub trait Executor { - fn execute(&self); -} diff --git a/core/src/compute/mod.rs b/core/src/compute/mod.rs deleted file mode 100644 index 3c8d0f3..0000000 --- a/core/src/compute/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -/* - Appellation: compute - Contrib: FL03 - Description: This module implements a basic framework for building dynamic, multiway systems -*/ -pub mod surface; - -use crate::Error; - -pub trait Compute { - type Output; - - fn compute(&self) -> Result; -} diff --git a/core/src/compute/surface.rs b/core/src/compute/surface.rs deleted file mode 100644 index 9f50524..0000000 --- a/core/src/compute/surface.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - Appellation: surface - Contrib: FL03 -*/ -//! # Surface -//! -//! A surface is used to describe a topological object that generally extends a graph data-structure by adding an additional surface value. -//! The surface value or area typically describes all of the possible results of a transition function and is used to establish a consistent state -pub trait Surface { - fn area(&self) -> f64; - fn perimeter(&self) -> f64; - fn edges(&self) -> u32; - fn vertices(&self) -> u32; - fn faces(&self) -> u32; - fn volume(&self) -> f64; -} - -pub trait Polytope { - fn dim(&self) -> u32; -} diff --git a/core/src/connect/connection.rs b/core/src/connect/connection.rs deleted file mode 100644 index 659ca67..0000000 --- a/core/src/connect/connection.rs +++ /dev/null @@ -1,247 +0,0 @@ -/* - Appellation: connection - Contrib: FL03 - Description: This module implements an explicit connection handler that supports the parsing of frames. The connection handler is used by the server and client to handle incoming connections. - The primary motivation for this was to support operations on a custom frame -*/ -use super::{Frame, FrameError}; -use crate::Resultant; -use bytes::{Buf, BytesMut}; -use std::io::{self, Cursor}; -use tokio::io::{AsyncReadExt, AsyncWriteExt, BufWriter}; -use tokio::net::TcpStream; - -/// Send and receive `Frame` values from a remote peer. -/// -/// When implementing networking protocols, a message on that protocol is -/// often composed of several smaller messages known as frames. The purpose of -/// `Connection` is to read and write frames on the underlying `TcpStream`. -/// -/// To read frames, the `Connection` uses an internal buffer, which is filled -/// up until there are enough bytes to create a full frame. Once this happens, -/// the `Connection` creates the frame and returns it to the caller. -/// -/// When sending frames, the frame is first encoded into the write buffer. -/// The contents of the write buffer are then written to the socket. -#[derive(Debug)] -pub struct Connection { - // The `TcpStream`. It is decorated with a `BufWriter`, which provides write - // level buffering. The `BufWriter` implementation provided by Tokio is - // sufficient for our needs. - stream: BufWriter, - - // The buffer for reading frames. Unfortunately, Tokio's `BufReader` - // currently requires you to empty its buffer before you can ask it to - // retrieve more data from the underlying stream, so we have to manually - // implement buffering. This should be fixed in Tokio v0.3. - buffer: BytesMut, -} - -impl Connection { - /// Create a new `Connection`, backed by `socket`. Read and write buffers - /// are initialized. - pub fn new(socket: TcpStream) -> Connection { - Connection { - stream: BufWriter::new(socket), - // Default to a 4KB read buffer. For the use case of mini redis, - // this is fine. However, real applications will want to tune this - // value to their specific use case. There is a high likelihood that - // a larger read buffer will work better. - buffer: BytesMut::with_capacity(4 * 1024), - } - } - - /// Read a single `Frame` value from the underlying stream. - /// - /// The function waits until it has retrieved enough data to parse a frame. - /// Any data remaining in the read buffer after the frame has been parsed is - /// kept there for the next call to `read_frame`. - /// - /// # Returns - /// - /// On success, the received frame is returned. If the `TcpStream` - /// is closed in a way that doesn't break a frame in half, it returns - /// `None`. Otherwise, an error is returned. - pub async fn read_frame(&mut self) -> Resultant> { - loop { - // Attempt to parse a frame from the buffered data. If enough data - // has been buffered, the frame is returned. - if let Some(frame) = self.parse_frame()? { - return Ok(Some(frame)); - } - - // There is not enough buffered data to read a frame. Attempt to - // read more data from the socket. - // - // On success, the number of bytes is returned. `0` indicates "end - // of stream". - if 0 == self.stream.read_buf(&mut self.buffer).await? { - // The remote closed the connection. For this to be a clean - // shutdown, there should be no data in the read buffer. If - // there is, this means that the peer closed the socket while - // sending a frame. - if self.buffer.is_empty() { - return Ok(None); - } else { - return Err(crate::Error::ConnectionError( - "connection closed before entire frame was received".to_string(), - )); - } - } - } - } - - /// Tries to parse a frame from the buffer. If the buffer contains enough - /// data, the frame is returned and the data removed from the buffer. If not - /// enough data has been buffered yet, `Ok(None)` is returned. If the - /// buffered data does not represent a valid frame, `Err` is returned. - fn parse_frame(&mut self) -> Resultant> { - // Cursor is used to track the "current" location in the - // buffer. Cursor also implements `Buf` from the `bytes` crate - // which provides a number of helpful utilities for working - // with bytes. - let mut buf = Cursor::new(&self.buffer[..]); - - // The first step is to check if enough data has been buffered to parse - // a single frame. This step is usually much faster than doing a full - // parse of the frame, and allows us to skip allocating data structures - // to hold the frame data unless we know the full frame has been - // received. - match Frame::check(&mut buf) { - Ok(_) => { - // The `check` function will have advanced the cursor until the - // end of the frame. Since the cursor had position set to zero - // before `Frame::check` was called, we obtain the length of the - // frame by checking the cursor position. - let len = buf.position() as usize; - - // Reset the position to zero before passing the cursor to - // `Frame::parse`. - buf.set_position(0); - - // Parse the frame from the buffer. This allocates the necessary - // structures to represent the frame and returns the frame - // value. - // - // If the encoded frame representation is invalid, an error is - // returned. This should terminate the **current** connection - // but should not impact any other connected client. - let frame = Frame::parse(&mut buf)?; - - // Discard the parsed data from the read buffer. - // - // When `advance` is called on the read buffer, all of the data - // up to `len` is discarded. The details of how this works is - // left to `BytesMut`. This is often done by moving an internal - // cursor, but it may be done by reallocating and copying data. - self.buffer.advance(len); - - // Return the parsed frame to the caller. - Ok(Some(frame)) - } - // There is not enough data present in the read buffer to parse a - // single frame. We must wait for more data to be received from the - // socket. Reading from the socket will be done in the statement - // after this `match`. - // - // We do not want to return `Err` from here as this "error" is an - // expected runtime condition. - Err(err) => match err { - FrameError::Incomplete => Ok(None), - FrameError::Other(e) => { - // An actual error was encountered while parsing the frame. - Err(e) - } - }, - } - } - - /// Write a single `Frame` value to the underlying stream. - /// - /// The `Frame` value is written to the socket using the various `write_*` - /// functions provided by `AsyncWrite`. Calling these functions directly on - /// a `TcpStream` is **not** advised, as this will result in a large number of - /// syscalls. However, it is fine to call these functions on a *buffered* - /// write stream. The data will be written to the buffer. Once the buffer is - /// full, it is flushed to the underlying socket. - pub async fn write_frame(&mut self, frame: &Frame) -> io::Result<()> { - // Arrays are encoded by encoding each entry. All other frame types are - // considered literals. For now, mini-redis is not able to encode - // recursive frame structures. See below for more details. - match frame { - Frame::Array(val) => { - // Encode the frame type prefix. For an array, it is `*`. - self.stream.write_u8(b'*').await?; - - // Encode the length of the array. - self.write_decimal(val.len() as u64).await?; - - // Iterate and encode each entry in the array. - for entry in &**val { - self.write_value(entry).await?; - } - } - // The frame type is a literal. Encode the value directly. - _ => self.write_value(frame).await?, - } - - // Ensure the encoded frame is written to the socket. The calls above - // are to the buffered stream and writes. Calling `flush` writes the - // remaining contents of the buffer to the socket. - self.stream.flush().await - } - - /// Write a frame literal to the stream - async fn write_value(&mut self, frame: &Frame) -> io::Result<()> { - match frame { - Frame::Simple(val) => { - self.stream.write_u8(b'+').await?; - self.stream.write_all(val.as_bytes()).await?; - self.stream.write_all(b"\r\n").await?; - } - Frame::Error(val) => { - self.stream.write_u8(b'-').await?; - self.stream.write_all(val.as_bytes()).await?; - self.stream.write_all(b"\r\n").await?; - } - Frame::Integer(val) => { - self.stream.write_u8(b':').await?; - self.write_decimal(*val).await?; - } - Frame::Null => { - self.stream.write_all(b"$-1\r\n").await?; - } - Frame::Bulk(val) => { - let len = val.len(); - - self.stream.write_u8(b'$').await?; - self.write_decimal(len as u64).await?; - self.stream.write_all(val).await?; - self.stream.write_all(b"\r\n").await?; - } - // Encoding an `Array` from within a value cannot be done using a - // recursive strategy. In general, async fns do not support - // recursion. Mini-redis has not needed to encode nested arrays yet, - // so for now it is skipped. - Frame::Array(_val) => unreachable!(), - } - - Ok(()) - } - - /// Write a decimal frame to the stream - async fn write_decimal(&mut self, val: u64) -> io::Result<()> { - use std::io::Write; - - // Convert the value to a string - let mut buf = [0u8; 12]; - let mut buf = Cursor::new(&mut buf[..]); - write!(&mut buf, "{}", val)?; - - let pos = buf.position() as usize; - self.stream.write_all(&buf.get_ref()[..pos]).await?; - self.stream.write_all(b"\r\n").await?; - - Ok(()) - } -} diff --git a/core/src/connect/frame.rs b/core/src/connect/frame.rs deleted file mode 100644 index a1a57fd..0000000 --- a/core/src/connect/frame.rs +++ /dev/null @@ -1,322 +0,0 @@ -/* - Appellation: frame - Contrib: FL03 - Description: A frame is used to describe units of data shared between two peers. Implementing a custom framing layer is useful for managing the various types of data that can be sent between peers. - This module provides a `Frame` enum that can be used to describe the various types of data that can be sent between peers. The `Frame` enum is used to implement a custom framing layer for - the `Connection` type. -*/ -use crate::Error; -use bytes::{Buf, Bytes}; -use std::convert::TryInto; -use std::io::Cursor; -use std::num::TryFromIntError; -use std::string::FromUtf8Error; - -/// A frame in the Redis protocol. -#[derive(Clone, Debug)] -pub enum Frame { - Simple(String), - Error(String), - Integer(u64), - Bulk(Bytes), - Null, - Array(Vec), -} - -#[derive(Debug)] -pub enum FrameError { - /// Not enough data is available to parse a message - Incomplete, - - /// Invalid message encoding - Other(Error), -} - -impl Frame { - /// Returns an empty array - pub fn array() -> Frame { - Frame::Array(vec![]) - } - - /// Push a "bulk" frame into the array. `self` must be an Array frame. - /// - /// # Panics - /// - /// panics if `self` is not an array - pub fn push_bulk(&mut self, bytes: Bytes) { - match self { - Frame::Array(vec) => { - vec.push(Frame::Bulk(bytes)); - } - _ => panic!("not an array frame"), - } - } - - /// Push an "integer" frame into the array. `self` must be an Array frame. - /// - /// # Panics - /// - /// panics if `self` is not an array - pub fn push_int(&mut self, value: u64) { - match self { - Frame::Array(vec) => { - vec.push(Frame::Integer(value)); - } - _ => panic!("not an array frame"), - } - } - - /// Checks if an entire message can be decoded from `src` - pub fn check(src: &mut Cursor<&[u8]>) -> Result<(), FrameError> { - match get_u8(src)? { - b'+' => { - get_line(src)?; - Ok(()) - } - b'-' => { - get_line(src)?; - Ok(()) - } - b':' => { - let _ = get_decimal(src)?; - Ok(()) - } - b'$' => { - if b'-' == peek_u8(src)? { - // Skip '-1\r\n' - skip(src, 4) - } else { - // Read the bulk string - let len: usize = get_decimal(src)?.try_into()?; - - // skip that number of bytes + 2 (\r\n). - skip(src, len + 2) - } - } - b'*' => { - let len = get_decimal(src)?; - - for _ in 0..len { - Frame::check(src)?; - } - - Ok(()) - } - actual => Err(format!("protocol error; invalid frame type byte `{}`", actual).into()), - } - } - - /// The message has already been validated with `scan`. - pub fn parse(src: &mut Cursor<&[u8]>) -> Result { - match get_u8(src)? { - b'+' => { - // Read the line and convert it to `Vec` - let line = get_line(src)?.to_vec(); - - // Convert the line to a String - let string = String::from_utf8(line)?; - - Ok(Frame::Simple(string)) - } - b'-' => { - // Read the line and convert it to `Vec` - let line = get_line(src)?.to_vec(); - - // Convert the line to a String - let string = String::from_utf8(line)?; - - Ok(Frame::Error(string)) - } - b':' => { - let len = get_decimal(src)?; - Ok(Frame::Integer(len)) - } - b'$' => { - if b'-' == peek_u8(src)? { - let line = get_line(src)?; - - if line != b"-1" { - return Err("protocol error; invalid frame format".into()); - } - - Ok(Frame::Null) - } else { - // Read the bulk string - let len = get_decimal(src)?.try_into()?; - let n = len + 2; - - if src.remaining() < n { - return Err(FrameError::Incomplete); - } - - let data = Bytes::copy_from_slice(&src.get_ref()[..len]); - - // skip that number of bytes + 2 (\r\n). - skip(src, n)?; - - Ok(Frame::Bulk(data)) - } - } - b'*' => { - let len = get_decimal(src)?.try_into()?; - let mut out = Vec::with_capacity(len); - - for _ in 0..len { - out.push(Frame::parse(src)?); - } - - Ok(Frame::Array(out)) - } - _ => unimplemented!(), - } - } - - /// Converts the frame to an "unexpected frame" error - pub fn to_error(&self) -> crate::Error { - format!("unexpected frame: {}", self).into() - } -} - -impl PartialEq<&str> for Frame { - fn eq(&self, other: &&str) -> bool { - match self { - Frame::Simple(s) => s.eq(other), - Frame::Bulk(s) => s.eq(other), - _ => false, - } - } -} - -impl std::fmt::Display for Frame { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - use std::str; - - match self { - Frame::Simple(response) => response.fmt(fmt), - Frame::Error(msg) => write!(fmt, "error: {}", msg), - Frame::Integer(num) => num.fmt(fmt), - Frame::Bulk(msg) => match str::from_utf8(msg) { - Ok(string) => string.fmt(fmt), - Err(_) => write!(fmt, "{:?}", msg), - }, - Frame::Null => "(nil)".fmt(fmt), - Frame::Array(parts) => { - for (i, part) in parts.iter().enumerate() { - if i > 0 { - write!(fmt, " ")?; - part.fmt(fmt)?; - } - } - - Ok(()) - } - } - } -} - -fn peek_u8(src: &mut Cursor<&[u8]>) -> Result { - if !src.has_remaining() { - return Err(FrameError::Incomplete); - } - - Ok(src.get_ref()[0]) -} - -fn get_u8(src: &mut Cursor<&[u8]>) -> Result { - if !src.has_remaining() { - return Err(FrameError::Incomplete); - } - - Ok(src.get_u8()) -} - -fn skip(src: &mut Cursor<&[u8]>, n: usize) -> Result<(), FrameError> { - if src.remaining() < n { - return Err(FrameError::Incomplete); - } - - src.advance(n); - Ok(()) -} - -/// Read a new-line terminated decimal -fn get_decimal(src: &mut Cursor<&[u8]>) -> Result { - use atoi::atoi; - - let line = get_line(src)?; - - atoi::(line).ok_or_else(|| "protocol error; invalid frame format".into()) -} - -/// Find a line -fn get_line<'a>(src: &mut Cursor<&'a [u8]>) -> Result<&'a [u8], FrameError> { - // Scan the bytes directly - let start = src.position() as usize; - // Scan to the second to last byte - let end = src.get_ref().len() - 1; - - for i in start..end { - if src.get_ref()[i] == b'\r' && src.get_ref()[i + 1] == b'\n' { - // We found a line, update the position to be *after* the \n - src.set_position((i + 2) as u64); - - // Return the line - return Ok(&src.get_ref()[start..i]); - } - } - - Err(FrameError::Incomplete) -} - -impl From for FrameError { - fn from(src: String) -> FrameError { - FrameError::Other(src.into()) - } -} - -impl From<&str> for FrameError { - fn from(src: &str) -> FrameError { - src.to_string().into() - } -} - -impl From for crate::Error { - fn from(src: FrameError) -> crate::Error { - crate::Error::from(src.to_string()) - } -} - -impl From> for FrameError { - fn from(src: Box) -> FrameError { - FrameError::Other(src.into()) - } -} - -impl From> for FrameError { - fn from(src: Box) -> FrameError { - FrameError::Other(src.into()) - } -} - -impl From for FrameError { - fn from(_src: FromUtf8Error) -> FrameError { - "protocol error; invalid frame format".into() - } -} - -impl From for FrameError { - fn from(_src: TryFromIntError) -> FrameError { - "protocol error; invalid frame format".into() - } -} - -impl std::error::Error for FrameError {} - -impl std::fmt::Display for FrameError { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - FrameError::Incomplete => "stream ended early".fmt(fmt), - FrameError::Other(err) => err.fmt(fmt), - } - } -} diff --git a/core/src/connect/mod.rs b/core/src/connect/mod.rs deleted file mode 100644 index 68386e4..0000000 --- a/core/src/connect/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - Appellation: connect - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::{connection::*, frame::*}; - -mod connection; -mod frame; - -use async_trait::async_trait; -use bytes::Buf; -use tokio::net::ToSocketAddrs; - -pub trait TokioFrame { - type Error; - fn check(buf: &mut impl Buf) -> Result<(), Self::Error>; - fn parse(buf: &mut impl Buf) -> Result - where - Self: Sized; -} - -#[async_trait] -pub trait AsyncConnection { - type Error: Send + Sync; - - async fn connect(addr: impl ToSocketAddrs) -> Result - where - Self: Sized; - fn read(&mut self) -> Result, Self::Error>; - async fn write(&mut self, frame: &Frame) -> Result<(), Self::Error>; -} diff --git a/core/src/delay.rs b/core/src/delay.rs deleted file mode 100644 index 7c51943..0000000 --- a/core/src/delay.rs +++ /dev/null @@ -1,151 +0,0 @@ -/* - Appellation: delay - Contrib: FL03 - Description: ... Summary ... -*/ -use std::sync::{Arc, Mutex}; -use std::task::{Context, Poll, Waker}; -use std::time::{Duration, Instant}; -use std::{future::Future, pin::Pin, thread}; -use tokio::sync::Notify; - -pub async fn delay(dur: Duration) { - let when = Instant::now() + dur; - let notify = Arc::new(Notify::new()); - let notify2 = notify.clone(); - - thread::spawn(move || { - let now = Instant::now(); - - if now < when { - thread::sleep(when - now); - } - - notify2.notify_one(); - }); - - notify.notified().await; -} - -/// The `Delay` future represents an asynchronous sleep. -#[derive(Clone, Debug)] -pub struct Delay { - // This is Some when we have spawned a thread, and None otherwise. - waker: Option>>, - // The `Instant` at which the delay will complete. - when: Instant, -} - -impl Delay { - pub fn new(when: Instant) -> Self { - Self { waker: None, when } - } - /// Adjusts the delay to be `duration` earlier. - pub fn decrease(&mut self, duration: Duration) { - self.when -= duration; - } - /// Adjusts the delay to be `duration` later. - pub fn increase(&mut self, duration: Duration) { - self.when += duration; - } - /// Returns the `Waker` that will be notified when the delay completes. - pub fn waker(&self) -> Option>> { - self.waker.clone() - } - /// Returns the `Instant` at which the delay will complete. - pub fn when(&self) -> Instant { - self.when - } -} - -impl Default for Delay { - fn default() -> Self { - Self::new(Instant::now()) - } -} - -impl Future for Delay { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { - // First, if this is the first time the future is called, spawn the - // timer thread. If the timer thread is already running, ensure the - // stored `Waker` matches the current task's waker. - if let Some(waker) = &self.waker { - let mut waker = waker.lock().unwrap(); - - // Check if the stored waker matches the current task's waker. - // This is necessary as the `Delay` future instance may move to - // a different task between calls to `poll`. If this happens, the - // waker contained by the given `Context` will differ and we - // must update our stored waker to reflect this change. - if !waker.will_wake(cx.waker()) { - *waker = cx.waker().clone(); - } - } else { - let when = self.when; - let waker = Arc::new(Mutex::new(cx.waker().clone())); - self.waker = Some(waker.clone()); - - // This is the first time `poll` is called, spawn the timer thread. - thread::spawn(move || { - let now = Instant::now(); - - if now < when { - thread::sleep(when - now); - } - - // The duration has elapsed. Notify the caller by invoking - // the waker. - let waker = waker.lock().unwrap(); - waker.wake_by_ref(); - }); - } - - // Once the waker is stored and the timer thread is started, it is - // time to check if the delay has completed. This is done by - // checking the current instant. If the duration has elapsed, then - // the future has completed and `Poll::Ready` is returned. - if Instant::now() >= self.when { - Poll::Ready(()) - } else { - // The duration has not elapsed, the future has not completed so - // return `Poll::Pending`. - // - // The `Future` trait contract requires that when `Pending` is - // returned, the future ensures that the given waker is signalled - // once the future should be polled again. In our case, by - // returning `Pending` here, we are promising that we will - // invoke the given waker included in the `Context` argument - // once the requested duration has elapsed. We ensure this by - // spawning the timer thread above. - // - // If we forget to invoke the waker, the task will hang - // indefinitely. - Poll::Pending - } - } -} - -impl PartialEq for Delay { - fn eq(&self, other: &Self) -> bool { - self.when == other.when - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_delay() { - assert!(Delay::default().waker.is_none()); - - let start = Instant::now(); - let dur = Duration::new(1, 0); - - let mut delay = Delay::new(start); - delay.increase(dur); - assert_eq!(delay.when, start + dur); - } -} diff --git a/core/src/epoch.rs b/core/src/epoch.rs deleted file mode 100644 index b1d911b..0000000 --- a/core/src/epoch.rs +++ /dev/null @@ -1,99 +0,0 @@ -/* - Appellation: epoch - Contrib: FL03 - Description: ... Summary ... -*/ -use serde::{Deserialize, Serialize}; -use std::time::Duration; - -pub enum EpochPerspective { - Before, - During, - After, -} - -/// An [Epoch] consists of a start time and optionally, a duration (seconds). If None, the system assumes an infinite duration -#[derive( - Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct Epoch { - pub duration: Duration, - pub start: i64, -} - -impl Epoch { - pub fn new(duration: Option, start: impl Into) -> Self { - Self { - duration: duration.unwrap_or_else(|| Duration::from_secs(1)), - start: start.into(), - } - } - pub fn duration(&self) -> Duration { - self.duration - } - /// Returns the [Duration] of time since the [Epoch] was created - pub fn elapsed(&self) -> Duration { - let now: i64 = chrono::Utc::now().timestamp(); - Duration::from_secs((now - self.start) as u64) - } - /// Returns the end time of the [Epoch] - pub fn end(&self) -> i64 { - self.start + self.duration.as_secs() as i64 - } - /// Returns true if the epoch has expired - pub fn is_expired(&self) -> bool { - self.elapsed() > self.duration - } - /// Returns the start time of the [Epoch] - pub fn start(&self) -> i64 { - self.start - } - pub fn tstep(&self, n: usize) -> Vec { - let step_size = self.duration.div_f64(n as f64).as_secs() as i64; - (0..n) - .map(|i| self.start + (i as i64 * step_size)) - .collect() - } -} - -impl Default for Epoch { - fn default() -> Self { - Self::new(Some(Duration::new(1, 0)), chrono::Utc::now().timestamp()) - } -} - -impl std::fmt::Display for Epoch { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", serde_json::to_string(&self).unwrap()) - } -} - -impl From for Epoch { - fn from(duration: Duration) -> Self { - Self::new(Some(duration), chrono::Utc::now().timestamp()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_epoch() { - let epoch = Epoch::from(Duration::new(2, 0)); - assert_eq!(epoch.duration(), Duration::new(2, 0)); - assert_eq!( - epoch.end(), - epoch.start() + epoch.duration().as_secs() as i64 - ); - assert_eq!(epoch.is_expired(), false); - assert_eq!(epoch.tstep(1), vec![epoch.start()]); - assert_eq!( - epoch.tstep(2), - vec![ - epoch.start(), - epoch.start() + epoch.duration().div_f64(2.0).as_secs() as i64 - ] - ); - } -} diff --git a/core/src/error.rs b/core/src/error.rs new file mode 100644 index 0000000..98ae317 --- /dev/null +++ b/core/src/error.rs @@ -0,0 +1,40 @@ +/* + appellation: error + authors: @FL03 +*/ +//! this module defines the core error type for the crate + +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, string::String}; +/// a type alias for a [`Result`](core::result::Result) configured to use the custom [`Error`] type. +pub type Result = core::result::Result; + +/// The custom error type for the crate. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[cfg(feature = "alloc")] + #[error(transparent)] + BoxError(#[from] Box), + #[error(transparent)] + FmtError(#[from] core::fmt::Error), + #[cfg(feature = "std")] + #[error(transparent)] + IOError(#[from] std::io::Error), + #[cfg(feature = "alloc")] + #[error("Unknown Error: {0}")] + Unknown(String), +} + +#[cfg(feature = "alloc")] +impl From<&str> for Error { + fn from(value: &str) -> Self { + Self::Unknown(String::from(value)) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + fn from(value: String) -> Self { + Self::Unknown(value) + } +} diff --git a/core/src/errors/asynchronous.rs b/core/src/errors/asynchronous.rs deleted file mode 100644 index 1278bab..0000000 --- a/core/src/errors/asynchronous.rs +++ /dev/null @@ -1,122 +0,0 @@ -/* - Appellation: error - Contrib: FL03 - Description: ... Summary ... -*/ -use super::Error; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive( - Clone, - Debug, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, - VariantNames, -)] -#[strum(serialize_all = "title_case")] -pub enum AsyncError { - BufError(String), - CapacityError(String), - ConnectionError(String), - #[default] - Error(String), - IOError(String), - RecvError(String), - RuntimeError(String), - SendError(String), - SyncError(String), -} - -impl AsyncError { - pub fn as_bytes(&self) -> &[u8] { - match self { - AsyncError::Error(e) => e.as_bytes(), - AsyncError::IOError(e) => e.as_bytes(), - AsyncError::BufError(e) => e.as_bytes(), - AsyncError::ConnectionError(e) => e.as_bytes(), - AsyncError::RecvError(e) => e.as_bytes(), - AsyncError::SendError(e) => e.as_bytes(), - AsyncError::RuntimeError(e) => e.as_bytes(), - AsyncError::SyncError(e) => e.as_bytes(), - AsyncError::CapacityError(e) => e.as_bytes(), - } - } - pub fn boxed(self) -> Box { - Box::new(self) - } -} - -impl std::error::Error for AsyncError {} - -impl From for Error { - fn from(error: AsyncError) -> Self { - Self::AsyncError(error) - } -} - -impl From> for AsyncError { - fn from(error: Box) -> Self { - Self::Error(error.to_string()) - } -} - -impl From for AsyncError { - fn from(error: anyhow::Error) -> Self { - Self::Error(error.to_string()) - } -} - -impl From for AsyncError { - fn from(error: serde_json::Error) -> Self { - Self::Error(error.to_string()) - } -} - -impl From for AsyncError { - fn from(error: tokio::io::Error) -> Self { - Self::IOError(error.to_string()) - } -} - -impl From for AsyncError { - fn from(error: tokio::net::tcp::ReuniteError) -> Self { - Self::ConnectionError(error.to_string()) - } -} - -impl From for AsyncError { - fn from(error: tokio::sync::AcquireError) -> Self { - Self::ConnectionError(error.to_string()) - } -} - -impl From> for AsyncError { - fn from(error: tokio::sync::SetError) -> Self { - Self::ConnectionError(error.to_string()) - } -} - -impl From> for AsyncError { - fn from(error: tokio::sync::mpsc::error::SendError) -> Self { - Self::SendError(error.to_string()) - } -} - -impl From for AsyncError { - fn from(error: tokio::sync::oneshot::error::RecvError) -> Self { - Self::RecvError(error.to_string()) - } -} diff --git a/core/src/errors/error.rs b/core/src/errors/error.rs deleted file mode 100644 index 2d82734..0000000 --- a/core/src/errors/error.rs +++ /dev/null @@ -1,133 +0,0 @@ -/* - Appellation: error - Contrib: FL03 - Description: ... Summary ... -*/ -use super::AsyncError; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive( - Clone, - Debug, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, - VariantNames, -)] -#[strum(serialize_all = "title_case")] -pub enum Error { - AsyncError(AsyncError), - CapacityError(String), - CompileError(String), - ConnectionError(String), - ExportError(String), - #[default] - Error(String), - ExecutionError(String), - Incomplete(String), - RangeError, - TypeError, - IOError(String), - MemoryError(String), - NotFound, - RecvError(String), - SendError(String), - StateError, - StoreError, - TranslateError, - TransformError, - TapeError, - RuntimeError(String), - ValidationError, -} - -impl Error { - pub fn as_bytes(&self) -> &[u8] { - match self { - Error::Error(e) => e.as_bytes(), - Error::IOError(e) => e.as_bytes(), - Error::AsyncError(e) => e.as_bytes(), - Error::CompileError(e) => e.as_bytes(), - Error::ConnectionError(e) => e.as_bytes(), - Error::ExportError(e) => e.as_bytes(), - Error::ExecutionError(e) => e.as_bytes(), - Error::Incomplete(e) => e.as_bytes(), - Error::MemoryError(e) => e.as_bytes(), - Error::RecvError(e) => e.as_bytes(), - Error::SendError(e) => e.as_bytes(), - Error::RuntimeError(e) => e.as_bytes(), - Error::TranslateError => b"TranslateError", - Error::TransformError => b"TransformError", - Error::TapeError => b"TapeError", - Error::ValidationError => b"ValidationError", - Error::RangeError => b"RangeError", - Error::TypeError => b"TypeError", - Error::StateError => b"StateError", - Error::StoreError => b"StoreError", - Error::CapacityError(e) => e.as_bytes(), - Error::NotFound => b"NotFound", - } - } -} - -impl std::error::Error for Error {} - -impl From for Error { - fn from(error: String) -> Self { - Error::Error(error) - } -} - -impl From> for Error { - fn from(error: Box) -> Self { - Error::Error(error.to_string()) - } -} - -impl From> for Error { - fn from(error: Box) -> Self { - Error::AsyncError(error.into()) - } -} - -impl From for Error { - fn from(error: anyhow::Error) -> Self { - Error::Error(error.to_string()) - } -} - -impl From for Error { - fn from(error: std::io::Error) -> Self { - Error::IOError(error.to_string()) - } -} - -impl From for Error { - fn from(error: serde_json::Error) -> Self { - Error::Error(error.to_string()) - } -} - -impl From> for Error { - fn from(error: tokio::sync::mpsc::error::SendError) -> Self { - Error::AsyncError(error.into()) - } -} - -impl From for Error { - fn from(error: tokio::sync::oneshot::error::RecvError) -> Self { - Error::AsyncError(error.into()) - } -} diff --git a/core/src/errors/mod.rs b/core/src/errors/mod.rs deleted file mode 100644 index 782fb5e..0000000 --- a/core/src/errors/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -/* - Appellation: errors - Contrib: FL03 - Description: ... Summary ... -*/ -pub use self::{asynchronous::*, error::*}; - -mod asynchronous; -mod error; - -pub trait BaseError: std::error::Error {} diff --git a/core/src/lib.rs b/core/src/lib.rs index a2535a2..7a8d035 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,30 +1,37 @@ -/* - Appellation: core - Contrib: FL03 -*/ -pub use self::{errors::*, primitives::*, specs::*, utils::*}; - -mod errors; -mod primitives; -mod specs; -mod utils; - -pub mod actors; -pub mod compute; -pub mod connect; -pub mod delay; -pub mod epoch; -pub mod states; -pub mod tasks; - -pub mod prelude { - pub use super::actors::*; - pub use super::compute::*; - pub use super::connect::*; - pub use super::delay::*; - pub use super::epoch::*; - pub use super::states::*; - pub use super::tasks::*; - - pub use super::{errors::*, primitives::*, specs::*, utils::*}; -} +/* + Appellation: core + Contrib: FL03 +*/ +//! this core components of the contained crate +#![allow( + clippy::missing_safety_doc, + clippy::module_inception, + clippy::needless_doctest_main, + clippy::upper_case_acronyms +)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(any(feature = "std", feature = "alloc")))] +compiler_error! { + "Either the 'std' or 'alloc' feature must be enabled." +} + +#[cfg(feature = "alloc")] +extern crate alloc; + +#[doc(inline)] +pub use self::error::{Error, Result}; + +#[macro_use] +pub(crate) mod macros { + #[macro_use] + pub mod seal; + #[macro_use] + pub mod wrapper_ops; + #[macro_use] + pub mod wrapper; +} + +pub mod error; + +pub mod prelude {} diff --git a/core/src/macros/seal.rs b/core/src/macros/seal.rs new file mode 100644 index 0000000..d6c7d80 --- /dev/null +++ b/core/src/macros/seal.rs @@ -0,0 +1,31 @@ +/* + Appellation: seal + Contrib: FL03 +*/ +//! The public parts of this private module are used to create traits +//! that cannot be implemented outside of our own crate. This way we +//! can feel free to extend those traits without worrying about it +//! being a breaking change for other implementations. + +/// If this type is pub but not publicly reachable, third parties +/// can't name it and can't implement traits using it. +#[allow(dead_code)] +pub struct Seal; + +#[allow(unused_macros)] +macro_rules! private { + () => { + /// This trait is private to implement; this method exists to make it + /// impossible to implement outside the crate. + #[doc(hidden)] + fn __private__(&self) -> $crate::macros::seal::Seal; + }; +} +#[allow(unused_macros)] +macro_rules! seal { + () => { + fn __private__(&self) -> $crate::macros::seal::Seal { + $crate::macros::seal::Seal + } + }; +} diff --git a/core/src/macros/wrapper.rs b/core/src/macros/wrapper.rs new file mode 100644 index 0000000..f7348b7 --- /dev/null +++ b/core/src/macros/wrapper.rs @@ -0,0 +1,176 @@ +/* + Appellation: wrapper + Contrib: @FL03 +*/ + +/// A macro to implement formatting traits for wrapper structs +/// +/// For tuple structs, use the gollowing: +/// ```ignore +/// fmt_wrapper! { +/// WrapperType::(Display, Debug, ...); +/// } +/// ``` +/// +/// For structs with named fields, use the following syntax, replacing `field` with the actual field name: +/// +/// ```ignore +/// fmt_wrapper! { +/// WrapperType.field::(Display, Debug, ...) +/// } +/// ``` +#[macro_export] +macro_rules! fmt_wrapper { + ($s:ident<$T:ident>.$field:ident::($($trait:ident),* $(,)?)) => { + $( + $crate::fmt_wrapper!(@impl $s<$T>::$trait.$field); + )* + }; + ($s:ident<$T:ident>$(.$field:ident)?::($($trait:ident),* $(,)?)) => { + $( + $crate::fmt_wrapper!(@impl $s<$T>::$trait.0); + )* + }; + (@impl $s:ident<$T:ident>::$trait:ident.$field:tt) => { + impl<$T> ::core::fmt::$trait for $s<$T> + where + $T: ::core::fmt::$trait + { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::$trait::fmt( + &self.$field, + f, + ) + } + } + }; +} + +#[macro_export] +macro_rules! wrapper { + ($($S:ident($vis:vis $T:ident) $(where $($rest:tt)*)?;),* $(,)?) => { + $( + $crate::wrapper!(@impl $S($vis $T) $(where $($rest)*)?;); + )* + }; + (@impl + #[derive($($derive:ident),*)] + $S:ident($vis:vis $T:ident) $(where $($rest:tt)*)?; + ) => { + #[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd, $($derive),*)] + #[cfg_attr( + feature = "serde", + derive(serde::Deserialize, serde::Serialize), + serde(default, transparent), + )] + #[repr(transparent)] + pub struct $S<$T>($vis $T) $(where $($rest)*)?; + + impl<$T> $S<$T> { + /// returns a new instance initialized with the default value + pub fn new() -> Self + where + $T: Default, + { + Self($T::default()) + } + /// returns a new instance with the given value + pub fn from_value(value: $T) -> Self { + Self(value) + } + /// returns an immutable reference to the inner value + pub const fn get(&self) -> &$T { + &self.0 + } + /// returns a mutable reference to the inner value + pub const fn get_mut(&mut self) -> &mut $T { + &mut self.0 + } + /// consumes the current instance to return the inner value + pub fn into_inner(self) -> $T { + self.0 + } + /// applies the given function to the inner value and returns a new instance with + /// the result + pub fn map(self, f: F) -> $S + where + F: FnOnce($T) -> R, + { + $S(f(self.0)) + } + /// uses the [`replace`](core::mem::replace) method to update and return the inner value + pub fn replace(&mut self, value: $T) -> $T { + core::mem::replace(self.get_mut(), value) + } + /// update the innerstate before returing a mutable reference to the wrapper + pub fn set(&mut self, value: $T) -> &mut Self { + *self.get_mut() = value; + self + } + /// uses the [`take`](core::mem::take) method to replace the inner value with the default + /// value to return its previous value + pub fn take(&mut self) -> $T + where + $T: Default, + { + core::mem::take(self.get_mut()) + } + /// consumes the current instance to create another with the given value + pub fn with(self, value: $T) -> Self { + Self(value) + } + /// captures a referenced value in a new instance + pub fn view(&self) -> $S<&$T> { + $S(self.get()) + } + /// captures a mutable reference to the inner value + pub fn view_mut(&mut self) -> $S<&mut $T> { + $S(self.get_mut()) + } + } + + impl<$T> AsRef<$T> for $S<$T> { + fn as_ref(&self) -> &$T { + self.get() + } + } + + impl<$T> AsMut<$T> for $S<$T> { + fn as_mut(&mut self) -> &mut $T { + self.get_mut() + } + } + + impl<$T> ::core::borrow::Borrow<$T> for $S<$T> { + fn borrow(&self) -> &$T { + self.get() + } + } + + impl<$T> ::core::borrow::BorrowMut<$T> for $S<$T> { + fn borrow_mut(&mut self) -> &mut $T { + self.get_mut() + } + } + + impl<$T> ::core::ops::Deref for $S<$T> { + type Target = $T; + + fn deref(&self) -> &Self::Target { + self.get() + } + } + + impl<$T> ::core::ops::DerefMut for $S<$T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.get_mut() + } + } + + impl<$T> From<$T> for $S<$T> { + fn from(value: $T) -> Self { + Self(value) + } + } + }; +} diff --git a/core/src/macros/wrapper_ops.rs b/core/src/macros/wrapper_ops.rs new file mode 100644 index 0000000..ab5192b --- /dev/null +++ b/core/src/macros/wrapper_ops.rs @@ -0,0 +1,205 @@ +/* + appellation: wrapper_ops + authors: @FL03 +*/ + +/// the [`impl_wrapper_binary!`] macro implements binary operations for a wrapper type. +/// +/// ## Syntax +/// +/// For tuple structs: +/// +/// ```ignore +/// impl_wrapper_binary! { +/// WrapperType::<[Op1.call, Op2.call, ...]> +/// } +/// ``` +/// +/// For structs with named fields: +/// +/// ```ignore +/// impl_wrapper_binary! { +/// ${struct}.$field::<[Op1.call, Op2.call, ...]> +/// } +/// ``` +#[macro_export] +macro_rules! impl_wrapper_binary { + ($s:ident.$field:tt::<[$($op:ident.$call:ident),* $(,)?]>) => { + $( + $crate::impl_wrapper_binary!(@impl $s::$op.$call($field)); + $crate::impl_wrapper_binary!(@mut $s::$op.$call($field)); + )* + }; + ($s:ident::<[$($op:ident.$call:ident),* $(,)?]>) => { + $( + $crate::impl_wrapper_binary!(@impl $s::$op.$call(0)); + $crate::impl_wrapper_binary!(@mut $s::$op.$call(0)); + )* + }; + (@impl $s:ident::$op:ident.$call:ident($field:tt)) => { + 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.$field, rhs.$field)) + } + } + + 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.$field, rhs.$field)) + } + } + + 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.$field, &rhs.$field)) + } + } + + 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.$field, &rhs.$field)) + } + } + + 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.$field, rhs.$field)) + } + } + + 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.$field, &rhs.$field)) + } + } + + 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.$field, &rhs.$field)) + } + } + }; + (@mut $s:ident::$op:ident.$call:ident) => { + paste::paste! { + $crate::impl_wrapper_binary_mut!(@impl $s::[<$op Assign>].[<$call _assign>]); + } + }; +} +#[macro_export] +macro_rules! impl_wrapper_binary_mut { + ($s:ident::<[$($op:ident.$call:ident),* $(,)?]>) => { + $( + $crate::impl_wrapper_binary_mut!(@impl $s::$op.$call); + )* + }; + (@impl $s:ident::$op:ident.$call:ident($field:tt)) => { + 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.$field, rhs.$field) + } + } + }; +} +#[macro_export] +macro_rules! impl_wrapper_unary { + ($s:ident::<[$($op:ident.$call:ident),* $(,)?]>) => { + $( + $crate::impl_wrapper_unary!(@impl $s::$op.$call(0)); + )* + }; + (@impl $s:ident::$op:ident.$call:ident($field:tt)) => { + 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.$field)) + } + } + + 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.$field)) + } + } + + 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.$field)) + } + } + + impl<'a, A, B> ::core::ops::$op for $s<&'a A> + where + &'a A: ::core::ops::$op, + { + type Output = $s; + + fn $call(self) -> Self::Output { + $s(core::ops::$op::$call(self.$field)) + } + } + + impl<'a, A, B> ::core::ops::$op for $s<&'a mut A> + where + &'a mut A: ::core::ops::$op, + { + type Output = $s; + + fn $call(self) -> Self::Output { + $s(core::ops::$op::$call(self.$field)) + } + } + }; +} diff --git a/core/src/primitives.rs b/core/src/primitives.rs deleted file mode 100644 index e7caee8..0000000 --- a/core/src/primitives.rs +++ /dev/null @@ -1,22 +0,0 @@ -/* - Appellation: primitives - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::{constants::*, types::*}; - -mod constants {} - -mod types { - use crate::{AsyncError, Error}; - use std::sync::{Arc, Mutex}; - - /// A type alias for a `Result` with the error type `AsyncError`. - pub type AsyncResult = std::result::Result; - /// Type alias for a [Result] - pub type Resultant = Result; - /// A type alias for a thread-safe [Vec] of [Mutex]es. - pub type Sharded = Arc>>; - /// A type alias for an thread-safe [Mutex]. - pub type Shared = Arc>; -} diff --git a/core/src/specs.rs b/core/src/specs.rs deleted file mode 100644 index 43022d6..0000000 --- a/core/src/specs.rs +++ /dev/null @@ -1,202 +0,0 @@ -/* - Appellation: specs - Contrib: FL03 - Description: ... Summary ... -*/ -use std::ops::{Index, IndexMut}; -use std::vec; - -/// [ArrayLike] describes the basic behaviors of an array-like structure -pub trait ArrayLike: - AsMut> + AsRef> + Eq + IndexMut + Iterable + Ord -{ - /// [ArrayLike::append] describes a method for appending another array to the end of the array - fn append(&mut self, elem: &mut Self) { - self.as_mut().append(elem.as_mut()); - } - fn as_slice(&self) -> &[T] { - self.as_ref().as_slice() - } - /// The capacity of the array - fn capacity(&self) -> usize { - self.as_ref().capacity() - } - /// [ArrayLike::clear] describes a method for clearing the array - fn clear(&mut self) { - self.as_mut().clear(); - } - /// [ArrayLike::contains] describes a method for checking if an element is present in the array - fn contains(&self, elem: &T) -> bool { - self.as_ref().contains(elem) - } - /// [ArrayLike::count] describes a method for counting the number of times an element appears in the array - fn count(&self, elem: &T) -> usize { - self.as_ref().iter().filter(|&x| x == elem).count() - } - /// [ArrayLike::dedup] describes a method for removing duplicate elements from the array - fn dedup(&mut self) { - self.as_mut().dedup(); - } - /// [ArrayLike::dedup_by] describes a method for removing duplicate elements from the array using a custom comparison function - fn dedup_by(&mut self, same_bucket: F) - where - F: FnMut(&mut T, &mut T) -> bool, - { - self.as_mut().dedup_by(same_bucket); - } - /// [ArrayLike::dedup_by_key] describes a method for removing duplicate elements from the array using a custom key extraction function - fn dedup_by_key(&mut self, key: F) - where - F: FnMut(&mut T) -> K, - K: PartialEq, - { - self.as_mut().dedup_by_key(key); - } - /// [ArrayLike::drain] describes a method for removing a range of elements from the array - fn drain(&mut self, range: std::ops::Range) -> vec::Drain { - self.as_mut().drain(range) - } - /// [ArrayLike::filter] describes a method for filtering the array - fn filter(&self, predicate: impl Fn(&T) -> bool) -> Vec { - self.as_ref() - .iter() - .filter(|&x| predicate(x)) - .cloned() - .collect() - } - /// [ArrayLike::first] describes a method for getting a reference to the first element in the array - fn first(&self) -> Option<&T> { - self.as_ref().first() - } - /// [ArrayLike::get] describes a method for getting a reference to an element at a specific position - fn get(&self, index: usize) -> Option<&T> { - if index < self.len() { - Some(&self[index]) - } else { - None - } - } - /// [ArrayLike::get_mut] describes a method for getting a mutable reference to an element at a specific position - fn get_mut(&mut self, index: usize) -> Option<&mut T> { - if index < self.len() { - Some(&mut self[index]) - } else { - None - } - } - /// [ArrayLike::is_empty] checks if the array is empty - fn is_empty(&self) -> bool { - self.len() == 0 - } - /// [ArrayLike::last] describes a method for gettings the last element in the array - fn last(&self) -> Option<&T> { - self.as_ref().last() - } - /// [ArrayLike::len] describes a method for getting the length of the array - fn len(&self) -> usize { - self.as_ref().len() - } - /// [ArrayLike::pop] describes a method for removing the last element from the array - fn pop(&mut self) -> Option { - self.as_mut().pop() - } - /// [ArrayLike::push] describes a method for adding an element to the end of the array - fn push(&mut self, elem: T) { - self.as_mut().push(elem); - } - /// [ArrayLike::remove] describes a method for removing an element at a specific position - fn remove(&mut self, index: usize) -> T { - self.as_mut().remove(index) - } - fn reverse(&mut self) { - self.as_mut().reverse(); - } - /// [ArrayLike::set] describes a method for setting the value of an element at a specific position - fn set(&mut self, index: usize, elem: T) { - self[index] = elem; - } - /// [ArrayLike::shrink_to] describes a method for shrinking the capacity of the array to a specific minimum - fn shrink_to(&mut self, min_capacity: usize) { - self.as_mut().shrink_to(min_capacity); - } - /// [ArrayLike::shrink_to_fit] describes a method for shrinking the capacity of the array to match its length - fn shrink_to_fit(&mut self) { - self.as_mut().shrink_to_fit(); - } - /// [ArrayLike::splice] describes a method for removing a range of elements and replacing them with another array - fn splice(&mut self, range: std::ops::Range, replace_with: Vec) -> Vec { - self.as_mut().splice(range, replace_with).collect() - } - /// [ArrayLike::split_off] describes a method for splitting the array into two at a specific position - fn split_off(&mut self, at: usize) -> Vec { - self.as_mut().split_off(at) - } - /// [ArrayLike::swap_remove] describes a method for removing an element at a specific position and returning it, replacing it with the last element - fn swap_remove(&mut self, index: usize) -> T { - self.as_mut().swap_remove(index) - } - /// [ArrayLike::truncate] describes a method for truncating the array to a specific length - fn truncate(&mut self, len: usize) { - self.as_mut().truncate(len); - } -} - -pub trait AsBytes { - fn as_bytes(&self) -> &[u8]; -} - -impl AsBytes for T -where - T: AsRef<[u8]>, -{ - fn as_bytes(&self) -> &[u8] { - self.as_ref() - } -} - -pub trait AsMutBytes { - fn as_mut_bytes(&mut self) -> &mut [u8]; -} - -impl AsMutBytes for T -where - T: AsMut<[u8]>, -{ - fn as_mut_bytes(&mut self) -> &mut [u8] { - self.as_mut() - } -} - -/// [Include] describes the basic behaviors of a structure which can include a new element -/// [Include] is designed to be an alternative to [ArrayLike::push] for structures which may or may not have a natural ordering -pub trait Include { - fn include(&mut self, elem: T); -} - -pub trait TryInclude { - type Error; - - fn try_include(&mut self, elem: T) -> Result; -} - -/// [Insert] describes the basic behaviors of a structure insert a new element given an index or key -pub trait Insert { - fn insert(&mut self, key: Idx, elem: V); -} - -pub trait TryInsert { - type Error; - - fn try_insert(&mut self, key: Idx, elem: V) -> Result; -} - -/// [Iterable] describes the basic behaviors of an iterable structure -pub trait Iterable -where - Self: Extend - + FromIterator - + Index - + Insert - + IntoIterator, -{ -} diff --git a/core/src/states/mod.rs b/core/src/states/mod.rs deleted file mode 100644 index 26b84ec..0000000 --- a/core/src/states/mod.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* - Appellation: state - Contrib: FL03 -*/ -pub use self::state::*; - -mod state; - -use crate::Shared; - -pub trait AsyncStateful: Clone { - fn state(&self) -> Shared; - fn update_state(&mut self, state: Shared); -} - -/// [Stateful] describes a stateful object capable of assuming any state defined by [StateSpec] -pub trait Stateful: Clone { - /// [Stateful::state] is used to get the state of the object - fn state(&self) -> S; - /// [Stateful::update_state] is used to update the state of the object - fn update_state(&mut self, state: S); -} - -impl Stateful for S -where - S: StateSpec + Copy, -{ - fn state(&self) -> S { - *self - } - fn update_state(&mut self, state: S) { - *self = state; - } -} - -/// [StateSpec] is used by [Stateful] to describe a specific state -pub trait StateSpec {} - -impl StateSpec for T {} diff --git a/core/src/states/state.rs b/core/src/states/state.rs deleted file mode 100644 index b8e6c9d..0000000 --- a/core/src/states/state.rs +++ /dev/null @@ -1,103 +0,0 @@ -/* - Appellation: state - Contrib: FL03 -*/ -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Default, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - VariantNames, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum State { - #[default] - Valid = 0, - Invalid = 1, -} - -impl State { - pub fn invalid() -> Self { - Self::Invalid - } - pub fn valid() -> Self { - Self::Valid - } - pub fn invalidate(&mut self) { - *self = Self::Invalid; - } - pub fn validate(&mut self) { - *self = Self::Valid; - } -} - -impl AsRef<[u8]> for State { - fn as_ref(&self) -> &[u8] { - match self { - Self::Invalid => b"invalid", - Self::Valid => b"valid", - } - } -} - -impl Unpin for State {} - -impl std::ops::Mul for State { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - match self { - Self::Invalid => match rhs { - Self::Invalid => Self::Invalid, - Self::Valid => Self::Valid, - }, - Self::Valid => match rhs { - Self::Invalid => Self::Invalid, - Self::Valid => Self::Valid, - }, - } - } -} - -impl std::ops::MulAssign for State { - fn mul_assign(&mut self, rhs: Self) { - *self = *self * rhs; - } -} - -impl From for State { - fn from(d: i64) -> Self { - Self::from(d.abs() as usize) - } -} - -impl From for State { - fn from(d: usize) -> Self { - match d % Self::COUNT { - 0 => State::valid(), - _ => State::invalid(), - } - } -} - -impl From for i64 { - fn from(d: State) -> i64 { - d as i64 - } -} diff --git a/core/src/tasks/manager.rs b/core/src/tasks/manager.rs deleted file mode 100644 index 4122723..0000000 --- a/core/src/tasks/manager.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* - Appellation: manager - Contrib: FL03 - Description: ... summary ... -*/ diff --git a/core/src/tasks/mod.rs b/core/src/tasks/mod.rs deleted file mode 100644 index 4a5b1fa..0000000 --- a/core/src/tasks/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - Appellation: tasks - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::{manager::*, registry::*}; - -mod manager; -mod registry; - -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Task { - group: String, - name: String, -} - -impl Task { - pub fn new(group: impl ToString, name: impl ToString) -> Self { - Self { - group: group.to_string(), - name: name.to_string(), - } - } -} - -impl std::fmt::Display for Task { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}:{}", self.group, self.name) - } -} diff --git a/core/src/tasks/registry.rs b/core/src/tasks/registry.rs deleted file mode 100644 index 8eaf69a..0000000 --- a/core/src/tasks/registry.rs +++ /dev/null @@ -1,36 +0,0 @@ -/* - Appellation: registry - Contrib: FL03 - Description: ... summary ... -*/ -use super::Task; -use crate::Shared; -use std::collections::HashMap; -use std::sync::{Arc, Mutex}; - -#[derive(Clone, Debug, Default)] -pub struct TaskRegistry { - tasks: Shared>, -} - -impl TaskRegistry { - pub fn new() -> Self { - Self { - tasks: Arc::new(Mutex::new(HashMap::new())), - } - } - pub fn register(&mut self, task: Task) -> usize { - let mut tasks = self.tasks.lock().unwrap(); - let count = if let Some(cnt) = tasks.get_mut(&task) { - *cnt += 1; - *cnt - } else { - 1 - }; - tasks.insert(task, count); - count - } - pub fn running(&self) -> HashMap { - self.tasks.lock().unwrap().clone() - } -} diff --git a/core/src/utils.rs b/core/src/utils.rs deleted file mode 100644 index fe42a03..0000000 --- a/core/src/utils.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - Appellation: utils - Contrib: FL03 -*/ - -/// [subspace] is a function for creating subsets of a given [Vec] and set size -pub fn subspace(args: Vec, size: usize) -> Vec> { - let mut res = Vec::>::new(); - for i in 0..args.len() { - let tmp = (1..size) - .map(|z: usize| (i + z) % size) - .collect::>(); - for j in 0..tmp.len() { - let mut subset = vec![args[i].clone()]; - subset.append( - &mut (0..tmp.len()) - .map(|k: usize| args[tmp[(j + k) % tmp.len()]].clone()) - .collect(), - ); - res.push(subset.clone()); - } - } - res -} diff --git a/core/tests/default.rs b/core/tests/default.rs index 44c72db..b8f130e 100644 --- a/core/tests/default.rs +++ b/core/tests/default.rs @@ -1,6 +1,17 @@ -#[cfg(test)] -#[test] -fn compiles() { - let f = |x: usize| x + 1; - assert_eq!(f(1), 2); -} +/* + appellation: default + authors: @FL03 +*/ + +fn adder(a: A, b: B) -> C +where + A: core::ops::Add, +{ + a + b +} + +#[test] +fn compiles() { + assert_eq!(adder(1, 100), 101); + assert_eq!(adder(1.0, 100.0), 101.0); +} diff --git a/core/tests/macros.rs b/core/tests/macros.rs new file mode 100644 index 0000000..42a1362 --- /dev/null +++ b/core/tests/macros.rs @@ -0,0 +1,26 @@ +/* + appellation: macros + authors: @FL03 +*/ +use contained_core::fmt_wrapper; + +pub struct A(pub T); + +pub struct B { + pub field: T, +} + +fmt_wrapper! { + A::(Binary, Debug, Display, LowerHex, UpperHex, LowerExp, UpperExp, Pointer) +} + +fmt_wrapper! { + B.field::(Binary, Debug, Display, LowerHex, UpperHex, LowerExp, UpperExp, Pointer) +} + +#[test] +fn test_fmt_wrapper() { + let a = A(42); + let b = B { field: 42 }; + assert_eq!(format!("{}", a), format!("{}", b)); +} diff --git a/derive/Cargo.toml b/derive/Cargo.toml new file mode 100644 index 0000000..88b6edd --- /dev/null +++ b/derive/Cargo.toml @@ -0,0 +1,42 @@ +[package] +build = "build.rs" +description = "useful derive macros for the scsys ecosystem" +name = "contained-derive" + +authors.workspace = true +categories.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +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 +doctest = false +proc-macro = true +test = false + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = { features = ["full"], version = "2" } + +[features] +default = [] + +nightly = ["proc-macro2/nightly"] diff --git a/derive/build.rs b/derive/build.rs new file mode 100644 index 0000000..940a4ce --- /dev/null +++ b/derive/build.rs @@ -0,0 +1,8 @@ +/* + Appellation: build + Contrib: FL03 +*/ + +fn main() { + println!("cargo::rustc-check-cfg=cfg(no_std)"); +} diff --git a/derive/src/attrs.rs b/derive/src/attrs.rs new file mode 100644 index 0000000..0ae3c6e --- /dev/null +++ b/derive/src/attrs.rs @@ -0,0 +1,4 @@ +/* + appellation: attrs + authors: @FL03 +*/ diff --git a/derive/src/attrs/display_attrs.rs b/derive/src/attrs/display_attrs.rs new file mode 100644 index 0000000..0374f4b --- /dev/null +++ b/derive/src/attrs/display_attrs.rs @@ -0,0 +1,45 @@ +/* + Appellation: display_attrs + Contrib: @FL03 +*/ +use syn::parse::{Parse, ParseStream}; +use syn::{Ident, LitStr}; + +#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct DisplayAttr { + pub format: Option, +} + +impl DisplayAttr { + /// attempts to parse the attribute from the given metadata + pub fn parse_nested(meta: &syn::meta::ParseNestedMeta<'_>) -> syn::Result { + let content: syn::parse::ParseBuffer<'_>; + syn::parenthesized!(content in meta.input); + // try finding the optional name parameter + let format = content.parse::(); + // create a new instance of ParamsAttr + let parsed = DisplayAttr { + format: format.ok(), + }; + // return the parsed instance + Ok(parsed) + } +} + +#[allow(dead_code)] +pub enum DisplayMeta { + Format(LitStr), +} + +impl Parse for DisplayMeta { + fn parse(input: ParseStream) -> syn::Result { + let ident: Ident = input.parse()?; + if ident == "format" { + dbg!("found format attribute key"); + input.parse::()?; + let lit: LitStr = input.parse()?; + return Ok(Self::Format(lit)); + } + Err(syn::Error::new_spanned(ident, "unknown field")) + } +} diff --git a/derive/src/attrs/nested.rs b/derive/src/attrs/nested.rs new file mode 100644 index 0000000..23d6af4 --- /dev/null +++ b/derive/src/attrs/nested.rs @@ -0,0 +1,49 @@ +/* + Appellation: nested + Contrib: @FL03 +*/ +use super::DisplayAttr; +use syn::Ident; +use syn::parse::{Parse, ParseStream}; + +//[`Meta`] for key-value pairs + +/// [`NestedAttr`] is an enumeration of various nested attributes the crate recognizes. +#[derive(Debug)] +pub enum NestedAttr { + Display(DisplayAttr), +} + +impl NestedAttr { + /// attempts to parse the attribute from the given metadata + pub fn parse_nested(meta: &syn::meta::ParseNestedMeta<'_>) -> syn::Result { + // #[contained(display(...))] + if meta.path.is_ident("display") { + let attr = DisplayAttr::parse_nested(meta)?; + return Ok(Self::Display(attr)); + } + + Err(meta.error("unrecognized repr")) + } +} + +impl Parse for NestedAttr { + fn parse(input: ParseStream) -> syn::Result { + let ident: Ident = input.parse()?; + if ident == "display" { + dbg!("found display attribute "); + let content; + syn::parenthesized!(content in input); + // Parse an optional identifier + let format = if content.is_empty() { + None + } else { + Some(content.parse::()?) + }; + + Ok(NestedAttr::Display(DisplayAttr { format })) + } else { + Err(syn::Error::new_spanned(ident, "unknown attribute")) + } + } +} diff --git a/derive/src/attrs/root.rs b/derive/src/attrs/root.rs new file mode 100644 index 0000000..a099367 --- /dev/null +++ b/derive/src/attrs/root.rs @@ -0,0 +1,39 @@ +/* + appellation: root + authors: @FL03 +*/ +use crate::attrs::{DisplayAttr, NestedAttr}; +use syn::Attribute; + +// AST for the scsys attribute +#[derive(Debug, Default)] +pub struct ContainedAttr { + pub display: Option, +} + +impl ContainedAttr { + pub fn set_display(&mut self, display: DisplayAttr) { + self.display = Some(display); + } + + // tries to extract the scsys attribute from a list of attributes + pub fn extract(attrs: &[Attribute]) -> syn::Result { + let mut scsys = Self::default(); + for attr in attrs { + if attr.path().is_ident("contained") { + attr.parse_nested_meta(|meta| { + if let Ok(nested) = NestedAttr::parse_nested(&meta) { + match nested { + NestedAttr::Display(inner) => { + scsys.set_display(inner); + return Ok(()); + } + } + } + Err(meta.error("unrecognized attribute")) + })?; + } + } + Ok(scsys) + } +} diff --git a/derive/src/impls/wrapper.rs b/derive/src/impls/wrapper.rs new file mode 100644 index 0000000..e36c51c --- /dev/null +++ b/derive/src/impls/wrapper.rs @@ -0,0 +1,188 @@ +/* + appellation: wrapper + authors: @FL03 +*/ +use quote::quote; +use syn::{Data, DataStruct, DeriveInput, Field, Generics, Ident}; + +pub fn impl_wrapper(input: &DeriveInput) -> proc_macro2::TokenStream { + // deconstruct the input to get the struct name and generics + let DeriveInput { + data, + generics, + ident: name, + .. // ignore other fields + } = input; + // split the generics for implementation + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // handle the case where the data is a struct + if let Data::Struct(DataStruct { fields, .. }) = data { + // ensure the struct is a single field struct + if fields.len() != 1 { + panic!("The `Wrapper` macro can only be derived for single field structs"); + } + // handle the fields + let methods = fields + .iter() + .map(|field| _handle_field(field, generics, name)); + // inject generics to ensure the wrapper can be used with generic types + return quote! { + impl #impl_generics #name #ty_generics #where_clause { + #(#methods)* + } + }; + } + + panic!("The `Wrapper` macro can only be derived for single field structs"); +} + +fn _handle_field( + field: &Field, + generics: &Generics, + name: &syn::Ident, +) -> proc_macro2::TokenStream { + // deconstruct the field for easier access + let Field { + ident: field_name, + ty: field_type, + .. + } = field; + // handle both named and unnamed fields + let methods = match field_name { + Some(_) => _handle_named(field, generics, name), + None => _handle_unnamed(field, generics, name), + }; + // generate the code for the wrapper methods + quote! { + #methods + /// consumes the current instance and returns a new one that captures the result of the + /// closure on the wrapped field + #[inline] + pub fn map(self, f: F) -> #name + where + F: FnOnce(#field_type) -> U, + { + #name::new(f(self.value())) + } + /// [`replace`](core::mem::replace) the wrapped field with a new value and return + /// the old value + pub const fn replace(&mut self, value: #field_type) -> #field_type { + ::core::mem::replace(self.get_mut(), value) + } + /// set the wrapped field to a new value and return a mutable reference to the + /// current instance + #[inline] + pub fn set(&mut self, value: #field_type) -> &mut Self { + *self.get_mut() = value; + self + } + /// [`swap`](core::mem::swap) the wrapped field with another instance + pub const fn swap(&mut self, other: &mut Self) { + ::core::mem::swap(self.get_mut(), other.get_mut()); + } + /// [`take`](core::mem::take) the wrapped field and replace it with a default value + #[inline] + pub fn take(&mut self) -> #field_type + where + #field_type: Default + { + ::core::mem::take(self.get_mut()) + } + /// returns a new instance of the wrapper that contains a reference to the inner value + pub const fn view(&self) -> #name<&#field_type> { + #name::new(self.get()) + } + /// returns a new instance of the wrapper that contains a mutable reference to the + /// inner value + pub const fn view_mut(&mut self) -> #name<&mut #field_type> { + #name::new(self.get_mut()) + } + } +} + +fn _handle_named( + field: &Field, + generics: &Generics, + _name: &syn::Ident, +) -> proc_macro2::TokenStream { + let Field { + ident, + ty: field_type, + .. + } = field; + + let _where_clause_u = generics.where_clause.as_ref(); + if ident.is_none() { + panic!("The `Wrapper` macro can only be derived for single field structs"); + } + // get a reference to the field name + let field_name = ident.as_ref().unwrap(); + // implement the methods for named fields + quote! { + pub const fn new(#field_name: #field_type) -> Self { + Self { #field_name } + } + /// returns a reference to the wrapped field + pub const fn get(&self) -> &#field_type { + &self.#field_name + } + /// returns a mutable reference to the wrapped field + pub const fn get_mut(&mut self) -> &mut #field_type { + &mut self.#field_name + } + /// consumes the current instance and returns the wrapped field + #[inline] + pub fn value(self) -> #field_type { + self.#field_name + } + } +} + +fn _handle_unnamed( + field: &Field, + _generics: &Generics, + _name: &syn::Ident, +) -> proc_macro2::TokenStream { + let field_type = &field.ty; + quote! { + pub const fn new(value: #field_type) -> Self { + Self(value) + } + /// returns a reference to the wrapped field + pub const fn get(&self) -> &#field_type { + &self.0 + } + /// returns a mutable reference to the wrapped field + pub const fn get_mut(&mut self) -> &mut #field_type { + &mut self.0 + } + /// consumes the current instance and returns the wrapped field + #[inline] + pub fn value(self) -> #field_type { + self.0 + } + } +} + +fn _convert_generic_where_clause( + new_ident: &Ident, + clause: &syn::WhereClause, +) -> proc_macro2::TokenStream { + let predicates = clause.predicates.iter().map(|p| { + if let syn::WherePredicate::Type(inner) = p { + let mut pred = inner.clone(); + pred.bounded_ty = if let syn::Type::Verbatim(_ty) = &inner.bounded_ty { + syn::Type::Verbatim(quote!(#new_ident)) + } else { + inner.bounded_ty.clone() + }; + // For other types of predicates, we can just return them as is + return quote!(#pred); + } + // For other types of predicates, we can just return them as is + quote!(#p) + }); + quote! { + where #(#predicates),* + } +} diff --git a/derive/src/lib.rs b/derive/src/lib.rs new file mode 100644 index 0000000..0f1cbfe --- /dev/null +++ b/derive/src/lib.rs @@ -0,0 +1,40 @@ +/* + Appellation: contained-derive + Contrib: FL03 +*/ +//! derive macros for facilitating the creation of wrapper types + +extern crate proc_macro; +extern crate quote; +extern crate syn; + +#[allow(unused)] +pub(crate) mod attrs { + pub use self::{display_attrs::*, nested::*, root::*}; + + mod display_attrs; + mod nested; + mod root; +} + +pub(crate) mod impls { + #[doc(inline)] + pub use self::wrapper::*; + + mod wrapper; +} +use proc_macro::TokenStream; +use syn::{DeriveInput, parse_macro_input}; + +/// The [`Wrapper`] macro is designed for single-field structs, implementing additional methods +/// supporting interactions with the inner value +#[proc_macro_derive(Wrapper, attributes(scsys))] +pub fn wrapper(input: TokenStream) -> TokenStream { + // Parse the inputs into the proper struct + let ast = parse_macro_input!(input as DeriveInput); + + // Build the impl + let res = impls::impl_wrapper(&ast); + + res.into() +} diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..f8072a5 --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,42 @@ +[package] +build = "build.rs" +description = "procedural macros for managing wrappers" +name = "contained-macros" + +authors.workspace = true +categories.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +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 +doctest = false +proc-macro = true +test = false + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = { features = ["full"], version = "2" } + +[features] +default = [] + +nightly = ["proc-macro2/nightly"] diff --git a/macros/build.rs b/macros/build.rs new file mode 100644 index 0000000..940a4ce --- /dev/null +++ b/macros/build.rs @@ -0,0 +1,8 @@ +/* + Appellation: build + Contrib: FL03 +*/ + +fn main() { + println!("cargo::rustc-check-cfg=cfg(no_std)"); +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..ae441b4 --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,17 @@ +/* + Appellation: contained-macros + Contributors: FL03 +*/ +//! procedural macros for interacting with various wrappers +extern crate proc_macro; + +use proc_macro::TokenStream; + +pub(crate) mod wrap; + +/// A procedural macro for generativly creating getter methods; i.e. $field_name() -> &$field_type and $field_name_mut() -> &mut $field_type +#[proc_macro] +pub fn wrap(input: TokenStream) -> TokenStream { + println!("display: {:?}", input); + input +} diff --git a/turing/src/errors.rs b/macros/src/wrap.rs similarity index 100% rename from turing/src/errors.rs rename to macros/src/wrap.rs diff --git a/music/Cargo.toml b/music/Cargo.toml deleted file mode 100644 index 7544459..0000000 --- a/music/Cargo.toml +++ /dev/null @@ -1,51 +0,0 @@ -[package] -authors.workspace = true -categories.workspace = true -description.workspace = true -edition.workspace = true -homepage.workspace = true -keywords.workspace = true -license.workspace = true -name = "contained-music" -readme.workspace = true -repository.workspace = true -version.workspace = true - -[features] -default = [] - -[lib] -crate-type = ["cdylib", "rlib"] -test = true - -[build-dependencies] - -[dependencies] -# Custom -# decanter.workspace = true - -# Dependencies -anyhow.workspace = true -futures.workspace = true -itertools.workspace = true - -petgraph = { features = ["serde-1"], version = "0.6" } -serde.workspace = true -serde_json.workspace = true -smart-default.workspace = true -strum.workspace = true -tokio = { features = ["sync"], version = "1" } -tracing = { features = ["log"], version = "0.1" } - -[dependencies.contained-core] -path = "../core" -version = "0.1.6" - -[dev-dependencies] -lazy_static = "1" -once_cell = "1" -tokio = { features = ["macros", "rt"], version = "1"} - -[package.metadata.docs.rs] -all-features = true -rustc-args = ["--cfg", "docsrs"] diff --git a/music/src/chords/chord.rs b/music/src/chords/chord.rs deleted file mode 100644 index 7492579..0000000 --- a/music/src/chords/chord.rs +++ /dev/null @@ -1,121 +0,0 @@ -/* - Appellation: chord - Contrib: FL03 - Description: A chord is any set of notes played simultaneously; for our considerations, allow a chord to represent the alphabet of a Turing machine or automata. -*/ -use crate::Note; -use contained::{ArrayLike, Insert, Iterable}; -use serde::{Deserialize, Serialize}; -use std::ops::{Index, IndexMut}; - -/// [Chord] is a wrapper for a [Vec] of [Note] -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Chord { - chord: Vec, -} - -impl Chord { - pub fn new() -> Self { - Self { chord: Vec::new() } - } - pub fn is_triadic(&self) -> bool { - self.len() == 3 - } - pub fn with_capacity(capacity: usize) -> Self { - Self { - chord: Vec::with_capacity(capacity), - } - } -} - -impl ArrayLike for Chord {} - -impl AsMut> for Chord { - fn as_mut(&mut self) -> &mut Vec { - &mut self.chord - } -} - -impl AsRef> for Chord { - fn as_ref(&self) -> &Vec { - &self.chord - } -} - -impl Extend for Chord { - fn extend>(&mut self, iter: T) { - self.chord.extend(iter) - } -} - -impl FromIterator for Chord { - fn from_iter>(iter: T) -> Self { - Self { - chord: Vec::from_iter(iter), - } - } -} - -impl Index for Chord { - type Output = Note; - - fn index(&self, index: usize) -> &Self::Output { - &self.chord[index] - } -} - -impl IndexMut for Chord { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.chord[index] - } -} - -impl Insert for Chord { - fn insert(&mut self, index: usize, elem: Note) { - self.as_mut().insert(index, elem); - } -} - -impl Iterable for Chord {} - -impl IntoIterator for Chord { - type Item = Note; - - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.chord.into_iter() - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_chord() { - // Create a new chord - let mut chord = Chord::new(); - // Assert that the chord is empty - assert!(chord.is_empty()); - // Append a chord to the chord - chord.append(&mut Chord::from_iter([0.into(), 3.into(), 8.into()])); - // Assert that the chord is not empty; it should have 3 notes - assert_eq!(chord.len(), 3); - // Assert that the first note is 0 - assert_eq!(chord[0], 0.into()); - // Set the first note to 1 via the index operator - chord[0] = 1.into(); - // Assert that the first note is now 1 - assert_eq!(chord[0], 1.into()); - assert_eq!(chord.as_ref(), &[1.into(), 3.into(), 8.into()]); - } - - #[test] - fn test_chord_from_iter() { - let chord = Chord::from_iter([0.into(), 3.into(), 8.into()]); - assert_eq!(chord[0], 0.into()); - assert_eq!(chord[1], 3.into()); - assert_eq!(chord[2], 8.into()); - } -} diff --git a/music/src/chords/mod.rs b/music/src/chords/mod.rs deleted file mode 100644 index e34e12c..0000000 --- a/music/src/chords/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* - Appellation: chords - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::chord::*; - -mod chord; - -pub trait IntoChord { - fn into_chord(self) -> Chord; -} - -impl IntoChord for T -where - T: Into, -{ - fn into_chord(self) -> Chord { - self.into() - } -} - -pub trait FromChord { - fn from_chord(chord: Chord) -> Self; -} - -impl FromChord for T -where - T: From, -{ - fn from_chord(chord: Chord) -> Self { - Self::from(chord) - } -} diff --git a/music/src/errors.rs b/music/src/errors.rs deleted file mode 100644 index 0592794..0000000 --- a/music/src/errors.rs +++ /dev/null @@ -1,87 +0,0 @@ -/* - Appellation: errors - Contrib: FL03 - Description: ... Summary ... -*/ -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumIter, EnumVariantNames}; - -#[derive( - Clone, - Debug, - Deserialize, - Display, - EnumIter, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, -)] -#[strum(serialize_all = "title_case")] -pub enum MusicError { - CompositionError(String), - IntervalError(String), - IOError(String), - #[default] - PitchError(String), - StdError(String), - TransformationError(String), -} - -impl std::error::Error for MusicError {} - -impl From<&str> for MusicError { - fn from(error: &str) -> Self { - MusicError::StdError(error.to_string()) - } -} - -impl From for MusicError { - fn from(error: String) -> Self { - MusicError::StdError(error) - } -} - -impl From for MusicError { - fn from(error: anyhow::Error) -> Self { - MusicError::StdError(error.to_string()) - } -} - -impl From for MusicError { - fn from(error: serde_json::Error) -> Self { - MusicError::IOError(error.to_string()) - } -} - -impl From for MusicError { - fn from(error: std::io::Error) -> Self { - MusicError::IOError(error.to_string()) - } -} - -impl From> for MusicError -where - E: std::error::Error, -{ - fn from(error: Box) -> Self { - MusicError::StdError(error.to_string()) - } -} - -impl From> for MusicError { - fn from(error: Box) -> Self { - MusicError::StdError(error.to_string()) - } -} - -impl From> for MusicError { - fn from(error: Box) -> Self { - MusicError::StdError(error.to_string()) - } -} diff --git a/music/src/frequency.rs b/music/src/frequency.rs deleted file mode 100644 index 7f19066..0000000 --- a/music/src/frequency.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - Appellation: frequency - Contrib: FL03 - Description: Frequency is the number of occurrences of a repeating event or signal per unit time -*/ -use serde::{Deserialize, Serialize}; -use std::time::Duration; -use tokio::time; - -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Frequency { - pub cycle: usize, - pub period: Duration, -} - -impl Frequency { - pub fn new(period: Duration) -> Self { - Self { cycle: 0, period } - } - pub fn freq(&self) -> Duration { - Duration::new(1, 0).div_f64(self.period.as_secs_f64()) - } - pub fn interval(&self) -> time::Interval { - time::interval(self.freq()) - } - pub fn period(&self) -> Duration { - self.period - } -} - -impl Default for Frequency { - fn default() -> Self { - Self::new(Duration::new(1, 0)) - } -} diff --git a/music/src/intervals/fifths.rs b/music/src/intervals/fifths.rs deleted file mode 100644 index ca52ca2..0000000 --- a/music/src/intervals/fifths.rs +++ /dev/null @@ -1,95 +0,0 @@ -/* - Appellation: fifths - Contrib: FL03 - Description: - - Fifths: - Augmented (8) - Perfect (7) - Diminished (6) -*/ -use crate::{BoxedError, Gradient, Note}; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, - VariantNames, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum Fifths { - Augmented = 8, - Diminished = 6, - #[default] - Perfect = 7, -} - -impl Fifths { - pub fn compute(note: Note) -> (Note, Note, Note) { - ( - Self::Augmented + note, - Self::Perfect + note, - Self::Diminished + note, - ) - } -} - -impl TryFrom<(Note, Note)> for Fifths { - type Error = BoxedError; - - fn try_from(data: (Note, Note)) -> Result { - // An interval is the difference in pitch between an two notes - // We take the pitch of the result to account for its modularity; (0, 11) -> 11 but (11, 0) -> 1 - let interval: i64 = (data.1.pitch() - data.0.pitch()).pitch(); - match interval { - 6 => Ok(Self::Diminished), - 7 => Ok(Self::Perfect), - 8 => Ok(Self::Augmented), - _ => Err("Interval is not a fifth...".into()), - } - } -} - -impl std::ops::Add for Fifths { - type Output = Note; - - fn add(self, rhs: Note) -> Self::Output { - (rhs.pitch() + self as i64).into() - } -} - -impl std::ops::Sub for Fifths { - type Output = Note; - - fn sub(self, rhs: Note) -> Self::Output { - (rhs.pitch() - self as i64).into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Note; - - #[test] - fn test_fifths() { - assert_eq!(Fifths::Perfect + Note::from(0), Note::from(7)) - } -} diff --git a/music/src/intervals/fourths.rs b/music/src/intervals/fourths.rs deleted file mode 100644 index 45c972d..0000000 --- a/music/src/intervals/fourths.rs +++ /dev/null @@ -1,91 +0,0 @@ -/* - Appellation: fifths - Contrib: FL03 - Description: - - Fifths: - Augmented (8) - Perfect (7) - Diminished (6) -*/ -use crate::{BoxedError, Gradient, Note}; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, - VariantNames, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum Fourths { - #[default] - Perfect = 5, -} - -impl Fourths { - pub fn new(from: Note, to: Note) -> Result { - let interval = to.pitch() - from.pitch(); - match interval { - 5 => Ok(Fourths::Perfect), - _ => Err("Invalid interval".into()), - } - } -} - -impl TryFrom<(Note, Note)> for Fourths { - type Error = BoxedError; - - fn try_from(data: (Note, Note)) -> Result { - // An interval is the difference in pitch between an two notes - // We take the pitch of the result to account for its modularity; (0, 11) -> 11 but (11, 0) -> 1 - let interval: i64 = (data.1.pitch() - data.0.pitch()).pitch(); - match interval { - 5 => Ok(Self::Perfect), - _ => Err("Interval is not a fifth...".into()), - } - } -} - -impl std::ops::Add for Fourths { - type Output = Note; - - fn add(self, rhs: Note) -> Self::Output { - (rhs.pitch() + self as i64).into() - } -} - -impl std::ops::Sub for Fourths { - type Output = Note; - - fn sub(self, rhs: Note) -> Self::Output { - (rhs.pitch() - self as i64).into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Note; - - #[test] - fn test_fourths() { - assert_eq!(Fourths::Perfect + Note::from(0), Note::from(5)) - } -} diff --git a/music/src/intervals/mod.rs b/music/src/intervals/mod.rs deleted file mode 100644 index fb003a6..0000000 --- a/music/src/intervals/mod.rs +++ /dev/null @@ -1,206 +0,0 @@ -/* - Appellation: intervals - Contrib: FL03 - Description: A collection of common musical intervals -*/ -pub use self::{fifths::*, fourths::*, sevenths::*, thirds::*}; - -mod fifths; -mod fourths; -mod sevenths; -mod thirds; - -use crate::{Gradient, Note}; - -use itertools::Itertools; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, - VariantNames, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum Interval { - Semitone = 1, - Tone = 2, - #[default] - Third(Thirds), - Fourth(Fourths), - Fifth(Fifths), - Seventh(Sevenths), - Interval, -} - -impl Interval { - pub fn new(from: Note, to: Note) -> Self { - let interval = to.pitch() - from.pitch(); - match interval.abs() { - 1 => Interval::Semitone, - 2 => Interval::Tone, - 3 => Interval::Third(Thirds::Minor), - 4 => Interval::Third(Thirds::Major), - 5 => Interval::Fourth(Fourths::Perfect), - 6 => Interval::Fifth(Fifths::Diminished), - 7 => Interval::Fifth(Fifths::Perfect), - 8 => Interval::Fifth(Fifths::Augmented), - 9 => Interval::Seventh(Sevenths::Diminished), - 10 => Interval::Seventh(Sevenths::Major), - 11 => Interval::Seventh(Sevenths::Minor), - 12 => Interval::Seventh(Sevenths::Augmented), - _ => Interval::Interval, - } - } - pub fn increase(&self, note: Note) -> Note { - let interval: i64 = (*self).into(); - (note.pitch() + interval).into() - } - pub fn decrease(&self, note: Note) -> Note { - let interval: i64 = (*self).into(); - (note.pitch() - interval).into() - } - pub fn intervals(iter: impl IntoIterator) -> Vec { - let mut intervals = Vec::new(); - let notes = Vec::from_iter(iter); - for (a, b) in notes.into_iter().circular_tuple_windows() { - intervals.push(Interval::new(a, b)); - } - intervals - } -} - -impl From for i64 { - fn from(interval: Interval) -> i64 { - match interval { - Interval::Semitone => 1, - Interval::Tone => 2, - Interval::Third(i) => i as i64, - Interval::Fourth(i) => i as i64, - Interval::Fifth(i) => i as i64, - Interval::Seventh(i) => i as i64, - Interval::Interval => 0, - } - } -} - -impl From for tokio::time::Interval { - fn from(interval: Interval) -> tokio::time::Interval { - let interval: i64 = interval.into(); - tokio::time::interval(std::time::Duration::from_secs_f64(1.0 / interval as f64)) - } -} - -impl From for Interval { - fn from(data: Fifths) -> Interval { - Interval::Fifth(data) - } -} - -impl From for Interval { - fn from(data: Fourths) -> Interval { - Interval::Fourth(data) - } -} - -impl From for Interval { - fn from(data: Sevenths) -> Interval { - Interval::Seventh(data) - } -} - -impl From for Interval { - fn from(data: Thirds) -> Interval { - Interval::Third(data) - } -} - -impl std::ops::Add for i64 { - type Output = i64; - - fn add(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - (self + interval).pitch() - } -} - -impl std::ops::AddAssign for i64 { - fn add_assign(&mut self, rhs: Interval) { - let interval: i64 = rhs.into(); - *self = (*self + interval).pitch(); - } -} - -impl std::ops::Sub for i64 { - type Output = i64; - - fn sub(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - (self - interval).pitch() - } -} - -impl std::ops::SubAssign for i64 { - fn sub_assign(&mut self, rhs: Interval) { - let interval: i64 = rhs.into(); - *self = (*self - interval).pitch(); - } -} - -impl std::ops::Add for Interval { - type Output = Note; - - fn add(self, rhs: Note) -> Self::Output { - let interval: i64 = self.into(); - (rhs.pitch() + interval).into() - } -} - -impl std::ops::Sub for Interval { - type Output = Note; - - fn sub(self, rhs: Note) -> Self::Output { - let interval: i64 = self.into(); - (rhs.pitch() - interval).into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Note; - - #[test] - fn test_interval() { - assert_eq!(Interval::from(Thirds::Major) + Note::from(0), Note::from(4)) - } - - #[test] - fn test_intervals() { - let notes = vec![Note::from(0), Note::from(4), Note::from(7)]; - assert_eq!( - Interval::intervals(notes), - vec![ - Interval::Third(Thirds::Major), - Interval::Third(Thirds::Minor), - Interval::Fifth(Fifths::Perfect) - ] - ) - } -} diff --git a/music/src/intervals/sevenths.rs b/music/src/intervals/sevenths.rs deleted file mode 100644 index f8c5a07..0000000 --- a/music/src/intervals/sevenths.rs +++ /dev/null @@ -1,83 +0,0 @@ -/* - Appellation: sevenths - Contrib: FL03 - Description: - - Sevenths: - Augmented (12) - Major (11) - Minor(10) - Diminished (9) -*/ -use crate::{BoxedError, Gradient, Note}; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, - VariantNames, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum Sevenths { - Augmented = 12, - Diminished = 9, - #[default] - Major = 11, - Minor = 10, -} - -impl Sevenths { - pub fn new(from: Note, to: Note) -> Result { - let interval = to.pitch() - from.pitch(); - match interval { - 12 => Ok(Sevenths::Augmented), - 9 => Ok(Sevenths::Diminished), - 11 => Ok(Sevenths::Major), - 10 => Ok(Sevenths::Minor), - _ => Err("Invalid interval".into()), - } - } - pub fn compute(&self, note: Note) -> Note { - let interval = match self { - Sevenths::Augmented => 12, - Sevenths::Diminished => 9, - Sevenths::Major => 11, - Sevenths::Minor => 10, - }; - let pitch = note.pitch() + interval; - Note::from(pitch) - } -} - -impl std::ops::Add for Sevenths { - type Output = Note; - - fn add(self, rhs: Note) -> Self::Output { - (rhs.pitch() + self as i64).into() - } -} - -impl std::ops::Sub for Sevenths { - type Output = Note; - - fn sub(self, rhs: Note) -> Self::Output { - (rhs.pitch() - self as i64).into() - } -} diff --git a/music/src/intervals/thirds.rs b/music/src/intervals/thirds.rs deleted file mode 100644 index 3b9aa9d..0000000 --- a/music/src/intervals/thirds.rs +++ /dev/null @@ -1,105 +0,0 @@ -/* - Appellation: thirds - Contrib: FL03 - Description: A collection of common musical intervals - A musical third can be either be a difference of three (minor) or four (major) semitones -*/ -use crate::{BoxedError, Gradient, Note}; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, - VariantNames, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum Thirds { - #[default] - Major = 4, - Minor = 3, -} - -impl Thirds { - /// [is_third] compares two notes to see if either a major or minor third interval exists - pub fn is_third(a: Note, b: Note) -> Result { - Self::try_from((a, b)) - } - pub fn compute(note: Note) -> (Note, Note) { - (Self::Major + note, Self::Minor + note) - } - /// Functional method for creating a major third - pub fn major() -> Self { - Self::Major - } - /// Functional method for creating a minor third - pub fn minor() -> Self { - Self::Minor - } -} - -impl TryFrom<(Note, Note)> for Thirds { - type Error = BoxedError; - - fn try_from(data: (Note, Note)) -> Result { - // An interval is the difference in pitch between an two notes - // We take the pitch of the result to account for its modularity; (0, 11) -> 11 but (11, 0) -> 1 - let interval: i64 = (data.1.pitch() - data.0.pitch()).pitch(); - match interval { - 3 => Ok(Self::Minor), - 4 => Ok(Self::Major), - _ => Err("Interval is not a third...".into()), - } - } -} - -impl TryFrom<[Note; 2]> for Thirds { - type Error = BoxedError; - - fn try_from(data: [Note; 2]) -> Result { - Thirds::try_from((data[0], data[1])) - } -} - -impl std::ops::Add for Thirds { - type Output = Note; - - fn add(self, rhs: Note) -> Self::Output { - (rhs.pitch() + self as i64).into() - } -} - -impl std::ops::Sub for Thirds { - type Output = Note; - - fn sub(self, rhs: Note) -> Self::Output { - (rhs.pitch() - self as i64).into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Note; - - #[test] - fn test_thirds() { - assert_eq!(Thirds::Major + Note::from(0), Note::from(4)) - } -} diff --git a/music/src/lib.rs b/music/src/lib.rs deleted file mode 100644 index d81ca70..0000000 --- a/music/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* - Appellation: music - Contrib: FL03 -*/ -//! # Music -//! -//! -extern crate contained_core as contained; -pub use self::{errors::*, notes::*, primitives::*, specs::*, utils::*}; - -pub mod chords; -pub mod frequency; -pub mod intervals; -pub mod neo; -pub mod score; - -mod errors; -mod notes; -mod primitives; -mod specs; -mod utils; - -pub mod prelude { - pub use super::chords::*; - pub use super::errors::*; - pub use super::frequency::*; - pub use super::intervals::*; - pub use super::neo::{self, *}; - pub use super::notes::*; - pub use super::primitives::*; - pub use super::score::*; - pub use super::specs::*; - pub use super::utils::*; -} diff --git a/music/src/neo/mod.rs b/music/src/neo/mod.rs deleted file mode 100644 index 0e5cc54..0000000 --- a/music/src/neo/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -/* - Appellation: neo - Contrib: FL03 -*/ -//! # Neo -//! -//! This module is dedicated to the neo-Riemannian theory of music and its computational implications. -//! -pub use self::{pathfinder::*, transform::*}; - -mod pathfinder; -mod transform; - -pub mod tonnetz; -pub mod triads; - -#[cfg(test)] -mod tests { - use super::*; - use triads::*; - - #[test] - fn test_pathfinder() { - let triad = Triad::new(0.into(), Triads::Major); - for i in [1, 3, 11] { - let mut pathfinder = PathFinder::new(i.into()).set_origin(triad.clone()); - assert!(pathfinder.find().is_some()); - } - } -} diff --git a/music/src/neo/pathfinder.rs b/music/src/neo/pathfinder.rs deleted file mode 100644 index 08e4a6e..0000000 --- a/music/src/neo/pathfinder.rs +++ /dev/null @@ -1,56 +0,0 @@ -/* - Appellation: pathfinder - Contrib: FL03 -*/ -//! # PathFinder -use super::{Transform, LPR}; -use crate::neo::triads::Triad; -use crate::Note; -use strum::IntoEnumIterator; - -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq, PartialOrd)] -pub struct PathFinder { - queue: Vec<(Vec, Triad)>, - target: Note, -} - -impl PathFinder { - pub fn new(target: Note) -> Self { - Self { - queue: Vec::new(), - target, - } - } - /// Finds the shortest path to the target - pub fn find(&mut self) -> Option> { - while let Some((path, triad)) = self.queue.pop() { - if triad.as_ref().contains(&self.target) { - return Some(path); - } - for i in LPR::iter() { - let mut triad = triad.clone(); - triad.transform(i); - let mut path = path.clone(); - path.push(i); - if triad.as_ref().contains(&self.target) { - return Some(path); - } - self.queue.push((path, triad)); - } - } - None - } - /// Resets the pathfinder - pub fn reset(&mut self) { - self.queue.clear(); - } - /// Sets the origin of the pathfinder - pub fn set_origin(mut self, triad: Triad) -> Self { - self.queue.push((Vec::new(), triad)); - self - } - /// Sets the target of the pathfinder - pub fn set_target(&mut self, target: Note) { - self.target = target; - } -} diff --git a/music/src/neo/tonnetz/cluster.rs b/music/src/neo/tonnetz/cluster.rs deleted file mode 100644 index 735c4f0..0000000 --- a/music/src/neo/tonnetz/cluster.rs +++ /dev/null @@ -1,113 +0,0 @@ -/* - Appellation: cluster - Contrib: FL03 - Description: - The cluster is an undirected, circular graph where each node is a note which is connected to 6 other nodes. - - If a tonnetz is a topological computer, then a cluster is a topological computer that is used to orchestrate a set of topological computers. - Locally, a tonnetz is typically fragemented only persisting as many triads as the host device allows for. However, as a network the cluster - glues together these framents into a single, cohesive, and complete experience orchestrated according to a single originator. - - Each triad persisted is required to maintian a set of invariants that are used to determine the state of the cluster. - If each edge in a traditional tonnetz is the interval between the two notes, than each edge in the cluster describes a type of seed value that encodes some information about the triad. -*/ -//! # Cluster -//! -//! A cluster is a type of tonnetz that is used to orchestrate a set of local or detached triadic machines. -use super::{TonnetzGraph, TonnetzSpec}; -use crate::neo::triads::*; -use crate::prelude::{Interval, Note, LPR}; -use petgraph::{Graph, Undirected}; -use std::sync::{Arc, Mutex}; - -pub enum ClusterEvent { - Applied(LPR), - Registered { triad: Triad }, - Unregistered { triad: Triad }, -} - -pub struct Boundary { - pub id: String, // the id of the triad that is the boundary - pub interval: Interval, -} - -#[derive(Clone, Debug, Default)] -pub struct Cluster { - scope: Arc>, - store: TonnetzGraph, -} - -impl Cluster {} - -impl std::fmt::Display for Cluster { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.store) - } -} - -impl TonnetzSpec for Cluster { - fn scope(&self) -> Triad { - self.scope.lock().unwrap().clone() - } - - fn store(&self) -> &TonnetzGraph { - &self.store - } - - fn store_mut(&mut self) -> &mut TonnetzGraph { - &mut self.store - } -} - -impl AsRef for Cluster { - fn as_ref(&self) -> &TonnetzGraph { - &self.store - } -} - -impl AsMut for Cluster { - fn as_mut(&mut self) -> &mut TonnetzGraph { - &mut self.store - } -} - -impl From for Cluster { - fn from(triad: Triad) -> Self { - let (rt, tf, rf): (Interval, Interval, Interval) = triad.clone().intervals(); - let mut cluster = Graph::::new_undirected(); - - let r = cluster.add_node(triad.root()); - let t = cluster.add_node(triad.third()); - let f = cluster.add_node(triad.fifth()); - cluster.extend_with_edges([(r, t, rt), (t, f, tf), (r, f, rf)]); - Self { - store: cluster, - scope: Arc::new(Mutex::new(triad)), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::MODULUS; - - #[test] - fn test_cluster() { - let triad = Triad::new(0.into(), Triads::Major); - - let mut cluster = Cluster::from(triad); - assert!(!cluster.fulfilled()); - for i in 1..MODULUS { - cluster.insert(Triad::new(i.into(), Triads::Major)); - } - eprintln!("{:?}", cluster.store()); - assert!(cluster.fulfilled()); - for class in [Triads::Minor, Triads::Augmented, Triads::Diminished] { - for i in 0..MODULUS { - cluster.insert(Triad::new(i.into(), class)); - } - } - assert!(cluster.fulfilled()); - } -} diff --git a/music/src/neo/tonnetz/mod.rs b/music/src/neo/tonnetz/mod.rs deleted file mode 100644 index a5e388e..0000000 --- a/music/src/neo/tonnetz/mod.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - Appellation: tonnetz - Contrib: FL03 - Description: - A tonnetz can be any set of connected, non-repeating triads. The tonnetz is essentially a topological computer created by gluing together several triadic machines together. - The tonnetz is an undirected, circular graph where each node is a note which is connected to 6 other nodes. - - To find the six related notes, one simply must find the thirds and perfect fifth that lie above and below the given node. For example, - Note(C) -> ((3, -3), (4, -4), (7, -7)) - (Minor Third) +/- 3 -> (D# / Eb, A) - (Major Third) +/- 4 -> (E, G# / Ab) - (Perfect Fifth) +/- 7 -> (G, F) -*/ -//! # Tonnetz -//! -//! A tonnetz is a type of topological computer that is used to orchestrate a set of local or detached triadic machines. -//! A single tonnetz is considered to be any set of connected, non-repeating traids. Repeating triadic structures are -//! used to create additional layers and can be used to enact more complex workloads. -pub use self::cluster::*; - -mod cluster; - -use crate::neo::triads::*; -use crate::{intervals::Interval, Note, MODULUS}; -// use decanter::prelude::{Hashable, Iter, H256}; -use petgraph::graph::{DefaultIx, NodeIndex}; -use petgraph::{Graph, Undirected}; - -pub type TonnetzGraph = Graph; - -// pub trait Link: Hashable { -// /// [Link::bridge] is used to synchronize the activties of two different triads; required to seperated by a single LPR transformation -// fn bridge(&self, with: impl Hashable) -> H256 { -// let mut iter = Iter::new(); -// iter.extend(vec![self.hash(), with.hash()]); -// iter.hash() -// } -// fn interval(&self) -> Interval; -// } - -pub trait TonnetzSpec { - const N: usize = MODULUS as usize; - - fn add_node(&mut self, note: Note) -> NodeIndex { - // Check if the node already exists - if let Some(index) = self - .store() - .node_indices() - .find(|&i| self.store()[i] == note) - { - return index; - } - - // Node doesn't exist, add it and return the new NodeIndex - self.store_mut().add_node(note) - // self.tonnetz_mut().add_node(note) - } - fn fulfilled(&self) -> bool { - self.store().node_count() == Self::N - } - fn insert(&mut self, triad: Triad) { - use ChordFactor::*; - // determine the intervals used to create the given triad - let (a, b, c): (Interval, Interval, Interval) = triad.clone().intervals(); - - let r = self.add_node(triad[Root]); - let t = self.add_node(triad[Third]); - let f = self.add_node(triad[Fifth]); - self.store_mut() - .extend_with_edges([(r, t, a), (t, f, b), (r, f, c)]); - } - fn scope(&self) -> Triad; - fn store(&self) -> &TonnetzGraph; - fn store_mut(&mut self) -> &mut TonnetzGraph; -} diff --git a/music/src/neo/transform/lpr.rs b/music/src/neo/transform/lpr.rs deleted file mode 100644 index 3724693..0000000 --- a/music/src/neo/transform/lpr.rs +++ /dev/null @@ -1,135 +0,0 @@ -/* - Appellation: transform - Contrib: FL03 - Description: - - Shift by a semitone : +/- 1 - Shift by a tone: +/- 2 - - number of elements + freq -*/ -//! (L)eading, (P)arallel, and (R)elative -//! -//! The three primary means of transforming a given triad. Each transformation preserves two of the original notes, only shifting one. -//! These transformations are invertible, meaning that any transformation can be undone by applying the same transformation again. -//! The property of enharmonics allows us to apply the transformations according to a notes assigned position, which is a modulus of 12. -//! - -use super::Dirac; -use crate::intervals::{Interval, Thirds}; -use crate::neo::triads::{ChordFactor, Triad}; -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, VariantNames}; - -/// [LPR::L] Preserves the minor third; shifts the remaining note by a semitone -/// [LPR::P] Preserves the perfect fifth; shifts the remaining note by a semitone -/// [LPR::R] preserves the major third in the triad and moves the remaining note by whole tone. - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Default, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - VariantNames, -)] -#[strum(serialize_all = "lowercase")] -pub enum LPR { - #[default] - #[strum(serialize = "l", serialize = "leading")] - L = 0, - #[strum(serialize = "p", serialize = "parallel")] - P = 1, - #[strum(serialize = "r", serialize = "relative")] - R = 2, -} - -impl LPR { - pub fn leading() -> Self { - Self::L - } - pub fn parallel() -> Self { - Self::P - } - pub fn relative() -> Self { - Self::R - } -} - -impl Dirac for LPR { - type Output = Triad; - - fn apply(&self, triad: &mut Triad) -> Self::Output { - use ChordFactor::*; - use Interval::{Semitone, Tone}; - - let (rt, _tf, _rf) = triad.clone().class().intervals(); - match rt { - Thirds::Major => match *self { - LPR::L => triad[Root] -= Semitone, - LPR::P => triad[Third] -= Semitone, - LPR::R => triad[Fifth] += Tone, - }, - Thirds::Minor => match *self { - LPR::L => triad[Fifth] += Semitone, - LPR::P => triad[Third] += Semitone, - LPR::R => triad[Root] -= Tone, - }, - }; - - triad.update().expect("Invalid triad") - } -} - -impl std::ops::Mul for LPR { - type Output = Triad; - - fn mul(self, rhs: Triad) -> Self::Output { - self.apply(&mut rhs.clone()) - } -} - -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use super::*; - use crate::neo::triads::*; - - #[test] - fn test_lpr() { - assert_eq!(LPR::from_str("l"), LPR::from_str("leading")); - } - - #[test] - fn test_leading() { - let triad = Triad::default(); - assert_eq!(triad.clone(), LPR::L * (LPR::L * triad.clone())); - assert_ne!(triad.clone(), LPR::L * triad); - } - - #[test] - fn test_parallel() { - let triad = Triad::default(); - assert_eq!(triad.clone(), LPR::P * (LPR::P * triad.clone())); - assert_ne!(triad.clone(), LPR::P * triad); - } - - #[test] - fn test_relative() { - let triad = Triad::default(); - assert_eq!(triad.clone(), LPR::R * (LPR::R * triad.clone())); - assert_ne!(triad.clone(), LPR::R * triad); - } -} diff --git a/music/src/neo/transform/mod.rs b/music/src/neo/transform/mod.rs deleted file mode 100644 index b0740db..0000000 --- a/music/src/neo/transform/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -/* - Appellation: transform - Contrib: FL03 - Description: ... Summary ... -*/ - -pub use self::{lpr::LPR, transformer::*}; - -mod lpr; -mod transformer; - -/// [Dirac] is a trait used to describe a transformative function; -/// Often, this trait is used to describe a set of functions that are used to transform one object into another of the same type. -pub trait Dirac { - type Output; - /// The function that transforms the object - fn apply(&self, to: &mut T) -> Self::Output; -} - -/// [Transform] is a trait used to describe a type that can be transformed by a [Dirac] function. -pub trait Transform: Sized { - type Dirac: Dirac; - - fn transform(&mut self, dirac: Self::Dirac) -> Self { - dirac.apply(self) - } -} diff --git a/music/src/neo/transform/transformer.rs b/music/src/neo/transform/transformer.rs deleted file mode 100644 index ed6ed1f..0000000 --- a/music/src/neo/transform/transformer.rs +++ /dev/null @@ -1,173 +0,0 @@ -/* - Appellation: transformer - Contrib: FL03 - Description: A transformer is designed to be an asynchronous iterator that applies a series of transformations to a given triad. -*/ -use super::{Transform, LPR}; -use crate::neo::triads::*; -use futures::Stream; -use itertools::Itertools; -use std::future::Future; -use std::task::{self, Poll}; - -#[derive(Clone, Debug, Default)] -pub struct Transformer { - index: usize, - iter: Vec, - scope: Triad, -} - -impl Transformer { - pub fn new(scope: Triad) -> Self { - Self { - index: 0, - iter: Vec::new(), - scope, - } - } - pub fn push(&mut self, lpr: LPR) { - self.iter.push(lpr); - } - pub fn with(mut self, iter: impl IntoIterator) -> Self { - self.iter = iter.into_iter().collect_vec(); - self - } -} - -impl Extend for Transformer { - fn extend>(&mut self, iter: T) { - self.iter.extend(iter); - } -} - -impl ExactSizeIterator for Transformer { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl Iterator for Transformer { - type Item = Triad; - - fn next(&mut self) -> Option { - if let Some(cur) = self.iter.get(self.index) { - // Increment the index - self.index += 1; - // Transform the scope - self.scope.transform(*cur); - // Return the scope - Some(self.scope.clone()) - } else { - None - } - } -} - -impl Future for Transformer { - type Output = Triad; - - fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { - cx.waker().wake_by_ref(); - if let None = self.next() { - return Poll::Ready(self.scope.clone()); - } - return Poll::Pending; - } -} - -impl Stream for Transformer { - type Item = Triad; - - fn poll_next( - mut self: std::pin::Pin<&mut Self>, - cx: &mut task::Context<'_>, - ) -> Poll> { - cx.waker().wake_by_ref(); - if let Some(cur) = self.next() { - Poll::Ready(Some(cur)) - } else { - Poll::Ready(None) - } - } -} - -impl Unpin for Transformer {} - -impl std::ops::Index for Transformer { - type Output = LPR; - - fn index(&self, index: usize) -> &Self::Output { - &self.iter[index] - } -} - -impl std::ops::IndexMut for Transformer { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.iter[index] - } -} - -impl From for Transformer { - fn from(scope: Triad) -> Self { - Self { - index: 0, - iter: Vec::new(), - scope, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use lazy_static::lazy_static; - use strum::IntoEnumIterator; - use LPR::*; - - lazy_static! { - static ref _EXPECTED: (Triad, Vec) = { - let mut triad = Triad::default(); - let prev = triad.walk_across([L, P, R]); - (triad, prev) - }; - } - - #[test] - fn test_transformer() { - let triad = Triad::default(); - let transformer = Transformer::new(triad).with(LPR::iter()); - let (expected, walked) = _EXPECTED.clone(); - for (i, t) in transformer.enumerate() { - if i >= walked.len() { - assert_eq!(t, expected); - } else { - assert_eq!(t, walked[i]); - } - } - } - - #[tokio::test] - async fn test_transformer_future() { - let triad = Triad::default(); - let (expected, _walked) = _EXPECTED.clone(); - let transformer = Transformer::new(triad).with(LPR::iter()); - assert_eq!(transformer.await, expected); - } - - #[tokio::test] - async fn test_stream_transformer() { - use futures::{stream, StreamExt}; - let triad = Triad::default(); - let (expected, walked) = _EXPECTED.clone(); - let transformer = Transformer::new(triad).with(LPR::iter()); - let s = stream::iter(transformer); - let res = s.collect::>().await; - for (i, triad) in res.clone().into_iter().enumerate() { - if i >= walked.len() { - assert_eq!(triad, expected); - } else { - assert_eq!(triad, walked[i]); - } - } - } -} diff --git a/music/src/neo/triads/builder.rs b/music/src/neo/triads/builder.rs deleted file mode 100644 index 8ab9405..0000000 --- a/music/src/neo/triads/builder.rs +++ /dev/null @@ -1,16 +0,0 @@ -/* - Appellation: builder - Contrib: FL03 -*/ -//! Triad Builder -//! - -use super::{Triad, Triads}; -use crate::prelude::{Interval, Note}; - -/// [TriadBuilder] is a simple struct that allows for the construction of a [Triad] from a [Note] and an [Interval]. -pub struct TriadBuilder { - notes: [Note; 3], - root: Note, - interval: Interval, -} diff --git a/music/src/neo/triads/class.rs b/music/src/neo/triads/class.rs deleted file mode 100644 index 14881ed..0000000 --- a/music/src/neo/triads/class.rs +++ /dev/null @@ -1,172 +0,0 @@ -/* - Appellation: class - Contrib: FL03 -*/ -use super::Triad; -use crate::intervals::{Fifths, Interval, Thirds}; -use crate::{BoxedError, Note}; -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, IntoEnumIterator, VariantNames}; - -/// [Triads::Augmented] is a [Triad] created with [Thirds::Major], [Thirds::Major] intervals -/// [Triads::Diminished] is a [Triad] created with [Thirds::Minor], [Thirds::Minor] intervals -/// [Triads::Major] is a [Triad] created with [Thirds::Major], [Thirds::Minor] intervals -/// [Triads::Minor] is a [Triad] created with [Thirds::Minor], [Thirds::Major] intervals -#[derive( - Clone, - Copy, - Debug, - Default, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - VariantNames, -)] -#[repr(u8)] -#[strum(serialize_all = "lowercase")] -pub enum Triads { - Augmented = 0, - Diminished = 1, - #[default] - Major = 2, - Minor = 3, -} - -impl Triads { - pub fn classes() -> Vec { - Self::iter().collect() - } - pub fn others(&self) -> Vec { - Self::iter().filter(|x| x != self).collect() - } - pub fn intervals(&self) -> (Thirds, Thirds, Fifths) { - use Triads::*; - match self { - Augmented => (Thirds::Major, Thirds::Major, Fifths::Augmented), - Diminished => (Thirds::Minor, Thirds::Minor, Fifths::Diminished), - Major => (Thirds::Major, Thirds::Minor, Fifths::Perfect), - Minor => (Thirds::Minor, Thirds::Major, Fifths::Perfect), - } - } -} - -impl From<(Thirds, Thirds)> for Triads { - fn from(intervals: (Thirds, Thirds)) -> Triads { - match intervals.0 { - Thirds::Major => match intervals.1 { - Thirds::Major => Triads::Augmented, - Thirds::Minor => Triads::Major, - }, - Thirds::Minor => match intervals.1 { - Thirds::Major => Triads::Minor, - Thirds::Minor => Triads::Diminished, - }, - } - } -} - -impl TryFrom for Triads { - type Error = BoxedError; - - fn try_from(data: Triad) -> Result { - let triad: (Note, Note, Note) = data.into(); - Self::try_from(triad) - } -} - -impl TryFrom<(Thirds, Fifths)> for Triads { - type Error = BoxedError; - - fn try_from(intervals: (Thirds, Fifths)) -> Result { - match intervals.0 { - Thirds::Major => match intervals.1 { - Fifths::Augmented => Ok(Triads::Augmented), - Fifths::Diminished => { - Err("Cannot create a triad with a major third and a diminished fifth".into()) - } - Fifths::Perfect => Ok(Triads::Major), - }, - Thirds::Minor => match intervals.1 { - Fifths::Augmented => Err( - "Cannot create an augmented triad with a minor third and an augmented fifth" - .into(), - ), - Fifths::Diminished => Ok(Triads::Diminished), - Fifths::Perfect => Ok(Triads::Minor), - }, - } - } -} - -impl TryFrom<[Note; 3]> for Triads { - type Error = BoxedError; - - fn try_from(data: [Note; 3]) -> Result { - let [r, t, f]: [Note; 3] = data; - let ab = Thirds::try_from((r, t))?; - let bc = Fifths::try_from((r, f))?; - - match ab { - Thirds::Major => match bc { - Fifths::Augmented => Ok(Self::Augmented), - Fifths::Perfect => Ok(Self::Major), - _ => Err("".into()), - }, - Thirds::Minor => match bc { - Fifths::Diminished => Ok(Self::Diminished), - Fifths::Perfect => Ok(Self::Minor), - _ => Err("".into()), - }, - } - } -} - -impl TryFrom<(Note, Note, Note)> for Triads { - type Error = BoxedError; - - fn try_from(data: (Note, Note, Note)) -> Result { - let (r, t, f): (Note, Note, Note) = (data.0, data.1, data.2); - let ab = Thirds::try_from((r, t))?; - let bc = Fifths::try_from((r, f))?; - - match ab { - Thirds::Major => match bc { - Fifths::Augmented => Ok(Self::Augmented), - Fifths::Perfect => Ok(Self::Major), - _ => Err("".into()), - }, - Thirds::Minor => match bc { - Fifths::Diminished => Ok(Self::Diminished), - Fifths::Perfect => Ok(Self::Minor), - _ => Err("".into()), - }, - } - } -} - -impl From for (Interval, Interval, Interval) { - fn from(class: Triads) -> (Interval, Interval, Interval) { - let intervals: (Thirds, Thirds, Fifths) = class.into(); - (intervals.0.into(), intervals.1.into(), intervals.2.into()) - } -} - -impl From for (Thirds, Thirds, Fifths) { - fn from(class: Triads) -> (Thirds, Thirds, Fifths) { - match class { - Triads::Augmented => (Thirds::Major, Thirds::Major, Fifths::Augmented), - Triads::Diminished => (Thirds::Minor, Thirds::Minor, Fifths::Diminished), - Triads::Major => (Thirds::Major, Thirds::Minor, Fifths::Perfect), - Triads::Minor => (Thirds::Minor, Thirds::Major, Fifths::Perfect), - } - } -} diff --git a/music/src/neo/triads/context.rs b/music/src/neo/triads/context.rs deleted file mode 100644 index b1fe688..0000000 --- a/music/src/neo/triads/context.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - Appellation: context - Contrib: FL03 -*/ -use super::Triad; -use contained::prelude::State; - -/// [Triadic] is a trait describing the contextual requirements of a [Triad]. -pub trait Triadic: Send + Sync { - type Store; - - fn state(&self) -> State; - fn store(&self) -> Self::Store; - fn triad(&self) -> Triad; -} - -pub struct TriadContext { - state: State, - store: T, - triad: Triad, -} - -impl TriadContext { - pub fn new(state: State, store: T, triad: Triad) -> Self { - Self { - state, - store, - triad, - } - } -} diff --git a/music/src/neo/triads/factors.rs b/music/src/neo/triads/factors.rs deleted file mode 100644 index bf713ec..0000000 --- a/music/src/neo/triads/factors.rs +++ /dev/null @@ -1,118 +0,0 @@ -/* - Appellation: misc - Contrib: FL03 - Description: Each triad is composed of three notes, called chord factors: root, third, and fifth; these are used as a means of indexing any given triad -*/ -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumCount, EnumIs, EnumIter, EnumString, IntoEnumIterator, VariantNames}; - -/// A [ChordFactor] is used as an indexer for a [super::Triad] -/// [ChordFactor::Root] is the first note, [ChordFactor::Third] is the second note, and [ChordFactor::Fifth] is the third note in a [super::Triad] - -#[derive( - Clone, - Copy, - Debug, - Default, - Deserialize, - Display, - EnumCount, - EnumIs, - EnumIter, - EnumString, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - VariantNames, -)] -#[repr(usize)] -#[strum(serialize_all = "lowercase")] -pub enum ChordFactor { - #[default] - #[strum(serialize = "r", serialize = "root")] - Root = 0, - #[strum(serialize = "t", serialize = "third")] - Third = 1, - #[strum(serialize = "f", serialize = "fifth")] - Fifth = 2, -} - -impl ChordFactor { - pub fn fifth() -> Self { - ChordFactor::Fifth - } - pub fn root() -> Self { - ChordFactor::Root - } - pub fn third() -> Self { - ChordFactor::Third - } - pub fn factors() -> Vec { - Self::iter().collect() - } - pub fn others(&self) -> Vec { - Self::iter().filter(|x| x != self).collect() - } -} - -impl From for ChordFactor { - fn from(x: usize) -> Self { - match x % 3 { - 0 => ChordFactor::Root, - 1 => ChordFactor::Third, - _ => ChordFactor::Fifth, - } - } -} - -impl From for usize { - fn from(x: ChordFactor) -> Self { - x as usize - } -} - -unsafe impl petgraph::graph::IndexType for ChordFactor { - fn new(x: usize) -> Self { - Self::from(x) - } - - fn index(&self) -> usize { - *self as usize - } - - fn max() -> Self { - Self::Fifth - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::neo::triads::{Triad, Triads}; - - #[test] - fn chord_factors() { - use ChordFactor::*; - - let triad = Triad::new(0.into(), Triads::Major); - assert_eq!(triad[Root], 0.into()); - assert_eq!(triad[Third], 4.into()); - assert_eq!(triad[Fifth], 7.into()); - assert_eq!(triad[Third..Fifth], vec![4.into()]); - assert_eq!(triad[Root..Fifth], vec![0.into(), 4.into()]); - } - - #[test] - fn chord_factors_iter() { - use ChordFactor::*; - - let factors = ChordFactor::factors(); - assert_eq!(factors.len(), 3); - assert_eq!(factors[0], Root); - assert_eq!(factors[1], Third); - assert_eq!(factors[2], Fifth); - } -} diff --git a/music/src/neo/triads/mod.rs b/music/src/neo/triads/mod.rs deleted file mode 100644 index d7d7e85..0000000 --- a/music/src/neo/triads/mod.rs +++ /dev/null @@ -1,101 +0,0 @@ -/* - Appellation: triads - Contrib: FL03 -*/ -//! Triad -//! -//! def. A triad is a set of three notes, called chord factors -//! -//! # Capabilities -//! -//! Justification for considering triads to be viable topological computing environment is found with the [Wolfram (2, 3) UTM](https://www.wolframscience.com/prizes/tm23). -//! Generally, a universal turing machine is capable of emulating any other turing machine. The (2, 3) UTM is a turing machine that can emulate any other turing machine using only two states and three symbols. -//! Considering a triad to be a three-tuple (a, b, c) where the intervals [a, b] and [b, c] are both thirds, we can see that the triad is a (2, 3) UTM where each side or "state" is consistently allowed to be either -//! invalid or valid. -//! -pub use self::{builder::*, class::*, context::*, factors::*, triad::*}; - -mod builder; -mod class; -mod context; -mod factors; -mod triad; - -/// [FromTriad] is a simple trait that allows for the explicit conversion of a [Triad] into any type that implements [From]. -pub trait FromTriad { - fn from_triad(triad: Triad) -> Self; -} - -impl FromTriad for T -where - T: From, -{ - fn from_triad(triad: Triad) -> Self { - Self::from(triad) - } -} - -/// [IntoTriad] is a simple trait that allows for the explicit conversion of any type that implements [Into] into a [Triad]. -pub trait IntoTriad { - fn into_triad(self) -> Triad; -} - -impl IntoTriad for T -where - T: Into, -{ - fn into_triad(self) -> Triad { - self.into() - } -} - -/// [TryIntoTriad] is a trait for explicitly attempting to convert any type into a [Triad]. -pub trait TryIntoTriad { - type Error; - - fn try_into_triad(self) -> Result; -} - -impl TryIntoTriad for T -where - T: TryInto, - T::Error: std::error::Error, -{ - type Error = T::Error; - - fn try_into_triad(self) -> Result { - self.try_into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::neo::LPR; - use strum::IntoEnumIterator; - - #[test] - fn test_triad() { - let triad = Triad::new(0.into(), Triads::Major); - assert_eq!(triad.as_ref(), &[0.into(), 4.into(), 7.into()]); - let b = Triad::try_from((11, 4, 7)); - assert!(b.is_ok()); - assert_ne!(&triad, &b.unwrap()) - } - - #[test] - fn test_walking() { - let expected = Triad::try_from([1, 4, 8]).unwrap(); - let triad = Triad::new(0.into(), Triads::Major); - - let mut a = triad.clone(); - let mut b = triad; - // Apply three consecutive transformations to the scope - a.walk(LPR::iter()); - assert_eq!(a.clone(), expected); - // Apply the same transformations in reverse to go back to the original - a.walk(LPR::iter()); - b.yoyo(LPR::iter()); - assert_eq!(a, b); - } -} diff --git a/music/src/neo/triads/triad.rs b/music/src/neo/triads/triad.rs deleted file mode 100644 index 22041b5..0000000 --- a/music/src/neo/triads/triad.rs +++ /dev/null @@ -1,333 +0,0 @@ -/* - Appellation: triad - Contrib: FL03 -*/ -//! Triad -//! -//! A [Triad] is a set of three [Note]s called chord factors ([ChordFactor]) that are related by a specific interval; represented here with a [Triads] classification. -//! [Triad]s are also considered to be stateful and can be transformed into other [Triad]s with the use of [LPR] transformations. -//! In music theory, the [Triad] is a fundamental building block used to construct more complex chords. -//! Similarly, the [Triad] is used to describe an abstract topological unit-computing environment that is often used in conjuction with other persistent instances to aid in the completion of a given task. -//! The [Wolfram (2, 3) UTM](https://www.wolframscience.com/prizes/tm23) is used as justification for describing the [Triad] as a topological unit-computing environment. -use super::{ChordFactor, Triads}; -use crate::neo::{Dirac, PathFinder, Transform, LPR}; -use crate::prelude::{Fifths, Gradient, Interval, MusicError, Note, Thirds}; -use contained::states::State; -// use decanter::prelude::Hashable; -use futures::Future; -use itertools::Itertools; -use petgraph::{Graph, Undirected}; -use serde::{Deserialize, Serialize}; -use std::ops::{Index, IndexMut, Range}; -use std::task::{self, Poll}; -use strum::IntoEnumIterator; - -fn constructor(data: &[Note; 3]) -> Result { - for (a, b, c) in data.iter().circular_tuple_windows() { - if let Ok(class) = Triads::try_from((*a, *b, *c)) { - return Ok(Triad::new(*a, class)); - } - } - Err(MusicError::IntervalError( - "Failed to find the required relationships within the given notes...".into(), - )) -} - -#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Triad { - class: Triads, - notes: [Note; 3], - state: State, -} - -impl Triad { - pub fn new(root: Note, class: Triads) -> Self { - let (a, _, c): (Thirds, Thirds, Fifths) = class.into(); - Self { - class, - notes: [root, a + root, c + root], - state: State::default(), - } - } - pub fn from_notes(notes: [Note; 3]) -> Self { - if let Ok(triad) = constructor(¬es) { - return triad; - } - Self { - class: Triads::default(), - notes, - state: State::Invalid, - } - } - /// Build a new [Triad] from a given [Notable] root and two [Thirds] - pub fn build(root: Note, a: Thirds, b: Thirds) -> Self { - Self::new(root, Triads::from((a, b))) - } - /// Returns the [Triads] classification of the [Triad] - pub fn class(&self) -> Triads { - self.class - } - /// Returns true if the [Triad] contains the [Note] - pub fn contains(&self, note: &Note) -> bool { - self.clone().into_iter().contains(note) - } - /// Endlessly applies the described transformations to the [Triad] - pub fn cycle(&mut self, iter: impl IntoIterator) { - for i in Vec::from_iter(iter).iter().cycle() { - self.transform(*i); - } - } - /// Set the initial state of the [Triad] - pub fn default_state(mut self, state: State) -> Self { - self.state = state; - self - } - /// Returns a reference to the current composition of the [Triad] - pub fn factors(&self) -> &[Note; 3] { - &self.notes - } - /// Returns an cloned instance of the note occupying the fifth - pub fn fifth(&self) -> Note { - self[ChordFactor::Fifth] - } - /// Classifies the [Triad] by describing the intervals that connect the notes - pub fn intervals(&self) -> (Interval, Interval, Interval) { - let (rt, tf, rf) = self.class.intervals(); - (rt.into(), tf.into(), rf.into()) - } - /// Returns a [Vec] of all neighboring [Triad]s; the [Triad]s that are one [LPR] away from the current [Triad] - pub fn neighbors(&self) -> Vec { - let mut neighbors = Vec::with_capacity(3); - for i in LPR::iter() { - let mut triad = self.clone(); - triad.transform(i); - neighbors.push(triad); - } - neighbors - } - /// Returns a [PathFinder] that can be used to find the path between the [Triad] and the [Note] - pub fn pathfinder(&self, note: Note) -> PathFinder { - PathFinder::new(note).set_origin(self.clone()) - } - /// Returns an cloned instance of the root of the triad - pub fn root(&self) -> Note { - self[ChordFactor::Root] - } - /// Returns the [State] of the [Triad] - pub fn state(&self) -> State { - self.state - } - /// Sets the current [State] of the [Triad] - pub fn set_state(&mut self, state: State) { - self.state = state; - } - /// Returns an cloned instance of the note occupying the third - pub fn third(&self) -> Note { - self[ChordFactor::Third] - } - /// After applying the transformation, the [Triad] is updated - pub fn update(&mut self) -> Result { - if let Ok(t) = constructor(self.as_ref()) { - *self = t; - return Ok(self.clone()); - } - self.state.invalidate(); - Err(MusicError::IntervalError( - "The given notes failed to contain the necessary relationships...".into(), - )) - } - /// Applies multiple [LPR] transformations onto the scoped [Triad] - /// The goal here is to allow the machine to work on and in the scope - pub fn walk(&mut self, iter: impl IntoIterator) { - for dirac in iter { - self.transform(dirac); - } - } - /// Applies multiple [LPR] transformations onto the scoped [Triad] and returns a vector all the previous [Triad] - pub fn walk_across(&mut self, iter: impl IntoIterator) -> Vec { - iter.into_iter().map(|i| self.transform(i)).collect() - } - /// Applies a set of [LPR] transformations from left-to-right, then returns home applying the same transformations in reverse - pub fn yoyo(&mut self, iter: impl Clone + IntoIterator) { - self.walk(iter.clone()); - let mut args = Vec::from_iter(iter); - args.reverse(); - self.walk(args); - } -} - -impl AsMut<[Note; 3]> for Triad { - fn as_mut(&mut self) -> &mut [Note; 3] { - &mut self.notes - } -} - -impl AsRef<[Note; 3]> for Triad { - fn as_ref(&self) -> &[Note; 3] { - &self.notes - } -} - -impl Default for Triad { - fn default() -> Self { - Self::new(0.into(), Triads::Major) - } -} - -impl std::fmt::Display for Triad { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}.{}.{}", self.root(), self.third(), self.fifth()) - } -} - -impl Future for Triad { - type Output = Self; - - fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { - cx.waker().wake_by_ref(); - - if self.state.is_valid() { - if let Ok(t) = self.update() { - return Poll::Ready(t); - } - } - Poll::Pending - } -} - -impl Index for Triad { - type Output = Note; - - fn index(&self, index: ChordFactor) -> &Self::Output { - &self.notes[index as usize] - } -} - -impl IndexMut for Triad { - fn index_mut(&mut self, index: ChordFactor) -> &mut Self::Output { - &mut self.notes[index as usize] - } -} - -impl Index> for Triad { - type Output = [Note]; - - fn index(&self, index: Range) -> &Self::Output { - &self.notes[index.start as usize..index.end as usize] - } -} - -impl IndexMut> for Triad { - fn index_mut(&mut self, index: Range) -> &mut Self::Output { - &mut self.notes[index.start as usize..index.end as usize] - } -} - -impl IntoIterator for Triad { - type Item = Note; - - type IntoIter = std::array::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.notes.into_iter() - } -} - -impl Transform for Triad { - type Dirac = LPR; -} - -impl Unpin for Triad {} - - -impl std::ops::Mul for Triad { - type Output = Triad; - - fn mul(self, rhs: LPR) -> Self::Output { - rhs.apply(&mut self.clone()) - } -} - -impl std::ops::MulAssign for Triad { - fn mul_assign(&mut self, rhs: LPR) { - *self = self.transform(rhs); - } -} - -impl From<(Note, Thirds, Thirds)> for Triad { - fn from(data: (Note, Thirds, Thirds)) -> Self { - Self::build(data.0, data.1, data.2) - } -} - -impl TryFrom<[Note; 3]> for Triad { - type Error = MusicError; - - fn try_from(data: [Note; 3]) -> Result { - constructor(&data) - } -} - -impl TryFrom<(Note, Note, Note)> for Triad { - type Error = MusicError; - - fn try_from(data: (Note, Note, Note)) -> Result { - Triad::try_from([data.0, data.1, data.2]) - } -} - -impl TryFrom<[i64; 3]> for Triad { - type Error = MusicError; - - fn try_from(notes: [i64; 3]) -> Result { - let notes: [Note; 3] = [notes[0].into(), notes[1].into(), notes[2].into()]; - Triad::try_from(notes) - } -} - -impl TryFrom<(i64, i64, i64)> for Triad { - type Error = MusicError; - - fn try_from(data: (i64, i64, i64)) -> Result { - Triad::try_from([data.0, data.1, data.2]) - } -} - -impl From for Graph { - fn from(d: Triad) -> Graph { - let (rt, tf, rf): (Interval, Interval, Interval) = d.intervals(); - - let mut graph = Graph::with_capacity(3, 3); - let r = graph.add_node(d.root()); - let t = graph.add_node(d.third()); - let f = graph.add_node(d.fifth()); - graph.add_edge(r, t, rt); - graph.add_edge(t, f, tf); - graph.add_edge(r, f, rf); - graph.clone() - } -} - -impl From for (Note, Note, Note) { - fn from(d: Triad) -> (Note, Note, Note) { - (d.root(), d.third(), d.fifth()) - } -} - -impl From for (i64, i64, i64) { - fn from(d: Triad) -> (i64, i64, i64) { - (d.root().pitch(), d.third().pitch(), d.fifth().pitch()) - } -} - -impl From for (Thirds, Thirds, Fifths) { - fn from(data: Triad) -> (Thirds, Thirds, Fifths) { - data.class().intervals() - } -} - -impl From for (Interval, Interval, Interval) { - fn from(data: Triad) -> (Interval, Interval, Interval) { - data.intervals() - } -} diff --git a/music/src/notes/aspn.rs b/music/src/notes/aspn.rs deleted file mode 100644 index 2b4f458..0000000 --- a/music/src/notes/aspn.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* - Appellation: note - Contrib: FL03 - Description: aspn is short for american scientific pitch notation; it is a specific type of note which denotes a certain octave for the given pitch-class -*/ -use super::PitchClass; -use crate::{intervals::Interval, Gradient, MODULUS}; -use serde::{Deserialize, Serialize}; - -/// [ASPN] is a specific type of [Note] which denotes a certain octave for the given pitch-class -#[derive( - Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct ASPN { - class: PitchClass, - octave: i64, -} - -impl ASPN { - pub fn new(class: PitchClass, octave: Option) -> Self { - Self { - class, - octave: octave.unwrap_or(1), - } - } -} - -impl std::fmt::Display for ASPN { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}.{}", self.class, self.octave) - } -} - -impl Gradient for ASPN { - const MODULUS: i64 = MODULUS; - - fn class(&self) -> PitchClass { - self.class - } - fn pitch(&self) -> i64 { - self.class.into() - } -} - -impl std::ops::AddAssign for ASPN { - fn add_assign(&mut self, rhs: Interval) { - // self.octave += interval / Self::MODULUS; - self.class += rhs; - } -} - -impl std::ops::SubAssign for ASPN { - fn sub_assign(&mut self, rhs: Interval) { - // self.octave += interval / Self::MODULUS; - self.class -= rhs; - } -} - -impl From for i64 { - fn from(data: ASPN) -> i64 { - data.pitch() - } -} diff --git a/music/src/notes/classes/accidentals.rs b/music/src/notes/classes/accidentals.rs deleted file mode 100644 index f2891c5..0000000 --- a/music/src/notes/classes/accidentals.rs +++ /dev/null @@ -1,206 +0,0 @@ -/* - Appellation: accidentals - Contrib: FL03 - Description: - Accidental notes are either sharp or flat -*/ -use super::Naturals; -use crate::Pitch; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumIter, EnumString, EnumVariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumIter, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, -)] -#[strum(serialize_all = "snake_case")] -pub enum Accidentals { - Flat(FlatNote), - #[default] - Sharp(SharpNote), -} - -impl From for i64 { - fn from(note: Accidentals) -> i64 { - match note { - Accidentals::Sharp(s) => s.into(), - Accidentals::Flat(f) => f.into(), - } - } -} - -impl TryFrom for Accidentals { - type Error = Box; - - fn try_from(data: Pitch) -> Result { - if Naturals::try_from(data).is_err() { - let note = if data.pitch() >= 0 { - Accidentals::Sharp(SharpNote::try_from(data)?) - } else { - Accidentals::Flat(FlatNote::try_from(data)?) - }; - return Ok(note); - } - Err("Provided note is natural".into()) - } -} - -impl TryFrom for Accidentals { - type Error = Box; - - fn try_from(data: i64) -> Result { - Accidentals::try_from(Pitch::new(data)) - } -} - -impl From for Pitch { - fn from(data: Accidentals) -> Pitch { - let pitch = match data { - Accidentals::Flat(n) => n as i64, - Accidentals::Sharp(n) => n as i64, - }; - Pitch::from(pitch) - } -} - -impl From for Accidentals { - fn from(data: FlatNote) -> Accidentals { - Accidentals::Flat(data) - } -} - -impl From for Accidentals { - fn from(data: SharpNote) -> Accidentals { - Accidentals::Sharp(data) - } -} - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumIter, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum FlatNote { - A = 8, - B = 10, - #[default] - D = 1, - E = 3, - G = 6, -} - -impl From for i64 { - fn from(note: FlatNote) -> i64 { - note as i64 - } -} - -impl TryFrom for FlatNote { - type Error = Box; - - fn try_from(value: i64) -> Result { - let data = value.abs() % 12; - match data { - 1 => Ok(Self::D), - 3 => Ok(Self::E), - 6 => Ok(Self::G), - 8 => Ok(Self::A), - 10 => Ok(Self::B), - _ => Err("".into()), - } - } -} - -impl TryFrom for FlatNote { - type Error = Box; - - fn try_from(value: Pitch) -> Result { - FlatNote::try_from(value.pitch()) - } -} - -#[derive( - Clone, - Copy, - Debug, - Default, - Deserialize, - Display, - EnumIter, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum SharpNote { - A = 10, - #[default] - C = 1, - D = 3, - F = 6, - G = 8, -} - -impl From for i64 { - fn from(note: SharpNote) -> i64 { - note as i64 - } -} - -impl TryFrom for SharpNote { - type Error = Box; - - fn try_from(value: Pitch) -> Result { - SharpNote::try_from(value.pitch()) - } -} - -impl TryFrom for SharpNote { - type Error = Box; - - fn try_from(value: i64) -> Result { - let data = value % 12; - match data { - 1 => Ok(Self::C), - 3 => Ok(Self::D), - 6 => Ok(Self::F), - 8 => Ok(Self::G), - 10 => Ok(Self::A), - _ => Err("".into()), - } - } -} diff --git a/music/src/notes/classes/mod.rs b/music/src/notes/classes/mod.rs deleted file mode 100644 index 03bf2bb..0000000 --- a/music/src/notes/classes/mod.rs +++ /dev/null @@ -1,216 +0,0 @@ -/* - Appellation: classes - Contrib: FL03 - Description: the module is dedicated to implementing the various pitch classes -*/ -pub use self::{accidentals::*, naturals::*}; - -mod accidentals; -mod naturals; - -use crate::{intervals::Interval, Gradient, Pitch}; -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumIter, EnumString, EnumVariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumIter, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, -)] -#[strum(serialize_all = "snake_case")] -pub enum PitchOpt { - #[strum(serialize = "b", serialize = "â™­", serialize = "flat")] - Flat, - #[strum(serialize = "#", serialize = "♯", serialize = "sharp")] - Sharp, - #[default] - #[strum(serialize = "n", serialize = "natural")] - Natural, -} - -impl PitchOpt { - pub fn is_accidental(&self) -> bool { - match self { - PitchOpt::Flat | PitchOpt::Sharp => true, - PitchOpt::Natural => false, - } - } -} - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumIter, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, -)] -#[strum(serialize_all = "snake_case")] -pub enum PitchClass { - Accidental(Accidentals), - #[default] - Natural(Naturals), -} - -impl PitchClass { - pub fn new(value: i64) -> Self { - if let Ok(v) = Accidentals::try_from(value) { - PitchClass::from(v) - } else { - PitchClass::from(Naturals::try_from(value).expect("")) - } - } -} - -impl Gradient for PitchClass { - const MODULUS: i64 = crate::MODULUS; - - fn class(&self) -> PitchClass { - *self - } -} - -impl From for PitchClass { - fn from(data: Accidentals) -> PitchClass { - PitchClass::Accidental(data) - } -} - -impl From for PitchClass { - fn from(data: Naturals) -> PitchClass { - PitchClass::Natural(data) - } -} - -impl From<&G> for PitchClass { - fn from(value: &G) -> PitchClass { - if let Ok(v) = Accidentals::try_from(value.pitch()) { - PitchClass::from(v) - } else { - PitchClass::from(Naturals::try_from(value.pitch()).expect("")) - } - } -} - -impl From for PitchClass { - fn from(value: i64) -> PitchClass { - PitchClass::from(&Pitch::new(value)) - } -} - -impl From for i64 { - fn from(data: PitchClass) -> i64 { - match data { - PitchClass::Accidental(note) => note.into(), - PitchClass::Natural(note) => note.into(), - } - } -} - -impl From for Pitch { - fn from(data: PitchClass) -> Pitch { - match data { - PitchClass::Accidental(v) => v.into(), - PitchClass::Natural(n) => Pitch::from(n as i64), - } - } -} - -impl std::ops::Add for PitchClass { - type Output = PitchClass; - - fn add(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - PitchClass::new(self.pitch() + interval) - } -} - -impl std::ops::AddAssign for PitchClass { - fn add_assign(&mut self, rhs: Interval) { - *self = *self + rhs; - } -} - -impl std::ops::Div for PitchClass { - type Output = PitchClass; - - fn div(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - PitchClass::new(self.pitch() / interval) - } -} - -impl std::ops::DivAssign for PitchClass { - fn div_assign(&mut self, rhs: Interval) { - *self = *self / rhs; - } -} - -impl std::ops::Mul for PitchClass { - type Output = PitchClass; - - fn mul(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - PitchClass::new(self.pitch() * interval) - } -} - -impl std::ops::MulAssign for PitchClass { - fn mul_assign(&mut self, rhs: Interval) { - *self = *self * rhs; - } -} - -impl std::ops::Sub for PitchClass { - type Output = PitchClass; - - fn sub(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - PitchClass::new(self.pitch() - interval) - } -} - -impl std::ops::SubAssign for PitchClass { - fn sub_assign(&mut self, rhs: Interval) { - *self = *self - rhs; - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_pitch_class() { - let a = PitchClass::default(); - let b = PitchClass::Accidental(Default::default()); - assert_ne!(a.clone(), b.clone()); - assert_eq!(a, PitchClass::Natural(Default::default())); - assert_eq!( - b, - PitchClass::Accidental(Accidentals::Sharp(Default::default())) - ) - } -} diff --git a/music/src/notes/classes/naturals.rs b/music/src/notes/classes/naturals.rs deleted file mode 100644 index 6be20e5..0000000 --- a/music/src/notes/classes/naturals.rs +++ /dev/null @@ -1,93 +0,0 @@ -/* - Appellation: naturals - Contrib: FL03 - Description: ... Summary ... -*/ -use crate::{Gradient, Pitch}; - -use serde::{Deserialize, Serialize}; -use smart_default::SmartDefault; -use strum::{Display, EnumIter, EnumString, EnumVariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Display, - EnumIter, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, - SmartDefault, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum Naturals { - #[default] - C = 0, - D = 2, - E = 4, - F = 5, - G = 7, - A = 9, - B = 11, -} - -impl Gradient for Naturals { - const MODULUS: i64 = 12; - - fn pitch(&self) -> i64 { - *self as i64 - } -} - -impl From for i64 { - fn from(note: Naturals) -> i64 { - note as i64 - } -} - -impl TryFrom for Naturals { - type Error = Box; - - fn try_from(value: Pitch) -> Result { - Naturals::try_from(value.pitch()) - } -} - -impl TryFrom for Naturals { - type Error = Box; - - fn try_from(value: i64) -> Result { - let data = value.abs() % 12; - match data { - 0 => Ok(Self::C), - 2 => Ok(Self::D), - 4 => Ok(Self::E), - 5 => Ok(Self::F), - 7 => Ok(Self::G), - 9 => Ok(Self::A), - 11 => Ok(Self::B), - _ => Err("".into()), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::str::FromStr; - - #[test] - fn test_naturals() { - assert!(Naturals::try_from(1).is_err()); - assert_eq!(Naturals::try_from(5).unwrap(), Naturals::F); - assert_eq!(Naturals::from_str("a").unwrap(), Naturals::A); - } -} diff --git a/music/src/notes/mod.rs b/music/src/notes/mod.rs deleted file mode 100644 index 37e52b0..0000000 --- a/music/src/notes/mod.rs +++ /dev/null @@ -1,81 +0,0 @@ -/* - Appellation: notes - Contrib: FL03 - Description: - def. A note is a symbolic representation of a particular pitch; also called a pitch class - -*/ -pub use self::{aspn::*, classes::*, note::*, pitch::*}; - -mod aspn; -mod classes; -mod note; -mod pitch; - -use crate::Gradient; - -/// [detect_accidentals] is a function for quickly determining the 'accidental' variations of the natural note -/// Given a [NaturalNote] find its optional sharp and flat variations -pub fn detect_accidentals(natural: Naturals) -> (Pitch, Option, Option) { - let note = natural as i64; - // Calculate the modulus of the next (a) and prev (b) position - let ab = ((note + 1).pitch(), (note.pitch() - 1).pitch()); - - if Naturals::try_from(ab.0).is_ok() { - // If a natural note exists with a modulus a semitone above the entry, than it only has one option at -1 (flat) - (note.into(), None, Some(ab.1.into())) - } else if Naturals::try_from(ab.1).is_ok() { - // If a natural note exists with a modulus a semitone below the entry, than it only has one option at +1 (sharp) - (note.into(), Some(ab.0.into()), None) - } else { - // If a natural note doesn't exists a semitone above or below the entry, than it has two possible variations - // a sharp a semitone above and a flat a semitone below - (note.into(), Some(ab.0.into()), Some(ab.1.into())) - } -} - -/// [IntoPitch] describes the conversion of a given object into a [Pitch] -pub trait IntoPitch { - fn into_pitch(self) -> Pitch; -} - -impl IntoPitch for T -where - T: Into, -{ - fn into_pitch(self) -> Pitch { - self.into() - } -} - -/// [FromPitch] describes the conversion of a [Pitch] into a given object -pub trait FromPitch { - fn from_pitch(pitch: Pitch) -> Self; -} - -impl FromPitch for T -where - T: From, -{ - fn from_pitch(pitch: Pitch) -> Self { - Self::from(pitch) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_detect() { - assert!(Naturals::try_from(1).is_err()); - assert_eq!( - detect_accidentals(Naturals::A), - (Pitch::new(9), Some(Pitch::new(10)), Some(Pitch::new(8))) - ); - assert_eq!( - detect_accidentals(Naturals::C), - (Pitch::new(0), Some(Pitch::new(1)), None) - ); - } -} diff --git a/music/src/notes/note.rs b/music/src/notes/note.rs deleted file mode 100644 index eca0f05..0000000 --- a/music/src/notes/note.rs +++ /dev/null @@ -1,155 +0,0 @@ -/* - Appellation: note - Contrib: FL03 - Description: - A note is a symbolic representation of the duration and pitch of a tone; - The enharmonic nature of musical notation enables us to create a system entirely - dependent upon the modulus of the Pitch rather than the specific symbol. - That being said, we will also adopt a note representation similar to that of the - American Scientific Pitch Notation which denotes a certain octave for the given pitch-class. -*/ -use super::{Pitch, PitchClass, ASPN}; -use crate::{intervals::Interval, Gradient}; -use serde::{Deserialize, Serialize}; - -/// A [Note] is simply a wrapper for a [PitchClass], providing additional information such as an octave ([i64]) -/// This type of musical notation is adopted from the American Scientific Pitch Notation -#[derive( - Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct Note { - class: PitchClass, - octave: i64, -} - -impl Note { - pub fn new(class: PitchClass, octave: Option) -> Self { - Self { - class, - octave: octave.unwrap_or(1), - } - } - pub fn aspn(&self) -> ASPN { - ASPN::new(self.class, Some(self.octave)) - } - pub fn interval(&self, other: &Self) -> Interval { - Interval::new(*self, *other) - } - pub fn octave(&self) -> i64 { - self.octave - } -} - -impl Gradient for Note { - const MODULUS: i64 = crate::MODULUS; - - fn class(&self) -> PitchClass { - self.class - } - fn pitch(&self) -> i64 { - self.class.into() - } -} - -impl std::fmt::Display for Note { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}.{}", self.class, self.octave) - } -} - -impl std::ops::Add

for Note { - type Output = Self; - - fn add(self, rhs: P) -> Self::Output { - (self.pitch() + rhs.pitch()).into() - } -} - -impl std::ops::AddAssign

for Note { - fn add_assign(&mut self, rhs: P) { - *self = *self + rhs; - } -} - -impl std::ops::Sub

for Note { - type Output = Self; - - fn sub(self, rhs: P) -> Self::Output { - (self.pitch() - rhs.pitch()).into() - } -} - -impl std::ops::SubAssign

for Note { - fn sub_assign(&mut self, rhs: P) { - *self = *self - rhs; - } -} - -impl std::ops::Add for Note { - type Output = Self; - - fn add(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - (self.pitch() + interval).into() - } -} - -impl std::ops::AddAssign for Note { - fn add_assign(&mut self, rhs: Interval) { - let interval: i64 = rhs.into(); - *self = (self.pitch() + interval).into() - } -} - -impl std::ops::Sub for Note { - type Output = Self; - - fn sub(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - (self.pitch() - interval).into() - } -} - -impl std::ops::SubAssign for Note { - fn sub_assign(&mut self, rhs: Interval) { - let interval: i64 = rhs.into(); - *self = (self.pitch() - interval).into() - } -} - -impl From for Note { - fn from(data: i64) -> Note { - Note::new(PitchClass::from(&Pitch::new(data)), None) - } -} - -impl From for Note { - fn from(data: Pitch) -> Note { - Note::new(PitchClass::from(&data), None) - } -} - -impl From<&P> for Note { - fn from(d: &P) -> Note { - Note::new(d.class(), None) - } -} - -impl From for i64 { - fn from(data: Note) -> i64 { - data.class.into() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Naturals; - - #[test] - fn test_notes() { - let a = Note::new(PitchClass::Natural(Naturals::C), None); - assert_eq!(a.pitch(), 0); - assert_eq!(a.octave(), 1); - } -} diff --git a/music/src/notes/pitch.rs b/music/src/notes/pitch.rs deleted file mode 100644 index 92b03fe..0000000 --- a/music/src/notes/pitch.rs +++ /dev/null @@ -1,220 +0,0 @@ -/* - Appellation: pitch - Contrib: FL03 - Description: - A pitch essentially represents the frequency of a sound wave and has been mathematically expressed to be - p = log(2f) - empirically based on the octave doubling of frequency exponetially - - * All notes or pitches are of mod 12, giving us { 0, 1, ..., 10, 11 } - * Sharp notes and flat notes are simply opposite; if sharp is up then flat is down - For our purposes, sharp notes are represented with positive integers while flat notes are reserved for negatives - - Another possibility would be to describe natural notes as prime numbers as this would restrict their existance and remove any possible enharmonic pairings. - More so, if we consider 1 to be a prime number -*/ -use super::{Note, PitchClass}; -use crate::intervals::Interval; -use crate::Gradient; -use serde::{Deserialize, Serialize}; - -/// [Pitch] describes the modular index of a given frequency -#[derive( - Clone, Copy, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct Pitch(i64); - -impl Pitch { - pub fn new(pitch: i64) -> Self { - Self(pitch) - } - /// Classify the pitch into a pitch class - pub fn class(&self) -> PitchClass { - self.pitch().into() - } - /// Find the modular index of the given pitch - pub fn pitch(&self) -> i64 { - crate::absmod(self.0, crate::MODULUS) - } -} - -impl Gradient for Pitch { - const MODULUS: i64 = crate::MODULUS; - - fn pitch(&self) -> i64 { - crate::absmod(self.0, Self::MODULUS) - } -} - -impl std::fmt::Display for Pitch { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From for Pitch { - fn from(p: i64) -> Pitch { - Pitch::new(p) - } -} - -impl From for Pitch { - fn from(note: Note) -> Pitch { - Pitch::new(note.pitch()) - } -} - -impl From for i64 { - fn from(p: Pitch) -> i64 { - p.0 - } -} - -impl std::ops::Add for Pitch { - type Output = Pitch; - - fn add(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - Pitch::new(self.0 + interval) - } -} - -impl std::ops::AddAssign for Pitch { - fn add_assign(&mut self, rhs: Interval) { - *self = *self + rhs; - } -} - -impl std::ops::Div for Pitch { - type Output = Pitch; - - fn div(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - Pitch::new(self.0 / interval) - } -} - -impl std::ops::DivAssign for Pitch { - fn div_assign(&mut self, rhs: Interval) { - *self = *self / rhs; - } -} - -impl std::ops::Mul for Pitch { - type Output = Pitch; - - fn mul(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - Pitch::new(self.0 * interval) - } -} - -impl std::ops::MulAssign for Pitch { - fn mul_assign(&mut self, rhs: Interval) { - *self = *self * rhs; - } -} - -impl std::ops::Sub for Pitch { - type Output = Pitch; - - fn sub(self, rhs: Interval) -> Self::Output { - let interval: i64 = rhs.into(); - Pitch::new(self.0 - interval) - } -} - -impl std::ops::SubAssign for Pitch { - fn sub_assign(&mut self, rhs: Interval) { - *self = *self - rhs; - } -} - -impl std::ops::Add

for Pitch { - type Output = Pitch; - - fn add(self, rhs: P) -> Self::Output { - Pitch::new(self.0 + rhs.pitch()) - } -} - -impl std::ops::AddAssign

for Pitch { - fn add_assign(&mut self, rhs: P) { - *self = *self + rhs.pitch(); - } -} - -impl std::ops::Div

for Pitch { - type Output = Pitch; - - fn div(self, rhs: P) -> Self::Output { - Pitch::new(self.0 / rhs.pitch()) - } -} - -impl std::ops::DivAssign

for Pitch { - fn div_assign(&mut self, rhs: P) { - *self = *self / rhs.pitch(); - } -} - -impl std::ops::Mul

for Pitch { - type Output = Pitch; - - fn mul(self, rhs: P) -> Self::Output { - Pitch::new(self.0 * rhs.pitch()) - } -} - -impl std::ops::MulAssign

for Pitch { - fn mul_assign(&mut self, rhs: P) { - *self = *self * rhs.pitch(); - } -} - -impl std::ops::Sub

for Pitch { - type Output = Pitch; - - fn sub(self, rhs: P) -> Self::Output { - Pitch::new(self.0 - rhs.pitch()) - } -} - -impl std::ops::SubAssign

for Pitch { - fn sub_assign(&mut self, rhs: P) { - *self = *self - rhs.pitch(); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Accidentals; - - #[test] - fn test_pitch_class() { - let a = PitchClass::default(); - let b = PitchClass::Accidental(Default::default()); - assert_ne!(a.clone(), b.clone()); - assert_eq!(a, PitchClass::Natural(Default::default())); - assert_eq!( - b, - PitchClass::Accidental(Accidentals::Sharp(Default::default())) - ) - } - - #[test] - fn test_pitch() { - let a = Pitch::from(144); - let b = Pitch::from(12); - assert_ne!(a, b); - assert_eq!(a.pitch(), b.pitch()); - } - - #[test] - fn test_pitch_ops() { - let pitch = Pitch::new(3); - assert_eq!(pitch + 1, Pitch::new(4)); - assert_eq!(Pitch::new(5) * 2, Pitch::new(10)); - } -} diff --git a/music/src/primitives.rs b/music/src/primitives.rs deleted file mode 100644 index b7d8648..0000000 --- a/music/src/primitives.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - Appellation: primitives - Contrib: FL03 - Description: ... summary ... -*/ -pub use self::{constants::*, types::*}; - -mod constants { - /// Used to describe the total number of notes considered - pub const MODULUS: i64 = 12; - /// A semitone is half of a tone - pub const SEMITONE: i64 = 1; - /// A tone is a difference of two - pub const TONE: i64 = 2; -} - -mod types { - use futures::Stream; - - pub type BoxedError = Box; - - pub type MusicResult = Result; - /// [Transformation] is a generic [Fn] which transforms one object into another - pub type Transformation = dyn Fn(S) -> T; - /// A type alias for a [Stream] of [Fn] which takes in one object and transforms it into another - /// as defined in Clifton Callender's work on continuous transformations. - pub type HarmonicInterpolation = dyn Stream>; -} diff --git a/music/src/score/clef.rs b/music/src/score/clef.rs deleted file mode 100644 index 21c924b..0000000 --- a/music/src/score/clef.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* - Appellation: clef - Contrib: FL03 - Description: - A clef, placed on the far left-hand side of the staff or stave, signals which notes are represented by the respective staff. -*/ -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumString, VariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Default, - Deserialize, - Display, - EnumString, - Eq, - Hash, - PartialEq, - PartialOrd, - Serialize, - VariantNames -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum Clef { - Alto = 0, - Bass = 1, - #[default] - Treble = 2, -} - -impl From for i64 { - fn from(data: Clef) -> i64 { - data as i64 - } -} - -impl From for Clef { - fn from(data: i64) -> Clef { - match data { - 0 => Clef::Alto, - 1 => Clef::Bass, - _ => Clef::Treble, - } - } -} diff --git a/music/src/score/measure.rs b/music/src/score/measure.rs deleted file mode 100644 index 341cff9..0000000 --- a/music/src/score/measure.rs +++ /dev/null @@ -1,10 +0,0 @@ -/* - Appellation: measure - Contrib: FL03 - Description: In music theory, a measure (or bar) refers to a single unit of time featuring a specific number of beats played at a particular tempo -*/ -use tokio::time::Interval; - -pub struct Measure { - pub interval: Interval, -} diff --git a/music/src/score/mod.rs b/music/src/score/mod.rs deleted file mode 100644 index 8f6d96e..0000000 --- a/music/src/score/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -/* - Appellation: score - Contrib: FL03 - Description: A score is a collection of staves, which are collections of measures, which are collections of notes. -*/ -pub use self::{clef::*, measure::*, stave::*}; - -mod clef; -mod measure; -mod stave; diff --git a/music/src/score/stave.rs b/music/src/score/stave.rs deleted file mode 100644 index bb93619..0000000 --- a/music/src/score/stave.rs +++ /dev/null @@ -1,12 +0,0 @@ -/* - Appellation: stave - Contrib: FL03 - Description: A stave is typically composed of five lines and four spaces, and is used to represent the staff on which notes are written. -*/ -use super::Clef; -use crate::chords::Chord; - -pub struct Stave { - pub clef: Clef, - pub measures: Vec, -} diff --git a/music/src/specs.rs b/music/src/specs.rs deleted file mode 100644 index 135957f..0000000 --- a/music/src/specs.rs +++ /dev/null @@ -1,36 +0,0 @@ -/* - Appellation: specs - Contrib: FL03 -*/ -use crate::prelude::{absmod, Interval, PitchClass}; -use std::ops::{AddAssign, SubAssign}; - -/// [IntervalMath] defines the operations that can be preformed with [Interval]s -pub trait IntervalMath: AddAssign + SubAssign {} - -impl IntervalMath for T where T: AddAssign + SubAssign {} - -/// [Gradient] provides a numerical interpretation of a given object -pub trait Gradient: Clone + Eq + Ord + Into { - const MODULUS: i64; - - fn class(&self) -> PitchClass { - self.pitch().into() - } - /// [Gradient::pitch] is a method for numerically representing the structure - fn pitch(&self) -> i64 { - absmod(self.clone().into(), Self::MODULUS) - } -} - -pub trait GradientExt: Gradient + IntervalMath {} - -impl GradientExt for T where T: Gradient + IntervalMath {} - -impl Gradient for i64 { - const MODULUS: i64 = 12; - - fn pitch(&self) -> i64 { - absmod(*self, Self::MODULUS) - } -} diff --git a/music/src/utils.rs b/music/src/utils.rs deleted file mode 100644 index e024e55..0000000 --- a/music/src/utils.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* - Appellation: utils - Contrib: FL03 - Description: ... summary ... -*/ -use itertools::Itertools; - -/// [absmod] is short for the absolute value of a modular number; -pub fn absmod(a: i64, m: i64) -> i64 { - (((a % m) + m) % m).abs() -} - -/// [harmonic_transformation] is a transformative function for continuous musical space -/// This is useful for describing the behavior between transitions as nothing is achieved instantly -pub fn harmonic_transformation(a: usize, b: usize, t: usize) -> usize { - (b - a) * t + a -} - -/// Find the difference between a collection of items where each element implements [Clone], [Into], and [Ord] -pub fn intervals(args: impl IntoIterator) -> Vec<((T, T), i64)> -where - T: Clone + Into + Ord, -{ - let pairs = { - let mut tmp = Vec::from_iter(args); - tmp.sort(); - tmp.clone() - .into_iter() - .circular_tuple_windows::<(T, T)>() - .collect::>() - }; - let mut res = pairs - .into_iter() - .map(|i| (i.clone(), i.1.into() - i.0.into())) - .collect::>(); - res.sort_by(|a, b| { - a.0.partial_cmp(&b.0) - .unwrap() - .then(a.1.abs().partial_cmp(&b.1.abs()).unwrap()) - }); - res -} -/// A simple function wrapper for [Itertools::permutations] -pub fn permute(args: impl IntoIterator, size: usize) -> Vec> -where - T: Clone, -{ - args.into_iter().permutations(size).collect::>() -} diff --git a/music/tests/default.rs b/music/tests/default.rs deleted file mode 100644 index aca731c..0000000 --- a/music/tests/default.rs +++ /dev/null @@ -1,29 +0,0 @@ -#[cfg(test)] -use contained_music::{absmod, intervals, Note}; - -#[test] -fn compiles() { - let f = |i: usize| i * i; - - assert_eq!(f(2), 4) -} - -#[test] -fn test_absmod() { - let a: i64 = -13 % 12; - assert_ne!(a.abs(), absmod(-13, 12)); - assert_eq!(absmod(-1, 12), 11); -} - -#[test] -fn test_intervals() { - let notes: Vec = vec![0.into(), 4.into(), 7.into()]; - assert_eq!( - intervals(notes), - vec![ - ((Note::from(0), Note::from(4)), 4), - ((Note::from(4), Note::from(7)), 3), - ((Note::from(7), Note::from(0)), -7), - ] - ); -} diff --git a/music/tests/music.rs b/music/tests/music.rs deleted file mode 100644 index fd75f42..0000000 --- a/music/tests/music.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[cfg(test)] -use contained_music::Gradient; - -#[test] -fn test_gradient() { - let b = -13; - assert_eq!(144_i64.pitch(), 0); - assert_eq!(b.pitch(), 11) -} diff --git a/scripts/setup.sh b/scripts/setup.sh deleted file mode 100644 index 21976b1..0000000 --- a/scripts/setup.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -rustup install nightly -rustup component add clippy rustfmt --toolchain nightly -rustup target add wasm32-unknown-unknown --toolchain nightly diff --git a/turing/Cargo.toml b/turing/Cargo.toml deleted file mode 100644 index d318f9d..0000000 --- a/turing/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -authors.workspace = true -categories = [] -description.workspace = true -edition.workspace = true -homepage.workspace = true -keywords = [] -license.workspace = true -name = "contained-turing" -readme.workspace = true -repository.workspace = true -version.workspace = true - -[lib] -crate-type = ["cdylib", "rlib"] -test = true - -[features] -default = [] -wasm = [] - -[build-dependencies] - -[dependencies] -anyhow.workspace = true -async-trait.workspace = true -contained-core = { path = "../core", version = "0.1.6" } -# decanter.workspace = true -futures.workspace = true -itertools.workspace = true -predicates = "3.0" -serde.workspace = true -serde_json.workspace = true -smart-default.workspace = true -strum.workspace = true - -[dev-dependencies] -contained-core = { path = "../core" } - -[package.metadata.docs.rs] -all-features = true -rustc-args = ["--cfg", "docsrs"] - -[target.wasm32-unknown-unknown] - -[target.wasm32-wasi] diff --git a/turing/src/exec.rs b/turing/src/exec.rs deleted file mode 100644 index 0a65d9e..0000000 --- a/turing/src/exec.rs +++ /dev/null @@ -1,172 +0,0 @@ -/* - Appellation: exec - Contrib: FL03 -*/ -use crate::instructions::Instruction; -use crate::{Alphabet, Program, Scope, Symbolic}; -use async_trait::async_trait; -use contained_core::{ - states::{State, Stateful}, - Error, -}; -use futures::{Future, StreamExt}; -use predicates::Predicate; -use std::sync::{Arc, Mutex}; - -/// [AsyncExecute] describes a self-contained executor that can be executed asynchronously. -#[async_trait] -pub trait AsyncExecute: - Alphabet + StreamExt> + Stateful + Unpin -{ - type Driver: Future + Scope + Send + Sync; - type Error: Send + Sync; - - async fn execute(&mut self) -> Result<&Arc>, Self::Error> { - // Get the default symbol - let default_symbol = self.clone().default_symbol(); - // Get the next instruction - while let Some(instruction) = self.next().await { - // Get the tail of the instruction - let tail = instruction.clone().tail(); - // Update the current state - self.update_state(tail.state()); - // Update the tape - self.scope_mut().lock().unwrap().set_symbol(tail.symbol()); - // Update the index; adjusts the index according to the direction - self.scope_mut() - .lock() - .unwrap() - .shift(tail.action(), default_symbol.clone()); - } - // Return the actor - Ok(self.scope()) - } - /// Returns a reference to the scope - fn scope(&self) -> &Arc>; - /// Returns a mutable reference to the scope - fn scope_mut(&mut self) -> &mut Arc>; -} - -/// [Execute] describes a self-contained executor that can be executed synchronously. -pub trait Execute: - Alphabet + Iterator> + Stateful -{ - type Driver: Scope; - - /// [Execute::execute] - fn execute(&mut self) -> Result<&Self::Driver, Error> { - // Get the default symbol - let default_symbol = self.program().default_symbol(); - // Get the next instruction - while let Some(instruction) = self.next() { - let tail = instruction.clone().tail(); - // Update the current state - self.update_state(tail.state()); - // Update the tape - self.scope_mut().set_symbol(tail.symbol()); - // Update the index; adjusts the index according to the direction - self.scope_mut() - .shift(tail.action(), default_symbol.clone()); - } - // Return the actor - Ok(self.scope()) - } - /// [Execute::execute_once] - fn execute_once(&mut self) -> Result<&Self::Driver, Error> { - // Get the default symbol - let default_symbol = self.clone().default_symbol(); - // Get the next instruction - if let Some(instruction) = self.next() { - let tail = instruction.tail(); - // Update the current state - self.update_state(tail.state()); - // Update the tape - self.scope_mut().set_symbol(tail.symbol()); - // Update the index; adjusts the index according to the direction - self.scope_mut().shift(tail.action(), default_symbol); - // Return the actor - return Ok(self.scope()); - } - Err(Error::ExecutionError( - "No more instructions to execute".into(), - )) - } - /// [Execute::execute_until] - fn execute_until( - &mut self, - until: impl Predicate, - ) -> Result<&Self::Driver, Error> { - while !until.eval(self.scope()) { - self.execute_once()?; - } - Ok(self.scope()) - } - - fn program(&self) -> &Program; - - fn scope(&self) -> &Self::Driver; - - fn scope_mut(&mut self) -> &mut Self::Driver; -} - -/// [Executable] describes a program that can be executed with an external driver. -pub trait Executable: Clone + Alphabet + Iterator> { - type Driver: Scope; - type Error; - - fn execute(&mut self, driver: &mut Self::Driver) -> Result { - // Get the default symbol - let default_symbol = self.clone().default_symbol(); - // Get the next instruction - for instruction in self.by_ref() { - let tail = instruction.clone().tail(); - // Update the current state - driver.update_state(tail.state()); - // Update the tape - driver.set_symbol(tail.symbol()); - // Update the index; adjusts the index according to the direction - driver.shift(tail.action(), default_symbol.clone()); - } - // Return the actor - Ok(driver.clone()) - } - fn execute_once(&mut self, driver: &mut Self::Driver) -> Result { - // Get the default symbol - let default_symbol = self.clone().default_symbol(); - // Get the next instruction - if let Some(instruction) = self.next() { - let tail = instruction.tail(); - // Update the current state - driver.update_state(tail.state()); - // Update the tape - driver.set_symbol(tail.symbol()); - // Update the index; adjusts the index according to the direction - driver.shift(tail.action(), default_symbol); - } - // Return the actor - Ok(driver.clone()) - } - fn execute_until( - &mut self, - driver: &mut Self::Driver, - until: impl Predicate, - ) -> Result { - // Get the default symbol - let default_symbol = self.clone().default_symbol(); - // Get the next instruction - for instruction in self.by_ref() { - let tail = instruction.clone().tail(); - // Update the current state - driver.update_state(tail.state()); - // Update the tape - driver.set_symbol(tail.symbol()); - // Update the index; adjusts the index according to the direction - driver.shift(tail.action(), default_symbol.clone()); - if until.eval(driver) { - break; - } - } - // Return the actor - Ok(driver.clone()) - } -} diff --git a/turing/src/instructions/head.rs b/turing/src/instructions/head.rs deleted file mode 100644 index 446f027..0000000 --- a/turing/src/instructions/head.rs +++ /dev/null @@ -1,60 +0,0 @@ -/* - Appellation: head - Contrib: FL03 - Description: - The instruction head is a two-tuple (State, Symbol) -*/ -use crate::{machine::Driver, Scope, Symbolic}; -use contained_core::prelude::{State, Stateful}; -use serde::{Deserialize, Serialize}; - -#[derive( - Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct Head { - state: State, - symbol: S, -} - -impl Head { - pub fn new(state: State, symbol: S) -> Self { - Self { state, symbol } - } - pub fn symbol(&self) -> S { - self.symbol.clone() - } -} - -impl Stateful for Head { - fn state(&self) -> State { - self.state - } - - fn update_state(&mut self, state: State) { - self.state = state; - } -} - -impl std::fmt::Display for Head { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "({}, {})", self.state, self.symbol) - } -} - -impl From> for Head { - fn from(value: Driver) -> Self { - Self::new(value.state(), value.current()) - } -} - -impl From> for (State, S) { - fn from(v: Head) -> (State, S) { - (v.state(), v.symbol()) - } -} - -impl From<(State, S)> for Head { - fn from(value: (State, S)) -> Self { - Self::new(value.0, value.1) - } -} diff --git a/turing/src/instructions/instruction.rs b/turing/src/instructions/instruction.rs deleted file mode 100644 index 3c05017..0000000 --- a/turing/src/instructions/instruction.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - Appellation: instructions - Contrib: FL03 -*/ -/// # Instructions -/// -/// Turing machines accept instructions in the form of a five-tuple: -/// (State, Symbol, State, Symbol, Move) -use super::{Head, Move, Tail}; -use crate::Symbolic; -use contained_core::prelude::State; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Instruction(Head, Tail); - -impl Instruction { - pub fn new(head: Head, tail: Tail) -> Self { - Self(head, tail) - } - pub fn head(&self) -> Head { - self.0.clone() - } - pub fn tail(&self) -> Tail { - self.1.clone() - } - pub fn update(&mut self, head: Head, tail: Tail) { - self.0 = head; - self.1 = tail; - } -} - -impl From<(State, S, State, S, Move)> for Instruction { - fn from(value: (State, S, State, S, Move)) -> Self { - let head = Head::new(value.0, value.1); - let tail = Tail::new(value.2, value.3, value.4); - Self::new(head, tail) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_instructions() { - let head = Head::new(State::invalid(), "b"); - let tail = Tail::new(State::invalid(), "a", Move::Right); - let instructions = Instruction::new(head, tail); - - assert_eq!(instructions.tail().action(), Move::Right) - } -} diff --git a/turing/src/instructions/iter.rs b/turing/src/instructions/iter.rs deleted file mode 100644 index 8a0123e..0000000 --- a/turing/src/instructions/iter.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - Appellation: iter - Contrib: FL03 - Description: - This module contains the implementation of the [InstructionSet] trait. -*/ -use super::*; -use crate::Symbolic; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Iter { - cursor: usize, - instructions: Vec>, -} - -impl Iter { - pub fn new(instructions: Vec>) -> Self { - Self { - cursor: 0, - instructions, - } - } -} - -impl Extend> for Iter { - fn extend>>(&mut self, iter: T) { - self.instructions.extend(iter) - } -} - -impl Iterator for Iter { - type Item = Instruction; - - fn next(&mut self) -> Option { - let instruction = self.instructions.get(self.cursor).cloned(); - self.cursor += 1; - instruction - } -} diff --git a/turing/src/instructions/mod.rs b/turing/src/instructions/mod.rs deleted file mode 100644 index 2dd8a06..0000000 --- a/turing/src/instructions/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -/* - Appellation: instructions - Contrib: FL03 - Description: - Turing machines accept instructions in the form of a five-tuple: - (State, Symbol, State, Symbol, Move) -*/ -pub use self::{head::*, instruction::*, iter::*, moves::*, tail::*}; - -mod head; -mod instruction; -mod iter; -mod moves; -mod tail; - -use crate::Symbolic; -use contained_core::states::{State, Stateful}; - -pub trait InstructionHead: Stateful { - fn symbol(&self) -> S; -} - -pub trait InstructionTail: Stateful { - fn action(&self) -> Move; - fn symbol(&self) -> S; -} - -pub trait InstructionSpec: IntoIterator { - type Head: InstructionHead; - type Tail: InstructionTail; - - fn new(head: Self::Head, tail: Self::Tail) -> Self; - fn head(&self) -> Self::Head; - fn tail(&self) -> Self::Tail; -} - -pub trait InstructionSet: Iterator> { - type Head: InstructionHead; - type Tail: InstructionTail; - - fn new(head: Self::Head, tail: Self::Tail) -> Self; - fn cursor(&self) -> usize; -} diff --git a/turing/src/instructions/moves.rs b/turing/src/instructions/moves.rs deleted file mode 100644 index 7b1c4a5..0000000 --- a/turing/src/instructions/moves.rs +++ /dev/null @@ -1,110 +0,0 @@ -/* - Appellation: moves - Contrib: FL03 - Description: - The Move enum is used to represent the direction of a Turing machine's - head. It is used in the instruction set to determine the next state of - the machine. -*/ -use crate::{Symbolic, Tape}; -use contained_core::ArrayLike; -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumString, EnumVariantNames}; - -#[derive( - Clone, - Copy, - Debug, - Default, - Deserialize, - Display, - EnumString, - EnumVariantNames, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Serialize, -)] -#[repr(i64)] -#[strum(serialize_all = "snake_case")] -pub enum Move { - Left = -1, - Right = 1, - #[default] - Stay = 0, -} - -impl Move { - pub fn invert(&self) -> Self { - match self { - Self::Left => Self::Right, - Self::Right => Self::Left, - Self::Stay => Self::Stay, - } - } - pub fn apply( - &self, - mut index: usize, - mut tape: Tape, - elem: S, - ) -> (usize, Tape) { - match *self { - // If the current position is 0, insert a new element at the top of the vector - Move::Left if index == 0 => { - tape[index] = elem; - } - Move::Left => { - index -= 1; - } - Move::Right => { - index += 1; - - if index == tape.len() { - tape[index] = elem; - } - } - Move::Stay => {} - }; - (index, tape.clone()) - } - pub fn shift(&self, pos: usize) -> usize { - (pos as i64 + *self as i64) as usize - } -} - -impl std::ops::Mul for usize { - type Output = usize; - - fn mul(self, rhs: Move) -> Self::Output { - rhs.shift(self) - } -} - -impl From for Move { - fn from(d: i64) -> Self { - match d % 2 { - -1 => Self::Left, - 1 => Self::Right, - _ => Self::Stay, - } - } -} - -impl From for i64 { - fn from(d: Move) -> i64 { - d as i64 - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_move_default() { - let a = Move::default(); - assert_eq!(a.clone(), Move::Stay); - } -} diff --git a/turing/src/instructions/tail.rs b/turing/src/instructions/tail.rs deleted file mode 100644 index 284d1d2..0000000 --- a/turing/src/instructions/tail.rs +++ /dev/null @@ -1,67 +0,0 @@ -/* - Appellation: tail - Contrib: FL03 -*/ -/// # Tail -/// -/// The tail of an instruction is the second half of a instruction set -use super::Move; -use crate::Symbolic; -use contained_core::states::{State, Stateful}; -use serde::{Deserialize, Serialize}; - -#[derive( - Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct Tail { - state: State, - symbol: S, - action: Move, -} - -impl Tail { - pub fn new(state: State, symbol: S, action: Move) -> Self { - Self { - state, - symbol, - action, - } - } - pub fn action(&self) -> Move { - self.action - } - pub fn state(&self) -> State { - self.state - } - pub fn symbol(&self) -> S { - self.symbol.clone() - } -} - -impl Stateful for Tail { - fn state(&self) -> State { - self.state - } - - fn update_state(&mut self, state: State) { - self.state = state; - } -} - -impl std::fmt::Display for Tail { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "({}, {}, {})", self.state, self.symbol, self.action) - } -} - -impl From<(State, S, Move)> for Tail { - fn from(args: (State, S, Move)) -> Self { - Self::new(args.0, args.1, args.2) - } -} - -impl From> for (State, S, Move) { - fn from(tail: Tail) -> (State, S, Move) { - (tail.state(), tail.symbol(), tail.action()) - } -} diff --git a/turing/src/lib.rs b/turing/src/lib.rs deleted file mode 100644 index a64ae50..0000000 --- a/turing/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - Appellation: turing - Contrib: FL03 -*/ -/// # Turing -pub use self::{exec::*, primitives::*, programs::*, specs::*, tape::*, utils::*}; - -pub(crate) use contained_core as core; - -mod exec; -mod primitives; - -mod programs; -mod specs; -mod tape; -mod utils; - -pub mod errors; -pub mod instructions; -pub mod machine; - -pub mod prelude { - pub use super::errors::*; - pub use super::exec::*; - pub use super::instructions::*; - pub use super::machine::*; - pub use super::primitives::*; - pub use super::programs::*; - pub use super::specs::*; - pub use super::tape::*; - pub use super::utils::*; -} diff --git a/turing/src/machine/driver.rs b/turing/src/machine/driver.rs deleted file mode 100644 index e03d36c..0000000 --- a/turing/src/machine/driver.rs +++ /dev/null @@ -1,159 +0,0 @@ -/* - Appellation: driver - Contrib: FL03 - Description: ... Summary ... -*/ -use crate::{Scope, Symbolic, Tape}; -use contained_core::states::{State, Stateful}; -use contained_core::{ArrayLike, Include, Insert}; - -use serde::{Deserialize, Serialize}; -use std::cell::RefCell; - -/// [Driver] implements the [Scope] trait and essentially represents the focus of a [super::Turing] machine -/// [Driver] is a [Stateful] [Iterator] that tracks the [State] of the [super::Turing] machine and the current position of the [Tape] -#[derive(Clone, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Driver { - index: RefCell, - state: State, - pub memory: Tape, -} - -impl Driver { - pub fn new(state: State, memory: Tape) -> Self { - Self { - index: 0.into(), - state, - memory, - } - } -} - -impl AsMut> for Driver { - fn as_mut(&mut self) -> &mut Driver { - self - } -} - -impl AsRef> for Driver { - fn as_ref(&self) -> &Driver { - self - } -} - -impl ExactSizeIterator for Driver { - fn len(&self) -> usize { - self.memory.len() - } -} - -impl Include for Driver { - fn include(&mut self, elem: S) { - self.memory.insert(self.cursor(), elem); - } -} - -impl Insert for Driver { - fn insert(&mut self, index: usize, elem: S) { - self.memory.insert(index, elem); - } -} - -impl Iterator for Driver { - type Item = S; - - fn next(&mut self) -> Option { - if let Some(cur) = self.memory.get(self.cursor()).cloned() { - self.index.replace(self.cursor() + 1); - Some(cur) - } else { - None - } - } -} - -impl Scope for Driver { - fn cursor(&self) -> usize { - *self.index.borrow() - } - - fn set_symbol(&mut self, elem: S) { - self.memory.set(self.cursor(), elem); - } - - fn tape(&self) -> Tape { - self.memory.clone() - } - - fn set_index(&mut self, pos: usize) { - self.index.replace(pos); - } -} - -impl Stateful for Driver { - fn state(&self) -> State { - self.state - } - - fn update_state(&mut self, state: State) { - self.state = state; - } -} - -impl std::fmt::Display for Driver { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}, {}, {:?}", self.cursor(), self.state, self.memory) - } -} - -impl From> for Driver { - fn from(tape: Tape) -> Self { - Self::new(State::Valid, tape) - } -} - -impl TryFrom<(usize, State, Tape)> for Driver { - type Error = Box; - - fn try_from(d: (usize, State, Tape)) -> Result { - if d.0 > d.2.len() { - return Err("Starting index is out of bounds...".into()); - } - Ok(Self { - index: d.0.into(), - state: d.1, - memory: d.2, - }) - } -} - -impl From> for (usize, State, Tape) { - fn from(d: Driver) -> Self { - (d.cursor(), d.state, d.memory) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::instructions::Move; - - #[test] - fn test_builder() { - let tape = ["a", "b", "c"]; - assert_ne!(Tape::norm(tape), Tape::std(tape)); - } - - #[test] - fn test_operations() { - let tape = Tape::from_iter(["a", "b", "c"]); - let mut actor = Driver::new(State::Valid, tape); - - actor.shift(Move::Left, "b"); - assert_eq!(actor.tape(), Tape::from_iter(["b", "a", "b", "c"])); - for _ in 0..actor.tape().len() { - actor.shift(Move::Right, "b"); - } - assert_eq!(actor.tape(), Tape::from_iter(["b", "a", "b", "c", "b"])); - } -} diff --git a/turing/src/machine/mod.rs b/turing/src/machine/mod.rs deleted file mode 100644 index d763d0a..0000000 --- a/turing/src/machine/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -/* - Appellation: machine - Contrib: FL03 -*/ - -pub use self::{driver::*, platform::*}; - -mod driver; -mod platform; diff --git a/turing/src/machine/platform.rs b/turing/src/machine/platform.rs deleted file mode 100644 index 61d8e0b..0000000 --- a/turing/src/machine/platform.rs +++ /dev/null @@ -1,115 +0,0 @@ -/* - Appellation: machine - Contrib: FL03 -*/ -use super::Driver; -use crate::instructions::Instruction; -use crate::{Alphabet, Program, Scope, Symbolic, Tape, Translate, Turing}; -use contained_core::prelude::{ArrayLike, Error, State, Stateful}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Machine { - pub driver: Driver, - pub program: Program, -} - -impl Machine { - pub fn new(driver: Driver, program: Program) -> Self { - Self { driver, program } - } - pub fn program(&self) -> Program { - self.program.clone() - } - pub fn scope(&self) -> Driver { - self.driver.clone() - } - pub fn tape(&self) -> Tape { - self.driver.tape() - } -} - -impl Extend for Machine { - fn extend>(&mut self, iter: T) { - self.driver.memory.extend(iter) - } -} - -impl Extend> for Machine { - fn extend>>(&mut self, iter: T) { - self.program.extend(iter) - } -} - -impl Iterator for Machine { - type Item = Instruction; - - fn next(&mut self) -> Option { - if let Some(cur) = self.clone().driver.tape().get(self.driver.cursor()) { - // Get the instruction - self.program - .get((self.state(), cur.clone()).into()) - .cloned() - } else { - None - } - } -} - -impl Alphabet for Machine { - fn is_viable(&self, symbol: &S) -> bool { - self.program.is_viable(symbol) - } - fn default_symbol(&self) -> S { - self.program.default_symbol() - } -} - -impl Stateful for Machine { - fn state(&self) -> State { - self.driver.state() - } - - fn update_state(&mut self, state: State) { - self.driver.update_state(state) - } -} - -impl Turing for Machine { - type Scope = Driver; - - fn execute(&mut self) -> Result<&Self, Self::Error> { - let until = |actor: &Driver| actor.state() == State::Invalid; - self.execute_until(until) - } - - fn execute_once(&mut self) -> Result<&Self, Self::Error> { - let head = self.driver.clone().into(); - let inst = self.program.get(head).expect("").clone(); - self.driver.update_state(inst.tail().state()); - self.driver.set_symbol(inst.tail().symbol()); - self.driver - .shift(inst.tail().action(), self.program.default_symbol()); - Ok(self) - } - - fn execute_until( - &mut self, - until: impl Fn(&Self::Scope) -> bool, - ) -> Result<&Self, Self::Error> { - while !until(&self.driver) { - self.execute_once()?; - } - Ok(self) - } -} - -impl Translate for Machine { - type Error = Error; - - fn translate(&mut self, tape: Tape) -> Result, Self::Error> { - self.driver = Driver::from(tape); - self.execute()?; - Ok(self.driver.tape()) - } -} diff --git a/turing/src/primitives.rs b/turing/src/primitives.rs deleted file mode 100644 index 98978e6..0000000 --- a/turing/src/primitives.rs +++ /dev/null @@ -1,9 +0,0 @@ -/* - Appellation: primitives - Contrib: FL03 -*/ -pub use self::{constants::*, types::*}; - -mod constants {} - -mod types {} diff --git a/turing/src/programs.rs b/turing/src/programs.rs deleted file mode 100644 index 45c07a1..0000000 --- a/turing/src/programs.rs +++ /dev/null @@ -1,226 +0,0 @@ -/* - Appellation: programs - Contrib: FL03 - Description: ... summary ... -*/ -use super::instructions::{Head, Instruction}; -use super::{Alphabet, Symbolic}; -use contained_core::{ - states::{State, Stateful}, - Include, Insert, -}; -use serde::{Deserialize, Serialize}; -use std::{ - mem::replace, - ops::{Index, IndexMut}, -}; - -pub trait Contract: - Clone - + IndexMut> - + Include> - + Insert> -{ - fn alphabet(&self) -> Box>; - fn final_state(&self) -> State; - - /// Given some [Head], find the coresponding [Instruction] - fn get(&self, head: Head) -> Option<&Instruction> { - // TODO: Reimplement the checks for getting a head value - if head.state() > self.final_state() { - return None; - } - self.instructions() - .iter() - .find(|inst: &&Instruction| inst.head() == head) - } - /// Try to insert a new [Instruction] into the program; if the instruction is invalid, return None - /// Otherwise, return the previous instruction at the same [Head] if it exists - fn insert(&mut self, inst: Instruction) -> Option> { - // TODO: Reimplement the checks for insertion - if inst.head().state() == State::Invalid { - return None; - } - if self.final_state() < inst.head().state() || self.final_state() < inst.tail().state() { - return None; - } - if !self.alphabet().is_viable(&inst.head().symbol()) - || !self.alphabet().is_viable(&inst.tail().symbol()) - { - return None; - } - - match self - .instructions() - .iter() - .position(|cand: &Instruction| cand.head() == inst.head()) - { - Some(index) => Some(replace(&mut self.instructions_mut()[index], inst)), - None => { - self.instructions_mut().push(inst.clone()); - Some(inst) - } - } - } - - fn instructions(&self) -> &Vec>; - fn instructions_mut(&mut self) -> &mut Vec>; -} - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Program { - pub alphabet: Vec, - instructions: Vec>, - final_state: State, -} - -impl Program { - pub fn new(alphabet: impl IntoIterator, final_state: State) -> Self { - let alphabet = Vec::from_iter(alphabet); - let s: i64 = final_state.into(); - let capacity = alphabet.len() * s as usize; - let instructions = Vec::with_capacity(capacity); - - Self { - alphabet, - instructions, - final_state, - } - } - /// Returns an owned instance of the current program's alphabet - pub fn alphabet(&self) -> &Vec { - &self.alphabet - } - /// Returns an owned instance of the current [Instruction] set - pub fn instructions(&self) -> &Vec> { - &self.instructions - } - /// Returns an owned instance of the final state - pub fn final_state(&self) -> &State { - &self.final_state - } - /// Given some [Head], find the coresponding [Instruction] - pub fn get(&self, head: Head) -> Option<&Instruction> { - if head.state() > *self.final_state() { - return None; - } - self.instructions() - .iter() - .find(|inst: &&Instruction| inst.head() == head) - } - /// Try to insert a new [Instruction] into the program; if the instruction is invalid, return None - /// Otherwise, return the previous instruction at the same [Head] if it exists - pub fn insert(&mut self, inst: Instruction) -> Option> { - // TODO: Reimplement the checks for insertion - if inst.head().state() == State::Invalid { - return None; - } - if *self.final_state() < inst.head().state() || *self.final_state() < inst.tail().state() { - return None; - } - if !self.alphabet().is_viable(&inst.head().symbol()) - || !self.alphabet().is_viable(&inst.tail().symbol()) - { - return None; - } - - match self - .instructions() - .iter() - .position(|cand: &Instruction| cand.head() == inst.head()) - { - Some(index) => Some(replace(&mut self.instructions[index], inst)), - None => { - self.instructions.push(inst.clone()); - Some(inst) - } - } - } - fn check_instruction(&self, inst: &Instruction) -> bool { - if inst.head().state() == State::Invalid { - return false; - } - if *self.final_state() < inst.head().state() || *self.final_state() < inst.tail().state() { - return false; - } - if !self.alphabet().is_viable(&inst.head().symbol()) - || !self.alphabet().is_viable(&inst.tail().symbol()) - { - return false; - } - true - } -} - -impl Alphabet for Program { - fn is_viable(&self, symbol: &S) -> bool { - self.alphabet.is_viable(symbol) - } - fn default_symbol(&self) -> S { - self.alphabet.default_symbol() - } -} - -impl Extend> for Program { - fn extend>>(&mut self, iter: T) { - for i in iter { - self.insert(i); - } - } -} - -impl Include> for Program { - fn include(&mut self, inst: Instruction) { - if self.check_instruction(&inst) { - match self - .instructions() - .iter() - .position(|cand: &Instruction| cand.head() == inst.head()) - { - Some(index) => { - let _ = std::mem::replace(&mut self.instructions[index], inst); - } - None => { - self.instructions.push(inst.clone()); - } - } - } - } -} - -// impl Insert> for Program { -// fn insert(&mut self, index: usize, inst: Instruction) { -// if self.check_instruction(&inst) { -// self.instructions.insert(index, inst); -// } -// } -// } - -impl Index for Program { - type Output = Instruction; - - fn index(&self, index: usize) -> &Self::Output { - &self.instructions[index] - } -} - -impl IndexMut for Program { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.instructions[index] - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::instructions::Move; - - #[test] - fn test_program() { - let inst = Instruction::from((State::valid(), "a", State::valid(), "b", Move::Right)); - let mut program = Program::new(vec!["a", "b", "c"], State::invalid()); - - assert!(program.insert(inst.clone()).is_some()); - assert!(program.get(inst.head().clone()).is_some()) - } -} diff --git a/turing/src/specs.rs b/turing/src/specs.rs deleted file mode 100644 index 9ab64da..0000000 --- a/turing/src/specs.rs +++ /dev/null @@ -1,151 +0,0 @@ -/* - Appellation: specs - Contrib: FL03 -*/ -use crate::instructions::Move; -use crate::Tape; -use contained_core::states::{State, Stateful}; -use contained_core::{ArrayLike, Include, Insert}; -use std::collections::{BTreeSet, HashSet}; - -/// [Alphabet] describes an immutable set of [Symbolic] elements -pub trait Alphabet { - /// [Alphabet::default_symbol] - fn default_symbol(&self) -> S { - Default::default() - } - /// Returns true if the symbol is in the alphabet - fn is_viable(&self, symbol: &S) -> bool; -} - -impl Alphabet for Vec { - fn is_viable(&self, symbol: &S) -> bool { - self.contains(symbol) - } - - fn default_symbol(&self) -> S { - if let Some(entry) = self.first() { - entry.clone() - } else { - Default::default() - } - } -} - -impl Alphabet for BTreeSet { - fn is_viable(&self, symbol: &S) -> bool { - self.contains(symbol) - } - fn default_symbol(&self) -> S { - if let Some(entry) = self.first() { - entry.clone() - } else { - Default::default() - } - } -} - -impl Alphabet for HashSet { - fn is_viable(&self, symbol: &S) -> bool { - self.contains(symbol) - } - - fn default_symbol(&self) -> S { - if let Some(entry) = self.iter().next() { - entry.clone() - } else { - Default::default() - } - } -} - -/// [Scope] describes the focus of the [crate::turing::Turing] -pub trait Scope: Include + Insert + Stateful { - /// [Scope::current] returns the current element of the [Scope] on the [Tape] - fn current(&self) -> S { - self.tape() - .get(self.cursor()) - .expect("Index is out of bounds...") - .clone() - } - /// [Scope::cursor] returns the current position of the [Scope] on the [Tape] - fn cursor(&self) -> usize; - /// [Scope::set_index] sets the current position of the [Scope] on the [Tape] - fn set_index(&mut self, index: usize); - /// [Scope::set_symbol] sets the current element of the [Scope] on the [Tape] - fn set_symbol(&mut self, elem: S); - /// [Move::Left] inserts a new element at the start of the tape if the current position is 0 - /// [Move::Right] inserts a new element at the end of the tape if the current position equals the total number of cells - /// [Move::Stay] does nothing - fn shift(&mut self, shift: Move, elem: S) { - let index = self.cursor(); - - match shift { - // If the current position is 0, insert a new element at the top of the vector - Move::Left if self.cursor() == 0 => { - self.include(elem); - } - Move::Left => { - self.set_index(index - 1); - } - Move::Right => { - self.set_index(index + 1); - - if self.cursor() == self.tape().len() { - self.include(elem); - } - } - Move::Stay => {} - } - } - /// [Scope::tape] returns the [Tape] of the [Scope] - fn tape(&self) -> Tape; -} - -/// Simple trait for compatible symbols -pub trait Symbolic: - Clone + Default + Eq + Ord + std::fmt::Debug + std::fmt::Display + std::hash::Hash -{ -} - -impl Symbolic for T where - T: Clone + Default + Eq + Ord + std::fmt::Debug + std::fmt::Display + std::hash::Hash -{ -} - -/// [Translate] is a trait that allows for the translation of a machine's memory -pub trait Translate { - type Error; - - fn translate(&mut self, tape: Tape) -> Result, Self::Error>; -} - -/// [Turing] describes a programmable Turing machine -pub trait Turing: Translate { - type Scope: Scope; - - /// [Turing::execute] - fn execute(&mut self) -> Result<&Self, Self::Error>; - /// [Turing::execute_once] - fn execute_once(&mut self) -> Result<&Self, Self::Error>; - /// [Turing::execute_until] - fn execute_until(&mut self, until: impl Fn(&Self::Scope) -> bool) - -> Result<&Self, Self::Error>; -} - -/// [With] describes a simple means of concating several objects together -pub trait With { - /// [With::Output] must be a superposition of self and T - type Output; - - /// [With::with] accepts an owned instance of the given type and returns a [With::Output] instance - fn with(&self, other: &T) -> Self::Output; -} - -/// [TryWith] is a trait that describes a means of trying to concate several objects together -pub trait TryWith { - type Output; - type Error; - - fn try_with(&self, other: &T) -> Result; -} diff --git a/turing/src/tape.rs b/turing/src/tape.rs deleted file mode 100644 index 9c8d94b..0000000 --- a/turing/src/tape.rs +++ /dev/null @@ -1,117 +0,0 @@ -/* - Appellation: tape - Contrib: FL03 -*/ -//! # Tape -//! -//! The [Tape] represents the memory of a turing machine. -use super::Symbolic; -use contained_core::{ArrayLike, Insert, Iterable}; -use serde::{Deserialize, Serialize}; -use std::ops::{Index, IndexMut}; - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Tape(Vec); - -impl Tape { - pub fn new() -> Self { - Self(Default::default()) - } - /// Creates a new tape from an iterator; preserves the original order. - pub fn norm(iter: impl IntoIterator) -> Self { - Self::from_iter(iter) - } - /// Creates a new tape from an iterator; the tape is reversed. - pub fn std(iter: impl IntoIterator) -> Self { - let mut tape = Vec::from_iter(iter); - tape.reverse(); - Self::from_iter(tape.clone()) - } - /// Creates a new tape with the specified capacity. - pub fn with_capacity(capacity: usize) -> Self { - Self(Vec::with_capacity(capacity)) - } - pub fn write(&mut self, index: usize, symbol: S) { - self.0.insert(index, symbol) - } -} - -impl ArrayLike for Tape {} - -impl AsMut> for Tape { - fn as_mut(&mut self) -> &mut Vec { - self.0.as_mut() - } -} - -impl AsRef> for Tape { - fn as_ref(&self) -> &Vec { - self.0.as_ref() - } -} - -impl Extend for Tape { - fn extend>(&mut self, iter: T) { - self.as_mut().extend(iter); - } -} - -impl FromIterator for Tape { - fn from_iter>(iter: T) -> Self { - Self(Vec::from_iter(iter)) - } -} - -impl Index for Tape { - type Output = S; - - fn index(&self, index: usize) -> &Self::Output { - &self.0[index] - } -} - -impl IndexMut for Tape { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.0[index] - } -} - -impl Insert for Tape { - fn insert(&mut self, index: usize, elem: S) { - self.as_mut().insert(index, elem); - } -} - -impl Iterable for Tape {} - -impl IntoIterator for Tape { - type Item = S; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl From> for Vec { - fn from(tape: Tape) -> Vec { - tape.as_ref().clone() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_tape() { - let mut tape = Tape::new(); - assert!(tape.is_empty()); - tape.push("a"); - assert_eq!(tape.len(), 1); - assert_eq!(tape[0], "a"); - tape.append(&mut Tape::from_iter(vec!["b", "c"])); - assert_eq!(tape.len(), 3); - assert_eq!(tape[2], "c"); - } -} diff --git a/turing/src/utils.rs b/turing/src/utils.rs deleted file mode 100644 index 752daba..0000000 --- a/turing/src/utils.rs +++ /dev/null @@ -1,4 +0,0 @@ -/* - Appellation: utils - Contrib: FL03 -*/ diff --git a/turing/tests/machine.rs b/turing/tests/machine.rs deleted file mode 100644 index 9f86f69..0000000 --- a/turing/tests/machine.rs +++ /dev/null @@ -1,33 +0,0 @@ -use contained_core::prelude::State; -#[cfg(test)] -use contained_turing::{ - instructions::{Instruction, Move}, - machine::{Driver, Machine}, - Program, Tape, Turing, -}; - -pub const TEST_ALPHABET: [&str; 3] = ["a", "b", "c"]; - -#[test] -fn test_machine() { - let alphabet = vec!["a", "b", "c"]; - - let tape = alphabet.clone(); - let scope = Driver::from(Tape::norm(tape)); - - let instructions: Vec> = vec![ - (State::default(), "a", State::default(), "c", Move::Right).into(), - (State::default(), "b", State::default(), "a", Move::Right).into(), - (State::default(), "c", State::invalid(), "a", Move::Stay).into(), - ]; - - // Setup the program - let mut program = Program::new(alphabet, State::invalid()); - // Instruction set; turn ["a", "b", "c"] into ["c", "a", "a"] - program.extend(instructions); - - let mut machine = Machine::new(scope, program); - - assert!(machine.execute().is_ok()); - assert_eq!(machine.tape().clone(), Tape::norm(["c", "a", "a"])); -} diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml deleted file mode 100644 index fd93825..0000000 --- a/xtask/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -authors = ["FL03 (https://github.com/FL03)"] -categories = [] -default-run = "xtask" -description = "xtask" -edition = "2021" -license = "Apache-2.0" -name = "xtask-sdk" -version = "0.1.0" # TODO: Update the package version - -[features] -default = [] - -[lib] -crate-type = ["cdylib", "rlib"] -test = true - -[[bin]] -bench = false -name = "xtask" -test = false - -[build-dependencies] - -[dependencies] -anyhow = "1" -clap = { features = ["cargo", "derive", "env"], version = "4" } -config = "0.14" -devx-cmd = "0.5" -devx-pre-commit = "0.5" -duct = "0.13.6" -serde = { features = ["derive"], version = "1" } -serde_json = "1" -smart-default = "0.7" -strum = { features = ["derive"], version = "0.26" } -tracing = "0.1" -tracing-subscriber = "0.3" - -[dev-dependencies] - -[package.metadata.docs.rs] -all-features = true -rustc-args = ["--cfg", "docsrs"] - -[target.wasm32-unknown-unknown] - -[target.wasm32-wasi] \ No newline at end of file diff --git a/xtask/src/bin/xtask.rs b/xtask/src/bin/xtask.rs deleted file mode 100644 index bbbf221..0000000 --- a/xtask/src/bin/xtask.rs +++ /dev/null @@ -1,14 +0,0 @@ -/* - Appellation: xtask - Contrib: FL03 - Description: ... Summary ... -*/ -use xtask_sdk::Xtask; - -fn main() -> anyhow::Result<()> { - let xtask = Xtask::new(Default::default()); - xtask.init(); - xtask.handle_cli(Default::default())?; - - Ok(()) -} diff --git a/xtask/src/cli/args/auto.rs b/xtask/src/cli/args/auto.rs deleted file mode 100644 index 31c0cda..0000000 --- a/xtask/src/cli/args/auto.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - Appellation: auto - Contrib: FL03 - Description: ... Summary ... -*/ -use super::Build; -use crate::command; -use anyhow::Result; -use clap::Args; -use serde::{Deserialize, Serialize}; - - - -#[derive( - Args, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct Auto; - -impl Auto { - pub fn cargo() -> Result<()> { - command("cargo", vec!["fmt", "--all"])?; - command("cargo", vec!["clippy", "--all", "--allow-dirty", "--fix"])?; - command("cargo", vec!["build", "--workspace"])?; - command("cargo", vec!["test", "--all"])?; - Ok(()) - } -} - diff --git a/xtask/src/cli/args/build.rs b/xtask/src/cli/args/build.rs deleted file mode 100644 index a3cddb7..0000000 --- a/xtask/src/cli/args/build.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* - Appellation: build - Contrib: FL03 - Description: ... Summary ... -*/ -use crate::command; -use anyhow::Result; -use clap::Args; -use serde::{Deserialize, Serialize}; - -fn builder(release: bool, workspace: bool) -> Result<()> { - let mut args = vec!["build"]; - - if release { - args.push("--release"); - } - if workspace { - args.push("--workspace"); - } - command("cargo", args)?; - Ok(()) -} - -#[derive( - Args, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct Build { - /// Build a specific target - #[clap(long, short)] - pub package: Option, -} - -impl Build { - pub fn cargo(&self, release: bool, workspace: bool) -> Vec<&str> { - let mut args = vec!["build"]; - if let Some(pkg) = self.package.clone() { - // args.push("-p"); - // args.push(&*pkg); - } else if workspace { - args.push("--workspace"); - } - if release { - args.push("--release"); - } - args - } -} \ No newline at end of file diff --git a/xtask/src/cli/args/mod.rs b/xtask/src/cli/args/mod.rs deleted file mode 100644 index 6a9f721..0000000 --- a/xtask/src/cli/args/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - Appellation: args - Contrib: FL03 - Description: ... Summary ... -*/ -pub use self::{auto::*, build::*, setup::*, test::*}; - -mod auto; -mod build; -mod setup; -mod test; - -use clap::{Args, ArgAction}; -use serde::{Deserialize, Serialize}; - -#[derive( - Args, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] - -pub struct Cargo { - - #[clap(long, short)] - pub package: Option, -} diff --git a/xtask/src/cli/args/setup.rs b/xtask/src/cli/args/setup.rs deleted file mode 100644 index e54b25e..0000000 --- a/xtask/src/cli/args/setup.rs +++ /dev/null @@ -1,92 +0,0 @@ -/* - Appellation: setup - Contrib: FL03 - Description: ... Summary ... -*/ -use anyhow::Result; -use crate::{command, dist_dir, rustup,}; -use clap::{Args, ArgAction, ValueEnum}; -use serde::{Deserialize, Serialize}; -use strum::{Display, EnumString, EnumVariantNames}; - -#[derive( - Clone, Debug, Default, Deserialize, Display, EnumString, EnumVariantNames, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, ValueEnum -)] -#[strum(serialize_all = "kebab-case")] -pub enum PlatformOpts { - #[default] - Linux, - MacOSX, - Windows, -} - -#[derive( - Args, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct Setup { - /// Quickly setup a development environment - #[arg(action = ArgAction::SetTrue, long, short)] - pub extras: bool, - /// Choose a platform to setup - #[clap(long, short)] - pub platform: PlatformOpts, - /// Setup the workspace for WebAssembly workflows - #[arg(action = ArgAction::SetTrue, long, short)] - pub wasm: bool -} - -impl Setup { - pub fn new(platform: PlatformOpts) -> Self { - Self { - extras: false, - platform, - wasm: false - } - } - pub fn clear(&self) -> Result<()> { - if std::fs::create_dir_all(dist_dir()).is_err() { - tracing::info!("Clearing out the previous build"); - std::fs::remove_dir_all(dist_dir())?; - std::fs::create_dir_all(dist_dir())?; - }; - Ok(()) - } - pub fn setup(&self) -> Result<()> { - self.clear()?; - - rustup(vec!["install", "nightly"])?; - - if self.wasm { - rustup(vec!["default", "nightly"])?; - rustup(vec![ - "target", - "add", - "wasm32-unknown-unknown", - "wasm32-wasi", - "--toolchain", - "nightly", - ])?; - command("npm", vec!["install", "-g", "wasm-pack"])?; - if self.extras { - rustup(vec![ - "component", - "add", - "clippy", - "rustfmt", - "--toolchain", - "nightly", - ])?; - }; - } - - Ok(()) - } - pub fn toggle_extras(mut self, extras: bool) -> Self { - self.extras = extras; - self - } - pub fn toggle_wasm(mut self, wasm: bool) -> Self { - self.wasm = wasm; - self - } -} \ No newline at end of file diff --git a/xtask/src/cli/args/test.rs b/xtask/src/cli/args/test.rs deleted file mode 100644 index c46a233..0000000 --- a/xtask/src/cli/args/test.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - Appellation: build - Contrib: FL03 - Description: ... Summary ... -*/ -use crate::command; -use anyhow::Result; -use clap::Args; -use serde::{Deserialize, Serialize}; - - - -#[derive( - Args, Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, -)] -pub struct Test { - #[clap(long, short)] - pub package: Option, -} - diff --git a/xtask/src/cli/mod.rs b/xtask/src/cli/mod.rs deleted file mode 100644 index bfeeaa1..0000000 --- a/xtask/src/cli/mod.rs +++ /dev/null @@ -1,57 +0,0 @@ -/* - Appellation: cli - Contrib: FL03 - Description: ... Summary ... -*/ -pub use self::opts::*; - -pub(crate) mod opts; - -pub mod args; - -use clap::{ArgAction, Parser}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, Parser, PartialEq, PartialOrd, Serialize)] -#[clap(about, author, long_about = None, version)] -#[command(arg_required_else_help(true), allow_missing_positional(true))] -pub struct CommandLineInterface { - /// Network specific commands - #[clap(subcommand)] - pub cmd: Option, - /// Optionally execute the program in production mode - #[arg(action = ArgAction::SetTrue, long, short)] - pub release: bool, - /// Startup - #[arg(action = ArgAction::SetTrue, long, short)] - pub up: bool, - /// Signal for more in-depth logging - #[arg(action = ArgAction::SetTrue, long, short)] - pub verbose: bool, - /// Build all - #[arg(action = ArgAction::SetTrue, long, short)] - pub workspace: bool, -} - -impl CommandLineInterface { - pub fn cmd(&self) -> &Option { - &self.cmd - } - pub fn release(&self) -> bool { - self.release - } - pub fn up(&self) -> bool { - self.up - } - pub fn verbose(&self) -> bool { - self.verbose - } - pub fn workspace(&self) -> bool { - self.workspace - } -} -impl Default for CommandLineInterface { - fn default() -> Self { - Self::parse() - } -} \ No newline at end of file diff --git a/xtask/src/cli/opts.rs b/xtask/src/cli/opts.rs deleted file mode 100644 index 600e502..0000000 --- a/xtask/src/cli/opts.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - Appellation: opts - Contrib: FL03 - Description: ... Summary ... -*/ -use super::args::{Build, Setup}; -use clap::Subcommand; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Subcommand)] -pub enum Opts { - Auto, - Build(Build), - Setup(Setup), - Test { - #[clap(long, short)] - package: Option - } -} - diff --git a/xtask/src/commands.rs b/xtask/src/commands.rs deleted file mode 100644 index 50b82d4..0000000 --- a/xtask/src/commands.rs +++ /dev/null @@ -1,57 +0,0 @@ -/* - Appellation: commands - Contrib: FL03 - Description: ... Summary ... -*/ -use crate::{command, dist_dir, rustup}; -use anyhow::Result; - - - -pub fn builder(release: bool, workspace: bool) -> Result<()> { - let mut args = vec!["build"]; - - if release { - args.push("--release"); - } - if workspace { - args.push("--workspace"); - } - command("cargo", args)?; - Ok(()) -} - -pub fn setup(extras: bool, wasm: bool) -> Result<()> { - if std::fs::create_dir_all(dist_dir()).is_err() { - tracing::info!("Clearing out the previous build"); - std::fs::remove_dir_all(dist_dir())?; - std::fs::create_dir_all(dist_dir())?; - }; - - rustup(vec!["install", "nightly"])?; - - if wasm { - rustup(vec!["default", "nightly"])?; - rustup(vec![ - "target", - "add", - "wasm32-unknown-unknown", - "wasm32-wasi", - "--toolchain", - "nightly", - ])?; - command("npm", vec!["install", "-g", "wasm-pack"])?; - if extras { - rustup(vec![ - "component", - "add", - "clippy", - "rustfmt", - "--toolchain", - "nightly", - ])?; - }; - } - - Ok(()) -} diff --git a/xtask/src/context.rs b/xtask/src/context.rs deleted file mode 100644 index 5f3d5a9..0000000 --- a/xtask/src/context.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* - Appellation: context - Contrib: FL03 - Description: ... Summary ... -*/ -use serde::{Deserialize, Serialize}; - - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Context { - -} - -impl Context { - pub fn name(self) -> String { - env!("CARGO_PKG_NAME").to_string() - } -} diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs deleted file mode 100644 index 432d4ec..0000000 --- a/xtask/src/lib.rs +++ /dev/null @@ -1,100 +0,0 @@ -/* - Appellation: xtask - Contrib: FL03 - Description: ... Summary ... -*/ -pub use self::{commands::*, context::*, primitives::*, utils::*}; - -mod commands; -mod context; -mod utils; - -pub mod cli; - -use cli::{CommandLineInterface, Opts}; -use anyhow::Result; -use serde::{Deserialize, Serialize}; - -/// -#[macro_export] -macro_rules! cmd { - ($( - $x:expr; - [ $( $y:expr ),* ] - );*) => { - { - $( - let mut cmd = std::process::Command::new($x); - cmd.current_dir(project_root()); - let mut tmp = Vec::new(); - $( - tmp.push($y); - )* - cmd.args(tmp.as_slice()).status().expect(""); - )* - } - }; -} - - -#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Xtask { - ctx: Context -} - -impl Xtask { - pub fn new(ctx: Context) -> Self { - Self { ctx } - } - pub fn handle_cli(&self, cli: CommandLineInterface) -> Result<()> { - let release = cli.release(); - let workspace = cli.workspace(); - if let Some(opts) = cli.cmd().clone() { - match opts { - Opts::Auto => { - tracing::info!("Initializing the automatic pipeline"); - command("cargo", vec!["fmt", "--all"])?; - command("cargo", vec!["clippy", "--all", "--allow-dirty", "--fix"])?; - command("cargo", vec!["build", "--workspace"])?; - command("cargo", vec!["test", "--all", "--allow-dirty"])?; - }, - Opts::Build(_build) => { - tracing::info!("Building the target..."); - let mut args = vec!["build"]; - - if release { - args.push("--release"); - } - if workspace { - args.push("--workspace"); - } - command("cargo", args)?; - }, - Opts::Setup(_setup) => { - tracing::info!("Setting up the workspace"); - setup(true, false)?; - }, - Opts::Test { .. } => { - tracing::info!("Testing the target(s)"); - } - } - } - Ok(()) - } - pub fn init(&self) { - tracing_subscriber::fmt::init(); - } - pub async fn run(&self) -> Result<()> { - self.init(); - self.handle_cli(Default::default())?; - - Ok(()) - } -} - - - -mod primitives { - /// - pub type Bundle = std::collections::HashMap>>; -} diff --git a/xtask/src/utils.rs b/xtask/src/utils.rs deleted file mode 100644 index ca8bbbf..0000000 --- a/xtask/src/utils.rs +++ /dev/null @@ -1,83 +0,0 @@ -/* - Appellation: utils - Contrib: FL03 - Description: ... Summary ... -*/ -use anyhow::Result; -use std::path::{Path, PathBuf}; -use std::{collections::HashMap, fs, io, process::Command}; - -/// -pub fn command(program: &str, args: Vec<&str>) -> Result<()> { - let mut cmd = Command::new(program); - cmd.current_dir(project_root()); - cmd.args(args.as_slice()).status()?; - Ok(()) -} -/// -pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { - fs::create_dir_all(&dst)?; - for entry in fs::read_dir(src)? { - let entry = entry?; - let ty = entry.file_type()?; - if ty.is_dir() { - copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?; - } else { - fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?; - } - } - Ok(()) -} -/// -pub fn dist_dir() -> PathBuf { - project_root().join(".artifacts/dist") -} -/// -pub fn execute_bundle(bundle: HashMap<&str, Vec>>) -> Result<()> { - for k in bundle.keys() { - // Step 1: Rustup - for i in 0..bundle[k].len() { - let mut cmd = Command::new(k); - cmd.current_dir(project_root()); - cmd.args(bundle[k][i].clone().as_slice()).status()?; - } - } - Ok(()) -} - -pub fn rustup(args: Vec<&str>) -> Result<()> { - command("rustup", args) -} -/// Fetch the project root unless specified otherwise with a CARGO_MANIFEST_DIR env variable -pub fn project_root() -> PathBuf { - Path::new(&env!("CARGO_MANIFEST_DIR")) - .ancestors() - .nth(1) - .unwrap() - .to_path_buf() -} - -pub struct Cargo { - queue: Vec<(String, Vec)>, -} - -impl Cargo { - pub fn new() -> Self { - Self { - queue: Vec::new(), - } - } - pub fn add(&mut self, command: String, args: Vec) { - self.queue.push((command, args)); - } - pub fn run(&self) -> Result<()> { - for (cmd, args) in self.queue.clone() { - let mut command = Command::new("cargo"); - command.current_dir(project_root()); - command.arg(cmd); - command.args(args); - command.status()?; - } - Ok(()) - } -} \ No newline at end of file