From a015ea84fa6aafdd9aaa73f8e589154ba83df828 Mon Sep 17 00:00:00 2001 From: Pawel Rutka Date: Tue, 19 Aug 2025 14:36:55 +0200 Subject: [PATCH 1/4] repo: setup according to S-CORE template Signed-off-by: Pawel Rutka --- .github/CODEOWNERS | 15 +++ .github/ISSUE_TEMPLATE/bug_fix.md | 13 +++ .github/ISSUE_TEMPLATE/improvement.md | 11 ++ .github/PULL_REQUEST_TEMPLATE/bug_fix.md | 19 +++ .github/PULL_REQUEST_TEMPLATE/improvement.md | 19 +++ .github/actions/gitlint/action.yml | 42 +++++++ .github/workflows/copyright.yml | 24 ++++ .github/workflows/docs-cleanup.yml | 29 +++++ .github/workflows/docs.yml | 43 +++++++ .github/workflows/format.yml | 26 +++++ .github/workflows/gitlint.yml | 31 +++++ .github/workflows/license_check.yml | 32 +++++ BUILD | 50 ++++++++ CONTRIBUTION.md | 35 ++++++ MODULE.bazel | 65 +++++++++++ README.md | 116 ++++++++++++++++++- docs/conf.py | 56 +++++++++ docs/index.rst | 81 +++++++++++++ examples/BUILD | 8 ++ examples/Cargo.lock | 18 +++ project_config.bzl | 5 + pyproject.toml | 9 ++ src/BUILD | 0 tests/cpp/BUILD | 19 +++ tests/cpp/test_main.cpp | 41 +++++++ tests/rust/BUILD | 18 +++ tests/rust/test_main.rs | 4 + 27 files changed, 827 insertions(+), 2 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_fix.md create mode 100644 .github/ISSUE_TEMPLATE/improvement.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/bug_fix.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/improvement.md create mode 100644 .github/actions/gitlint/action.yml create mode 100644 .github/workflows/copyright.yml create mode 100644 .github/workflows/docs-cleanup.yml create mode 100644 .github/workflows/docs.yml create mode 100644 .github/workflows/format.yml create mode 100644 .github/workflows/gitlint.yml create mode 100644 .github/workflows/license_check.yml create mode 100644 BUILD create mode 100644 CONTRIBUTION.md create mode 100644 MODULE.bazel create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 examples/BUILD create mode 100644 examples/Cargo.lock create mode 100644 project_config.bzl create mode 100644 pyproject.toml create mode 100644 src/BUILD create mode 100644 tests/cpp/BUILD create mode 100644 tests/cpp/test_main.cpp create mode 100644 tests/rust/BUILD create mode 100644 tests/rust/test_main.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..ff063c4 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,15 @@ +# Comment out as not in score yet +# * @eclipse-score/infrastructure-tooling-community +# .* @eclipse-score/infrastructure-tooling-community +# .github/CODEOWNERS @eclipse-score/technical-lead + +# in separate repositories: +# +# /docs @eclipse-score/process-community +# /docs/manual @eclipse-score//safety-manager +# /docs/release @eclipse-score//quality-manager @eclipse-score//module-lead +# /docs/safety_plan @eclipse-score//safety-manager @eclipse-score//module-lead +# /docs/safety_analysis @eclipse-score//safety-manager +# /docs/verification @eclipse-score//quality-manager @eclipse-score//safety-manager +# /components @eclipse-score//technical-lead +# /components/*/ @eclipse-score//automotive-score-committers diff --git a/.github/ISSUE_TEMPLATE/bug_fix.md b/.github/ISSUE_TEMPLATE/bug_fix.md new file mode 100644 index 0000000..43ba081 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_fix.md @@ -0,0 +1,13 @@ +--- +name: Bugfix +about: 'Issue to track a bugfix' +title: 'Bugfix: Your bugfix title' +labels: 'codeowner_review' +assignees: '' + +--- + +> [!IMPORTANT] +> Make sure to link this issue with the PR for your bugfix. + + diff --git a/.github/ISSUE_TEMPLATE/improvement.md b/.github/ISSUE_TEMPLATE/improvement.md new file mode 100644 index 0000000..fd2c171 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/improvement.md @@ -0,0 +1,11 @@ +--- +name: Improvement +about: 'Issue to track a improvement' +title: 'Improvement: Your improvement title' +labels: 'codeowner_review' +assignees: '' + +--- + +> [!IMPORTANT] +> Make sure to link this issue with the PR for your improvement. diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_fix.md b/.github/PULL_REQUEST_TEMPLATE/bug_fix.md new file mode 100644 index 0000000..8341f51 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/bug_fix.md @@ -0,0 +1,19 @@ +# Bugfix + +> [!IMPORTANT] +> Use this template only for bugfixes that do not influence topics covered by contribution requests or improvements. + +> [!CAUTION] +> Make sure to submit your pull-request as **Draft** until you are ready to have it reviewed by the Committers. + +## Description + +[A short description of the bug being fixed by the contribution.] + +## Related ticket + +> [!IMPORTANT] +> Please replace `[ISSUE-NUMBER]` with the issue-number that tracks this bug fix. If there is no such +> ticket yet, create one via [this issue template](../ISSUE_TEMPLATE/new?template=bug_fix.md). + +closes [ISSUE-NUMBER] (bugfix ticket) diff --git a/.github/PULL_REQUEST_TEMPLATE/improvement.md b/.github/PULL_REQUEST_TEMPLATE/improvement.md new file mode 100644 index 0000000..090ad43 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/improvement.md @@ -0,0 +1,19 @@ +# Improvement + +> [!IMPORTANT] +> Use this template only for improvement that do not influence topics covered by contribution requests or bug fixes. + +> [!CAUTION] +> Make sure to submit your pull-request as **Draft** until you are ready to have it reviewed by the Committers. + +## Description + +[A short description of the improvement being addressed by the contribution.] + +## Related ticket + +> [!IMPORTANT] +> Please replace `[ISSUE-NUMBER]` with the issue-number that tracks this bug fix. If there is no such +> ticket yet, create one via [this issue template](../ISSUE_TEMPLATE/new?template=improvement.md). + +closes [ISSUE-NUMBER] (improvement ticket) diff --git a/.github/actions/gitlint/action.yml b/.github/actions/gitlint/action.yml new file mode 100644 index 0000000..5f0417b --- /dev/null +++ b/.github/actions/gitlint/action.yml @@ -0,0 +1,42 @@ +# ******************************************************************************* +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +name: "Gitlint Action" +description: "An action to install and run Gitlint on PR commits" +inputs: + pr-number: + description: "Pull Request number used to fetch commits" + required: true + base-branch: + description: "Base branch to compare commits against (default: origin/main)" + default: "origin/main" + required: false +runs: + using: "docker" + image: "jorisroovers/gitlint:0.19.1" + entrypoint: /bin/sh + args: + - -c + - | + git config --global --add safe.directory /github/workspace && \ + git fetch origin +refs/heads/main:refs/remotes/origin/main && \ + git fetch origin +refs/pull/${{ inputs.pr-number }}/head && \ + if ! gitlint --commits ${{ inputs.base-branch }}..HEAD; then \ + echo -e "\nWARNING: Your commit message does not follow the required format." && \ + echo "Formatting rules: https://eclipse-score.github.io/score/process/guidance/git/index.html" && \ + echo -e "To fix your commit message, run:\n" && \ + echo " git commit --amend" && \ + echo "Then update your commit (fix gitlint warnings). Finally, force-push:" && \ + echo " git push --force-with-lease" && \ + exit 1; \ + fi diff --git a/.github/workflows/copyright.yml b/.github/workflows/copyright.yml new file mode 100644 index 0000000..08ef376 --- /dev/null +++ b/.github/workflows/copyright.yml @@ -0,0 +1,24 @@ +# ******************************************************************************* +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +name: Copyright checks +on: + pull_request: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] +jobs: + copyright-check: + uses: eclipse-score/cicd-workflows/.github/workflows/copyright.yml@main + with: + bazel-target: "run //:copyright.check" diff --git a/.github/workflows/docs-cleanup.yml b/.github/workflows/docs-cleanup.yml new file mode 100644 index 0000000..cfa4ae2 --- /dev/null +++ b/.github/workflows/docs-cleanup.yml @@ -0,0 +1,29 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +name: Documentation Cleanup + +permissions: + contents: write + pages: write + id-token: write + +on: + schedule: + - cron: '0 0 * * *' # Runs every day at midnight UTC + +jobs: + docs-cleanup: + uses: eclipse-score/cicd-workflows/.github/workflows/docs-cleanup.yml@main + secrets: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..24bd399 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,43 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +name: Documentation + +permissions: + contents: write + pages: write + pull-requests: write + id-token: write + +on: + pull_request_target: + types: [opened, reopened, synchronize] # Allows forks to trigger the docs build + push: + branches: + - main + merge_group: + types: [checks_requested] + +jobs: + build-docs: + uses: eclipse-score/cicd-workflows/.github/workflows/docs.yml@main + permissions: + contents: write + pages: write + pull-requests: write + id-token: write + + with: + # the bazel-target depends on your repo specific docs_targets configuration (e.g. "suffix") + bazel-target: "//:docs -- --github_user=${{ github.repository_owner }} --github_repo=${{ github.event.repository.name }}" + retention-days: 3 diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 0000000..620d697 --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,26 @@ +# ******************************************************************************* +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +name: Formatting checks + +on: + pull_request: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] + +jobs: + formatting-check: + uses: eclipse-score/cicd-workflows/.github/workflows/format.yml@main + with: + bazel-target: "test //:format.check" # optional, this is the default diff --git a/.github/workflows/gitlint.yml b/.github/workflows/gitlint.yml new file mode 100644 index 0000000..90487ed --- /dev/null +++ b/.github/workflows/gitlint.yml @@ -0,0 +1,31 @@ +# ******************************************************************************* +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +name: Gitlint check +on: + pull_request: + types: [opened, synchronize, reopened] +jobs: + lint-commits: + name: check-commit-messages + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Run Gitlint Action + if: ${{ github.event_name == 'pull_request' }} + uses: ./.github/actions/gitlint + with: + pr-number: ${{ github.event.number }} diff --git a/.github/workflows/license_check.yml b/.github/workflows/license_check.yml new file mode 100644 index 0000000..aba7f99 --- /dev/null +++ b/.github/workflows/license_check.yml @@ -0,0 +1,32 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +name: License check preparation +on: + pull_request_target: + types: [opened, reopened, synchronize] + merge_group: + types: [checks_requested] + +permissions: + pull-requests: write + issues: write + + +jobs: + license-check: + uses: eclipse-score/cicd-workflows/.github/workflows/license-check.yml@main + with: + repo-url: "${{ github.server_url }}/${{ github.repository }}" + secrets: + dash-api-token: ${{ secrets.ECLIPSE_GITLAB_API_TOKEN }} diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..1d9e33c --- /dev/null +++ b/BUILD @@ -0,0 +1,50 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +load("@score_cr_checker//:cr_checker.bzl", "copyright_checker") +load("@score_dash_license_checker//:dash.bzl", "dash_license_checker") +load("@score_docs_as_code//:docs.bzl", "docs") +load("@score_format_checker//:macros.bzl", "use_format_targets") +load("@score_starpls_lsp//:starpls.bzl", "setup_starpls") +load("//:project_config.bzl", "PROJECT_CONFIG") + +setup_starpls( + name = "starpls_server", + visibility = ["//visibility:public"], +) + +copyright_checker( + name = "copyright", + srcs = [ + "src", + "tests", + "//:BUILD", + "//:MODULE.bazel", + ], + config = "@score_cr_checker//resources:config", + template = "@score_cr_checker//resources:templates", + visibility = ["//visibility:public"], +) + +dash_license_checker( + src = "//examples:cargo_lock", + file_type = "", # let it auto-detect based on project_config + project_config = PROJECT_CONFIG, + visibility = ["//visibility:public"], +) + +# Add target for formatting checks +use_format_targets() + +docs( + source_dir = "docs", +) diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md new file mode 100644 index 0000000..dcc54e6 --- /dev/null +++ b/CONTRIBUTION.md @@ -0,0 +1,35 @@ +# Eclipse Safe Open Vehicle Core (SCORE) +The [Eclipse Safe Open Vehicle Core](https://projects.eclipse.org/projects/automotive.score) project aims to develop an open-source core stack for Software Defined Vehicles (SDVs), specifically targeting embedded high-performance Electronic Control Units (ECUs). +Please check the [documentation](https://eclipse-score.github.io) for more information. +The source code is hosted at [GitHub](https://github.com/eclipse-score). + +The communication mainly takes place via the [`score-dev` mailing list](https://accounts.eclipse.org/mailing-list/score-dev) and GitHub issues & pull requests (PR). And we have a chatroom for community discussions here [Eclipse SCORE chatroom](https://chat.eclipse.org/#/room/#automotive.score:matrix.eclipse.org). + +Please note that for the project the [Eclipse Foundationโ€™s Terms of Use](https://www.eclipse.org/legal/terms-of-use/) apply. +In addition, you need to sign the [ECA](https://www.eclipse.org/legal/ECA.php) and the [DCO](https://www.eclipse.org/legal/dco/) to contribute to the project. + +## Contributing +### Getting the source code & building the project +Please refer to the [README.md](README.md) for further information. + +### Getting involved + +#### Setup Phase +This phase is part of the eclipse Incubation Phase and shall establish all the processes needed for a safe development of functions. Only after this phase it will be possible to contribute code to the project. As the development in this project is driven by requirements, the processes and needed infrastructure incl. tooling will be established based on non-functional Stakeholder_Requirements. During setup phase the contributions are Bug Fixes and Improvements (both on processes and infrastructure). + +#### Bug Fixes and Improvements +Improvements are adding/changing processes and infrastructure, bug fixes can be also on development work products like code. +In case you want to fix a bug or contribute an improvement, please perform the following steps: +1) Create a PR by using the corresponding template ([Bugfix PR template](.github/PULL_REQUEST_TEMPLATE/bug_fix.md) or [Improvement PR template](.github/PULL_REQUEST_TEMPLATE/improvement.md)). Please mark your PR as draft until it's ready for review by the Committers (see the [Eclipse Foundation Project Handbook](https://www.eclipse.org/projects/handbook/#contributing-committers) for more information on the role definitions). Improvements are requested by the definition or modification of [Stakeholder Requirements](docs/stakeholder_requirements) or [Tool Requirements](docs/tool_requirements) and may be implemented after acceptance/merge of the request by a second Improvement PR. The needed reviews are automatically triggered via the [CODEOWNERS](.github/CODEOWNERS) file in the repository. +2) Initiate content review by opening a corresponding issue for the PR when it is ready for review. Review of the PR and final merge into the project repository is in responsibility of the Committers. Use the [Bugfix Issue template](.github/ISSUE_TEMPLATE/bug_fix.md) or [Improvement Issue template](.github/ISSUE_TEMPLATE/improvement.md) for this. + +Please check here for our Git Commit Rules in the [Configuration_Tool_Guidelines](https://eclipse-score.github.io/score/process_description/guidelines/index.html). + +Please use the [Stakeholder and Tool Requirements Template](https://eclipse-score.github.io/score/process_description/templates/index.html) when defining these requirements. + +![Contribution guide workflow](./docs/_assets/contribution_guide.svg "Contribution guide workflow") + +#### Additional Information +Please note, that all Git commit messages must adhere the rules described in the [Eclipse Foundation Project Handbook](https://www.eclipse.org/projects/handbook/#resources-commit). + +Please find process descriptions here: [process description](https://eclipse-score.github.io/score/process_description/). diff --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..44009a0 --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,65 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +module( + name = "cpp_rust_template_repository", + version = "1.0", +) + +bazel_dep(name = "rules_python", version = "1.4.1") + +PYTHON_VERSION = "3.12" + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +python.toolchain( + is_default = True, + python_version = PYTHON_VERSION, +) +use_repo(python) + +# Add GoogleTest dependency +bazel_dep(name = "googletest", version = "1.14.0") + +# Rust rules for Bazel +bazel_dep(name = "rules_rust", version = "0.56.0") + +# Checker rule for CopyRight checks/fixs +bazel_dep(name = "score_cr_checker", version = "0.3.1") +bazel_dep(name = "score_python_basics", version = "0.3.4") +bazel_dep(name = "score_starpls_lsp", version = "0.1.0") + +# C/C++ rules for Bazel +bazel_dep(name = "rules_cc", version = "0.1.1") + +# LLVM Toolchains Rules - host configuration +bazel_dep(name = "toolchains_llvm", version = "1.2.0") + +llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm") +llvm.toolchain( + cxx_standard = {"": "c++17"}, + llvm_version = "19.1.0", +) +use_repo(llvm, "llvm_toolchain") +use_repo(llvm, "llvm_toolchain_llvm") + +register_toolchains("@llvm_toolchain//:all") + +# Dash license checker +bazel_dep(name = "score_dash_license_checker", version = "0.1.2") + +# Format checker +bazel_dep(name = "score_format_checker", version = "0.1.1") +bazel_dep(name = "aspect_rules_lint", version = "1.4.4") +bazel_dep(name = "buildifier_prebuilt", version = "8.2.0.2") + +#docs-as-code +bazel_dep(name = "score_docs_as_code", version = "1.0.1") diff --git a/README.md b/README.md index f9db4f1..1e8af75 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,114 @@ -# inc_mw_log -Incubation repository for logging framework + +# C++ & Rust Bazel Template Repository + +This repository serves as a **template** for setting up **C++ and Rust projects** using **Bazel**. +It provides a **standardized project structure**, ensuring best practices for: + +- **Build configuration** with Bazel. +- **Testing** (unit and integration tests). +- **Documentation** setup. +- **CI/CD workflows**. +- **Development environment** configuration. + +--- + +## ๐Ÿ“‚ Project Structure + +| File/Folder | Description | +| ----------------------------------- | ------------------------------------------------- | +| `README.md` | Short description & build instructions | +| `src/` | Source files for the module | +| `tests/` | Unit tests (UT) and integration tests (IT) | +| `examples/` | Example files used for guidance | +| `docs/` | Documentation (Doxygen for C++ / mdBook for Rust) | +| `.github/workflows/` | CI/CD pipelines | +| `.vscode/` | Recommended VS Code settings | +| `.bazelrc`, `MODULE.bazel`, `BUILD` | Bazel configuration & settings | +| `project_config.bzl` | Project-specific metadata for Bazel macros | +| `LICENSE.md` | Licensing information | +| `CONTRIBUTION.md` | Contribution guidelines | + +--- + +## ๐Ÿš€ Getting Started + +### 1๏ธโƒฃ Clone the Repository + +```sh +git clone https://github.com/eclipse-score/YOUR_PROJECT.git +cd YOUR_PROJECT +``` + +### 2๏ธโƒฃ Build the Examples of module + +> DISCLAIMER: Depending what module implements, it's possible that different +> configuration flags needs to be set on command line. + +To build all targets of the module the following command can be used: + +```sh +bazel build //src/... +``` + +This command will instruct Bazel to build all targets that are under Bazel +package `src/`. The ideal solution is to provide single target that builds +artifacts, for example: + +```sh +bazel build //src/:release_artifacts +``` + +where `:release_artifacts` is filegroup target that collects all release +artifacts of the module. + +> NOTE: This is just proposal, the final decision is on module maintainer how +> the module code needs to be built. + +### 3๏ธโƒฃ Run Tests + +```sh +bazel test //tests/... +``` + +--- + +## ๐Ÿ›  Tools & Linters + +The template integrates **tools and linters** from **centralized repositories** to ensure consistency across projects. + +- **C++:** `clang-tidy`, `cppcheck`, `Google Test` +- **Rust:** `clippy`, `rustfmt`, `Rust Unit Tests` +- **CI/CD:** GitHub Actions for automated builds and tests + +--- + +## ๐Ÿ“– Documentation + +- A **centralized docs structure** is planned. + +--- + +## โš™๏ธ `project_config.bzl` + +This file defines project-specific metadata used by Bazel macros, such as `dash_license_checker`. + +### ๐Ÿ“Œ Purpose + +It provides structured configuration that helps determine behavior such as: + +- Source language type (used to determine license check file format) +- Safety level or other compliance info (e.g. ASIL level) + +### ๐Ÿ“„ Example Content + +```python +PROJECT_CONFIG = { + "asil_level": "QM", # or "ASIL-A", "ASIL-B", etc. + "source_code": ["cpp", "rust"] # Languages used in the module +} +``` + +### ๐Ÿ”ง Use Case + +When used with macros like `dash_license_checker`, it allows dynamic selection of file types + (e.g., `cargo`, `requirements`) based on the languages declared in `source_code`. diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..cf13475 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,56 @@ +# ******************************************************************************* +# Copyright (c) 2024 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = "Module Template Project" +project_url = "https://eclipse-score.github.io/module_template/" +project_prefix = "MODULE_TEMPLATE_" +author = "S-CORE" +version = "0.1" + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + + +extensions = [ + "sphinx_design", + "sphinx_needs", + "sphinxcontrib.plantuml", + "score_plantuml", + "score_metamodel", + "score_draw_uml_funcs", + "score_source_code_linker", + "score_layout", +] + +exclude_patterns = [ + # The following entries are not required when building the documentation via 'bazel + # build //docs:docs', as that command runs in a sandboxed environment. However, when + # building the documentation via 'bazel run //docs:incremental' or esbonio, these + # entries are required to prevent the build from failing. + "bazel-*", + ".venv_docs", +] + +templates_path = ["templates"] + +# Enable numref +numfig = True diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..32332d4 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,81 @@ +.. + # ******************************************************************************* + # Copyright (c) 2024 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Module Template Documentation +============================= + +This documentation describes the structure, usage and configuration of the Bazel-based C++/Rust module template. + +.. contents:: Table of Contents + :depth: 2 + :local: + +Overview +-------- + +This repository provides a standardized setup for projects using **C++** or **Rust** and **Bazel** as a build system. +It integrates best practices for build, test, CI/CD and documentation. + +Requirements +------------ + +.. stkh_req:: Example Functional Requirement + :id: stkh_req__docgen_enabled__example + :status: valid + :safety: QM + :reqtype: Functional + :rationale: Ensure documentation builds are possible for all modules + + +Project Layout +-------------- + +The module template includes the following top-level structure: + +- `src/`: Main C++/Rust sources +- `tests/`: Unit and integration tests +- `examples/`: Usage examples +- `docs/`: Documentation using `docs-as-code` +- `.github/workflows/`: CI/CD pipelines + +Quick Start +----------- + +To build the module: + +.. code-block:: bash + + bazel build //src/... + +To run tests: + +.. code-block:: bash + + bazel test //tests/... + +Configuration +------------- + +The `project_config.bzl` file defines metadata used by Bazel macros. + +Example: + +.. code-block:: python + + PROJECT_CONFIG = { + "asil_level": "QM", + "source_code": ["cpp", "rust"] + } + +This enables conditional behavior (e.g., choosing `clang-tidy` for C++ or `clippy` for Rust). diff --git a/examples/BUILD b/examples/BUILD new file mode 100644 index 0000000..012dd54 --- /dev/null +++ b/examples/BUILD @@ -0,0 +1,8 @@ +# Needed for Dash tool to check python dependency licenses. +filegroup( + name = "cargo_lock", + srcs = [ + "Cargo.lock", + ], + visibility = ["//visibility:public"], +) diff --git a/examples/Cargo.lock b/examples/Cargo.lock new file mode 100644 index 0000000..cd1fbfa --- /dev/null +++ b/examples/Cargo.lock @@ -0,0 +1,18 @@ +# 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 = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" diff --git a/project_config.bzl b/project_config.bzl new file mode 100644 index 0000000..f764a1d --- /dev/null +++ b/project_config.bzl @@ -0,0 +1,5 @@ +# project_config.bzl +PROJECT_CONFIG = { + "asil_level": "QM", + "source_code": ["rust"], +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3c9144d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,9 @@ +[tool.basedpyright] +extends = "bazel-bin/ide_support.runfiles/score_python_basics~/pyproject.toml" +verboseOutput = true + +exclude = [ + "**/__pycache__", + "**/.*", + "**/bazel-*", +] diff --git a/src/BUILD b/src/BUILD new file mode 100644 index 0000000..e69de29 diff --git a/tests/cpp/BUILD b/tests/cpp/BUILD new file mode 100644 index 0000000..e3b354e --- /dev/null +++ b/tests/cpp/BUILD @@ -0,0 +1,19 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +cc_test( + name = "cpp_test_main", + srcs = ["test_main.cpp"], + deps = [ + "@googletest//:gtest_main", # GoogleTest dependency via Bazel Modules + ], +) diff --git a/tests/cpp/test_main.cpp b/tests/cpp/test_main.cpp new file mode 100644 index 0000000..f523107 --- /dev/null +++ b/tests/cpp/test_main.cpp @@ -0,0 +1,41 @@ +/******************************************************************************** +* Copyright (c) 2025 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ +#include + +// Function to be tested +int add(int a, int b) { + return a + b; +} + +// Test case +TEST(AdditionTest, HandlesPositiveNumbers) { + EXPECT_EQ(add(2, 3), 5); + EXPECT_EQ(add(10, 20), 30); +} + +TEST(AdditionTest, HandlesNegativeNumbers) { + EXPECT_EQ(add(-2, -3), -5); + EXPECT_EQ(add(-10, 5), -5); +} + +TEST(AdditionTest, HandlesZero) { + EXPECT_EQ(add(0, 0), 0); + EXPECT_EQ(add(0, 5), 5); + EXPECT_EQ(add(5, 0), 5); +} + +// Main function for running tests +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/rust/BUILD b/tests/rust/BUILD new file mode 100644 index 0000000..828a001 --- /dev/null +++ b/tests/rust/BUILD @@ -0,0 +1,18 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +load("@rules_rust//rust:defs.bzl", "rust_test") + +rust_test( + name = "rust_hello_test", + srcs = ["test_main.rs"], +) diff --git a/tests/rust/test_main.rs b/tests/rust/test_main.rs new file mode 100644 index 0000000..9390d5e --- /dev/null +++ b/tests/rust/test_main.rs @@ -0,0 +1,4 @@ +#[test] +fn test_hello() { + assert_eq!(2 + 2, 4); +} From 0dd7f007ddc31e6e0719f8c222df5518078a6da2 Mon Sep 17 00:00:00 2001 From: Pawel Rutka Date: Tue, 19 Aug 2025 14:50:52 +0200 Subject: [PATCH 2/4] import: import log crate as baseline Import from log crate 3aa1359e926a39f841791207d6e57e00da3e68e2 at https://github.com/rust-lang/log Signed-off-by: Pawel Rutka --- Cargo.lock | 216 ++++ Cargo.toml | 8 + src/mw_log/Cargo.toml | 39 + src/mw_log/src/__private_api.rs | 151 +++ src/mw_log/src/kv/error.rs | 94 ++ src/mw_log/src/kv/key.rs | 163 +++ src/mw_log/src/kv/mod.rs | 265 +++++ src/mw_log/src/kv/source.rs | 514 +++++++++ src/mw_log/src/kv/value.rs | 1395 +++++++++++++++++++++++ src/mw_log/src/lib.rs | 1871 +++++++++++++++++++++++++++++++ src/mw_log/src/macros.rs | 579 ++++++++++ src/mw_log/src/serde.rs | 397 +++++++ 12 files changed, 5692 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/mw_log/Cargo.toml create mode 100644 src/mw_log/src/__private_api.rs create mode 100644 src/mw_log/src/kv/error.rs create mode 100644 src/mw_log/src/kv/key.rs create mode 100644 src/mw_log/src/kv/mod.rs create mode 100644 src/mw_log/src/kv/source.rs create mode 100644 src/mw_log/src/kv/value.rs create mode 100644 src/mw_log/src/lib.rs create mode 100644 src/mw_log/src/macros.rs create mode 100644 src/mw_log/src/serde.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3d38df6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,216 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "mw_log" +version = "0.0.1" +dependencies = [ + "value-bag", +] + +[[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 = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[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_fmt" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" +dependencies = [ + "serde", +] + +[[package]] +name = "sval" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9739f56c5d0c44a5ed45473ec868af02eb896af8c05f616673a31e1d1bb09" + +[[package]] +name = "sval_buffer" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f39b07436a8c271b34dad5070c634d1d3d76d6776e938ee97b4a66a5e8003d0b" +dependencies = [ + "sval", + "sval_ref", +] + +[[package]] +name = "sval_dynamic" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffcb072d857431bf885580dacecf05ed987bac931230736739a79051dbf3499b" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f214f427ad94a553e5ca5514c95c6be84667cbc5568cce957f03f3477d03d5c" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ed34b32e638dec9a99c8ac92d0aa1220d40041026b625474c2b6a4d6f4feb" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14bae8fcb2f24fee2c42c1f19037707f7c9a29a0cda936d2188d48a961c4bb2a" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4eaea3821d3046dcba81d4b8489421da42961889902342691fb7eab491d79e" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172dd4aa8cb3b45c8ac8f3b4111d644cd26938b0643ede8f93070812b87fb339" +dependencies = [ + "serde", + "sval", + "sval_nested", +] + +[[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 = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35540706617d373b118d550d41f5dfe0b78a0c195dc13c6815e92e2638432306" +dependencies = [ + "erased-serde", + "serde", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe7e140a2658cc16f7ee7a86e413e803fc8f9b5127adc8755c19f9fefa63a52" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a3b18d2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] +members = [ + "src/mw_log" +] +resolver = "2" + +[workspace.dependencies] + diff --git a/src/mw_log/Cargo.toml b/src/mw_log/Cargo.toml new file mode 100644 index 0000000..a448a2d --- /dev/null +++ b/src/mw_log/Cargo.toml @@ -0,0 +1,39 @@ +[package] + +name = "mw_log" +version = "0.0.1" +authors = ["S-CORE"] +readme = "README.md" +description = """ +A lightweight logging facade for Rust +""" + +rust-version = "1.61.0" +edition = "2021" + +[package.metadata.docs.rs] +features = ["std", "kv_std"] + +[features] +max_level_off = [] +max_level_error = [] +max_level_warn = [] +max_level_info = [] +max_level_debug = [] +max_level_trace = [] + +release_max_level_off = [] +release_max_level_error = [] +release_max_level_warn = [] +release_max_level_info = [] +release_max_level_debug = [] +release_max_level_trace = [] + +std = [] + +kv = [] +kv_std = ["std", "kv", "value-bag/error"] + + +[dependencies] +value-bag = { version = "1.7", optional = true, default-features = false, features = ["inline-i128"] } diff --git a/src/mw_log/src/__private_api.rs b/src/mw_log/src/__private_api.rs new file mode 100644 index 0000000..58d4c0f --- /dev/null +++ b/src/mw_log/src/__private_api.rs @@ -0,0 +1,151 @@ +//! WARNING: this is not part of the crate's public API and is subject to change at any time + +use self::sealed::KVs; +use crate::{logger, Level, Log, Metadata, Record}; +use std::fmt::Arguments; +use std::panic::Location; +pub use std::{format_args, module_path, stringify}; + +#[cfg(not(feature = "kv"))] +pub type Value<'a> = &'a str; + +mod sealed { + /// Types for the `kv` argument. + pub trait KVs<'a> { + fn into_kvs(self) -> Option<&'a [(&'a str, super::Value<'a>)]>; + } +} + +// Types for the `kv` argument. + +impl<'a> KVs<'a> for &'a [(&'a str, Value<'a>)] { + #[inline] + fn into_kvs(self) -> Option<&'a [(&'a str, Value<'a>)]> { + Some(self) + } +} + +impl<'a> KVs<'a> for () { + #[inline] + fn into_kvs(self) -> Option<&'a [(&'a str, Value<'a>)]> { + None + } +} + +// Log implementation. + +/// The global logger proxy. +#[derive(Debug)] +pub struct GlobalLogger; + +impl Log for GlobalLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + logger().enabled(metadata) + } + + fn log(&self, record: &Record) { + logger().log(record) + } + + fn flush(&self) { + logger().flush() + } +} + +// Split from `log` to reduce generics and code size +fn log_impl( + logger: L, + args: Arguments, + level: Level, + &(target, module_path, loc): &(&str, &'static str, &'static Location), + kvs: Option<&[(&str, Value)]>, +) { + #[cfg(not(feature = "kv"))] + if kvs.is_some() { + panic!("key-value support is experimental and must be enabled using the `kv` feature") + } + + let mut builder = Record::builder(); + + builder + .args(args) + .level(level) + .target(target) + .module_path_static(Some(module_path)) + .file_static(Some(loc.file())) + .line(Some(loc.line())); + + #[cfg(feature = "kv")] + builder.key_values(&kvs); + + logger.log(&builder.build()); +} + +pub fn log<'a, K, L>( + logger: L, + args: Arguments, + level: Level, + target_module_path_and_loc: &(&str, &'static str, &'static Location), + kvs: K, +) where + K: KVs<'a>, + L: Log, +{ + log_impl( + logger, + args, + level, + target_module_path_and_loc, + kvs.into_kvs(), + ) +} + +pub fn enabled(logger: L, level: Level, target: &str) -> bool { + logger.enabled(&Metadata::builder().level(level).target(target).build()) +} + +#[track_caller] +pub fn loc() -> &'static Location<'static> { + Location::caller() +} + +#[cfg(feature = "kv")] +mod kv_support { + use crate::kv; + + pub type Value<'a> = kv::Value<'a>; + + // NOTE: Many functions here accept a double reference &&V + // This is so V itself can be ?Sized, while still letting us + // erase it to some dyn Trait (because &T is sized) + + pub fn capture_to_value<'a, V: kv::ToValue + ?Sized>(v: &'a &'a V) -> Value<'a> { + v.to_value() + } + + pub fn capture_debug<'a, V: core::fmt::Debug + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_debug(v) + } + + pub fn capture_display<'a, V: core::fmt::Display + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_display(v) + } + + #[cfg(feature = "kv_std")] + pub fn capture_error<'a>(v: &'a (dyn std::error::Error + 'static)) -> Value<'a> { + Value::from_dyn_error(v) + } + + #[cfg(feature = "kv_sval")] + pub fn capture_sval<'a, V: sval::Value + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_sval(v) + } + + #[cfg(feature = "kv_serde")] + pub fn capture_serde<'a, V: serde::Serialize + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_serde(v) + } +} + +#[cfg(feature = "kv")] +pub use self::kv_support::*; diff --git a/src/mw_log/src/kv/error.rs b/src/mw_log/src/kv/error.rs new file mode 100644 index 0000000..7efa5af --- /dev/null +++ b/src/mw_log/src/kv/error.rs @@ -0,0 +1,94 @@ +use std::fmt; + +/// An error encountered while working with structured data. +#[derive(Debug)] +pub struct Error { + inner: Inner, +} + +#[derive(Debug)] +enum Inner { + #[cfg(feature = "std")] + Boxed(std_support::BoxedError), + Msg(&'static str), + #[cfg(feature = "value-bag")] + Value(crate::kv::value::inner::Error), + Fmt, +} + +impl Error { + /// Create an error from a message. + pub fn msg(msg: &'static str) -> Self { + Error { + inner: Inner::Msg(msg), + } + } + + // Not public so we don't leak the `crate::kv::value::inner` API + #[cfg(feature = "value-bag")] + pub(super) fn from_value(err: crate::kv::value::inner::Error) -> Self { + Error { + inner: Inner::Value(err), + } + } + + // Not public so we don't leak the `crate::kv::value::inner` API + #[cfg(feature = "value-bag")] + pub(super) fn into_value(self) -> crate::kv::value::inner::Error { + match self.inner { + Inner::Value(err) => err, + #[cfg(feature = "kv_std")] + _ => crate::kv::value::inner::Error::boxed(self), + #[cfg(not(feature = "kv_std"))] + _ => crate::kv::value::inner::Error::msg("error inspecting a value"), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Inner::*; + match &self.inner { + #[cfg(feature = "std")] + Boxed(err) => err.fmt(f), + #[cfg(feature = "value-bag")] + Value(err) => err.fmt(f), + Msg(msg) => msg.fmt(f), + Fmt => fmt::Error.fmt(f), + } + } +} + +impl From for Error { + fn from(_: fmt::Error) -> Self { + Error { inner: Inner::Fmt } + } +} + +#[cfg(feature = "std")] +mod std_support { + use super::*; + use std::{error, io}; + + pub(super) type BoxedError = Box; + + impl Error { + /// Create an error from a standard error type. + pub fn boxed(err: E) -> Self + where + E: Into, + { + Error { + inner: Inner::Boxed(err.into()), + } + } + } + + impl error::Error for Error {} + + impl From for Error { + fn from(err: io::Error) -> Self { + Error::boxed(err) + } + } +} diff --git a/src/mw_log/src/kv/key.rs b/src/mw_log/src/kv/key.rs new file mode 100644 index 0000000..6e00a2c --- /dev/null +++ b/src/mw_log/src/kv/key.rs @@ -0,0 +1,163 @@ +//! Structured keys. + +use std::borrow::Borrow; +use std::fmt; + +/// A type that can be converted into a [`Key`](struct.Key.html). +pub trait ToKey { + /// Perform the conversion. + fn to_key(&self) -> Key; +} + +impl<'a, T> ToKey for &'a T +where + T: ToKey + ?Sized, +{ + fn to_key(&self) -> Key { + (**self).to_key() + } +} + +impl<'k> ToKey for Key<'k> { + fn to_key(&self) -> Key { + Key { key: self.key } + } +} + +impl ToKey for str { + fn to_key(&self) -> Key { + Key::from_str(self) + } +} + +/// A key in a key-value. +// These impls must only be based on the as_str() representation of the key +// If a new field (such as an optional index) is added to the key they must not affect comparison +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Key<'k> { + // NOTE: This may become `Cow<'k, str>` + key: &'k str, +} + +impl<'k> Key<'k> { + /// Get a key from a borrowed string. + pub fn from_str(key: &'k str) -> Self { + Key { key } + } + + /// Get a borrowed string from this key. + /// + /// The lifetime of the returned string is bound to the borrow of `self` rather + /// than to `'k`. + pub fn as_str(&self) -> &str { + self.key + } + + /// Try get a borrowed string for the lifetime `'k` from this key. + /// + /// If the key is a borrow of a longer lived string, this method will return `Some`. + /// If the key is internally buffered, this method will return `None`. + pub fn to_borrowed_str(&self) -> Option<&'k str> { + // NOTE: If the internals of `Key` support buffering this + // won't be unconditionally `Some` anymore. We want to keep + // this option open + Some(self.key) + } +} + +impl<'k> fmt::Display for Key<'k> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.key.fmt(f) + } +} + +impl<'k> AsRef for Key<'k> { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl<'k> Borrow for Key<'k> { + fn borrow(&self) -> &str { + self.as_str() + } +} + +impl<'k> From<&'k str> for Key<'k> { + fn from(s: &'k str) -> Self { + Key::from_str(s) + } +} + +#[cfg(feature = "std")] +mod std_support { + use super::*; + + use std::borrow::Cow; + + impl ToKey for String { + fn to_key(&self) -> Key { + Key::from_str(self) + } + } + + impl<'a> ToKey for Cow<'a, str> { + fn to_key(&self) -> Key { + Key::from_str(self) + } + } +} + +#[cfg(feature = "kv_sval")] +mod sval_support { + use super::*; + + use sval::Value; + use sval_ref::ValueRef; + + impl<'a> Value for Key<'a> { + fn stream<'sval, S: sval::Stream<'sval> + ?Sized>( + &'sval self, + stream: &mut S, + ) -> sval::Result { + self.key.stream(stream) + } + } + + impl<'a> ValueRef<'a> for Key<'a> { + fn stream_ref + ?Sized>(&self, stream: &mut S) -> sval::Result { + self.key.stream(stream) + } + } +} + +#[cfg(feature = "kv_serde")] +mod serde_support { + use super::*; + + use serde::{Serialize, Serializer}; + + impl<'a> Serialize for Key<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.key.serialize(serializer) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn key_from_string() { + assert_eq!("a key", Key::from_str("a key").as_str()); + } + + #[test] + fn key_to_borrowed() { + assert_eq!("a key", Key::from_str("a key").to_borrowed_str().unwrap()); + } +} diff --git a/src/mw_log/src/kv/mod.rs b/src/mw_log/src/kv/mod.rs new file mode 100644 index 0000000..34e61c3 --- /dev/null +++ b/src/mw_log/src/kv/mod.rs @@ -0,0 +1,265 @@ +//! Structured logging. +//! +//! Add the `kv` feature to your `Cargo.toml` to enable +//! this module: +//! +//! ```toml +//! [dependencies.log] +//! features = ["kv"] +//! ``` +//! +//! # Structured logging in `log` +//! +//! Structured logging enhances traditional text-based log records with user-defined +//! attributes. Structured logs can be analyzed using a variety of data processing +//! techniques, without needing to find and parse attributes from unstructured text first. +//! +//! In `log`, user-defined attributes are part of a [`Source`] on the log record. +//! Each attribute is a key-value; a pair of [`Key`] and [`Value`]. Keys are strings +//! and values are a datum of any type that can be formatted or serialized. Simple types +//! like strings, booleans, and numbers are supported, as well as arbitrarily complex +//! structures involving nested objects and sequences. +//! +//! ## Adding key-values to log records +//! +//! Key-values appear before the message format in the `log!` macros: +//! +//! ``` +//! # use log::info; +//! info!(a = 1; "Something of interest"); +//! ``` +//! +//! Key-values support the same shorthand identifier syntax as `format_args`: +//! +//! ``` +//! # use log::info; +//! let a = 1; +//! +//! info!(a; "Something of interest"); +//! ``` +//! +//! Values are capturing using the [`ToValue`] trait by default. To capture a value +//! using a different trait implementation, use a modifier after its key. Here's how +//! the same example can capture `a` using its `Debug` implementation instead: +//! +//! ``` +//! # use log::info; +//! info!(a:? = 1; "Something of interest"); +//! ``` +//! +//! The following capturing modifiers are supported: +//! +//! - `:?` will capture the value using `Debug`. +//! - `:debug` will capture the value using `Debug`. +//! - `:%` will capture the value using `Display`. +//! - `:display` will capture the value using `Display`. +//! - `:err` will capture the value using `std::error::Error` (requires the `kv_std` feature). +//! - `:sval` will capture the value using `sval::Value` (requires the `kv_sval` feature). +//! - `:serde` will capture the value using `serde::Serialize` (requires the `kv_serde` feature). +//! +//! ## Working with key-values on log records +//! +//! Use the [`Record::key_values`](../struct.Record.html#method.key_values) method to access key-values. +//! +//! Individual values can be pulled from the source by their key: +//! +//! ``` +//! # fn main() -> Result<(), log::kv::Error> { +//! use log::kv::{Source, Key, Value}; +//! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); +//! +//! // info!(a = 1; "Something of interest"); +//! +//! let a: Value = record.key_values().get(Key::from("a")).unwrap(); +//! assert_eq!(1, a.to_i64().unwrap()); +//! # Ok(()) +//! # } +//! ``` +//! +//! All key-values can also be enumerated using a [`VisitSource`]: +//! +//! ``` +//! # fn main() -> Result<(), log::kv::Error> { +//! use std::collections::BTreeMap; +//! +//! use log::kv::{self, Source, Key, Value, VisitSource}; +//! +//! struct Collect<'kvs>(BTreeMap, Value<'kvs>>); +//! +//! impl<'kvs> VisitSource<'kvs> for Collect<'kvs> { +//! fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { +//! self.0.insert(key, value); +//! +//! Ok(()) +//! } +//! } +//! +//! let mut visitor = Collect(BTreeMap::new()); +//! +//! # let record = log::Record::builder().key_values(&[("a", 1), ("b", 2), ("c", 3)]).build(); +//! // info!(a = 1, b = 2, c = 3; "Something of interest"); +//! +//! record.key_values().visit(&mut visitor)?; +//! +//! let collected = visitor.0; +//! +//! assert_eq!( +//! vec!["a", "b", "c"], +//! collected +//! .keys() +//! .map(|k| k.as_str()) +//! .collect::>(), +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! [`Value`]s have methods for conversions to common types: +//! +//! ``` +//! # fn main() -> Result<(), log::kv::Error> { +//! use log::kv::{Source, Key}; +//! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); +//! +//! // info!(a = 1; "Something of interest"); +//! +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! assert_eq!(1, a.to_i64().unwrap()); +//! # Ok(()) +//! # } +//! ``` +//! +//! Values also have their own [`VisitValue`] type. Value visitors are a lightweight +//! API for working with primitives types: +//! +//! ``` +//! # fn main() -> Result<(), log::kv::Error> { +//! use log::kv::{self, Source, Key, VisitValue}; +//! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); +//! +//! struct IsNumeric(bool); +//! +//! impl<'kvs> VisitValue<'kvs> for IsNumeric { +//! fn visit_any(&mut self, _value: kv::Value) -> Result<(), kv::Error> { +//! self.0 = false; +//! Ok(()) +//! } +//! +//! fn visit_u64(&mut self, _value: u64) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_i64(&mut self, _value: i64) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_u128(&mut self, _value: u128) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_i128(&mut self, _value: i128) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_f64(&mut self, _value: f64) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! } +//! +//! // info!(a = 1; "Something of interest"); +//! +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! let mut visitor = IsNumeric(false); +//! +//! a.visit(&mut visitor)?; +//! +//! let is_numeric = visitor.0; +//! +//! assert!(is_numeric); +//! # Ok(()) +//! # } +//! ``` +//! +//! To serialize a value to a format like JSON, you can also use either `serde` or `sval`: +//! +//! ``` +//! # fn main() -> Result<(), Box> { +//! # #[cfg(feature = "serde")] +//! # { +//! # use log::kv::Key; +//! #[derive(serde::Serialize)] +//! struct Data { +//! a: i32, b: bool, +//! c: &'static str, +//! } +//! +//! let data = Data { a: 1, b: true, c: "Some data" }; +//! +//! # let source = [("a", log::kv::Value::from_serde(&data))]; +//! # let record = log::Record::builder().key_values(&source).build(); +//! // info!(a = data; "Something of interest"); +//! +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! assert_eq!("{\"a\":1,\"b\":true,\"c\":\"Some data\"}", serde_json::to_string(&a)?); +//! # } +//! # Ok(()) +//! # } +//! ``` +//! +//! The choice of serialization framework depends on the needs of the consumer. +//! If you're in a no-std environment, you can use `sval`. In other cases, you can use `serde`. +//! Log producers and log consumers don't need to agree on the serialization framework. +//! A value can be captured using its `serde::Serialize` implementation and still be serialized +//! through `sval` without losing any structure or data. +//! +//! Values can also always be formatted using the standard `Debug` and `Display` +//! traits: +//! +//! ``` +//! # use log::kv::Key; +//! #[derive(Debug)] +//! struct Data { +//! a: i32, +//! b: bool, +//! c: &'static str, +//! } +//! +//! let data = Data { a: 1, b: true, c: "Some data" }; +//! +//! # let source = [("a", log::kv::Value::from_debug(&data))]; +//! # let record = log::Record::builder().key_values(&source).build(); +//! // info!(a = data; "Something of interest"); +//! +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! assert_eq!("Data { a: 1, b: true, c: \"Some data\" }", format!("{a:?}")); +//! ``` + +mod error; +mod key; + +#[cfg(not(feature = "kv_unstable"))] +mod source; +#[cfg(not(feature = "kv_unstable"))] +mod value; + +pub use self::error::Error; +pub use self::key::{Key, ToKey}; +pub use self::source::{Source, VisitSource}; +pub use self::value::{ToValue, Value, VisitValue}; + +#[cfg(feature = "kv_unstable")] +pub mod source; +#[cfg(feature = "kv_unstable")] +pub mod value; + +#[cfg(feature = "kv_unstable")] +pub use self::source::Visitor; diff --git a/src/mw_log/src/kv/source.rs b/src/mw_log/src/kv/source.rs new file mode 100644 index 0000000..f463e6d --- /dev/null +++ b/src/mw_log/src/kv/source.rs @@ -0,0 +1,514 @@ +//! Sources for key-values. +//! +//! This module defines the [`Source`] type and supporting APIs for +//! working with collections of key-values. + +use crate::kv::{Error, Key, ToKey, ToValue, Value}; +use std::fmt; + +/// A source of key-values. +/// +/// The source may be a single pair, a set of pairs, or a filter over a set of pairs. +/// Use the [`VisitSource`](trait.VisitSource.html) trait to inspect the structured data +/// in a source. +/// +/// A source is like an iterator over its key-values, except with a push-based API +/// instead of a pull-based one. +/// +/// # Examples +/// +/// Enumerating the key-values in a source: +/// +/// ``` +/// # fn main() -> Result<(), log::kv::Error> { +/// use log::kv::{self, Source, Key, Value, VisitSource}; +/// +/// // A `VisitSource` that prints all key-values +/// // VisitSources are fed the key-value pairs of each key-values +/// struct Printer; +/// +/// impl<'kvs> VisitSource<'kvs> for Printer { +/// fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { +/// println!("{key}: {value}"); +/// +/// Ok(()) +/// } +/// } +/// +/// // A source with 3 key-values +/// // Common collection types implement the `Source` trait +/// let source = &[ +/// ("a", 1), +/// ("b", 2), +/// ("c", 3), +/// ]; +/// +/// // Pass an instance of the `VisitSource` to a `Source` to visit it +/// source.visit(&mut Printer)?; +/// # Ok(()) +/// # } +/// ``` +pub trait Source { + /// Visit key-values. + /// + /// A source doesn't have to guarantee any ordering or uniqueness of key-values. + /// If the given visitor returns an error then the source may early-return with it, + /// even if there are more key-values. + /// + /// # Implementation notes + /// + /// A source should yield the same key-values to a subsequent visitor unless + /// that visitor itself fails. + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error>; + + /// Get the value for a given key. + /// + /// If the key appears multiple times in the source then which key is returned + /// is implementation specific. + /// + /// # Implementation notes + /// + /// A source that can provide a more efficient implementation of this method + /// should override it. + fn get(&self, key: Key) -> Option> { + get_default(self, key) + } + + /// Count the number of key-values that can be visited. + /// + /// # Implementation notes + /// + /// A source that knows the number of key-values upfront may provide a more + /// efficient implementation. + /// + /// A subsequent call to `visit` should yield the same number of key-values. + fn count(&self) -> usize { + count_default(self) + } +} + +/// The default implementation of `Source::get` +fn get_default<'v>(source: &'v (impl Source + ?Sized), key: Key) -> Option> { + struct Get<'k, 'v> { + key: Key<'k>, + found: Option>, + } + + impl<'k, 'kvs> VisitSource<'kvs> for Get<'k, 'kvs> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + if self.key == key { + self.found = Some(value); + } + + Ok(()) + } + } + + let mut get = Get { key, found: None }; + + let _ = source.visit(&mut get); + get.found +} + +/// The default implementation of `Source::count`. +fn count_default(source: impl Source) -> usize { + struct Count(usize); + + impl<'kvs> VisitSource<'kvs> for Count { + fn visit_pair(&mut self, _: Key<'kvs>, _: Value<'kvs>) -> Result<(), Error> { + self.0 += 1; + + Ok(()) + } + } + + let mut count = Count(0); + let _ = source.visit(&mut count); + count.0 +} + +impl<'a, T> Source for &'a T +where + T: Source + ?Sized, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } +} + +impl Source for (K, V) +where + K: ToKey, + V: ToValue, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + visitor.visit_pair(self.0.to_key(), self.1.to_value()) + } + + fn get(&self, key: Key) -> Option> { + if self.0.to_key() == key { + Some(self.1.to_value()) + } else { + None + } + } + + fn count(&self) -> usize { + 1 + } +} + +impl Source for [S] +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + for source in self { + source.visit(visitor)?; + } + + Ok(()) + } + + fn get(&self, key: Key) -> Option> { + for source in self { + if let Some(found) = source.get(key.clone()) { + return Some(found); + } + } + + None + } + + fn count(&self) -> usize { + self.iter().map(Source::count).sum() + } +} + +impl Source for [S; N] +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(self as &[_], visitor) + } + + fn get(&self, key: Key) -> Option> { + Source::get(self as &[_], key) + } + + fn count(&self) -> usize { + Source::count(self as &[_]) + } +} + +impl Source for Option +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + if let Some(source) = self { + source.visit(visitor)?; + } + + Ok(()) + } + + fn get(&self, key: Key) -> Option> { + self.as_ref().and_then(|s| s.get(key)) + } + + fn count(&self) -> usize { + self.as_ref().map_or(0, Source::count) + } +} + +/// A visitor for the key-value pairs in a [`Source`](trait.Source.html). +pub trait VisitSource<'kvs> { + /// Visit a key-value pair. + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error>; +} + +impl<'a, 'kvs, T> VisitSource<'kvs> for &'a mut T +where + T: VisitSource<'kvs> + ?Sized, +{ + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + (**self).visit_pair(key, value) + } +} + +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugMap<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.entry(&key, &value); + Ok(()) + } +} + +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugList<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.entry(&(key, value)); + Ok(()) + } +} + +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugSet<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.entry(&(key, value)); + Ok(()) + } +} + +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugTuple<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.field(&key); + self.field(&value); + Ok(()) + } +} + +#[cfg(feature = "std")] +mod std_support { + use super::*; + use std::borrow::Borrow; + use std::collections::{BTreeMap, HashMap}; + use std::hash::{BuildHasher, Hash}; + use std::rc::Rc; + use std::sync::Arc; + + impl Source for Box + where + S: Source + ?Sized, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl Source for Arc + where + S: Source + ?Sized, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl Source for Rc + where + S: Source + ?Sized, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl Source for Vec + where + S: Source, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl<'kvs, V> VisitSource<'kvs> for Box + where + V: VisitSource<'kvs> + ?Sized, + { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + (**self).visit_pair(key, value) + } + } + + impl Source for HashMap + where + K: ToKey + Borrow + Eq + Hash, + V: ToValue, + S: BuildHasher, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + for (key, value) in self { + visitor.visit_pair(key.to_key(), value.to_value())?; + } + Ok(()) + } + + fn get(&self, key: Key) -> Option> { + HashMap::get(self, key.as_str()).map(|v| v.to_value()) + } + + fn count(&self) -> usize { + self.len() + } + } + + impl Source for BTreeMap + where + K: ToKey + Borrow + Ord, + V: ToValue, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + for (key, value) in self { + visitor.visit_pair(key.to_key(), value.to_value())?; + } + Ok(()) + } + + fn get(&self, key: Key) -> Option> { + BTreeMap::get(self, key.as_str()).map(|v| v.to_value()) + } + + fn count(&self) -> usize { + self.len() + } + } + + #[cfg(test)] + mod tests { + use crate::kv::value; + + use super::*; + + #[test] + fn count() { + assert_eq!(1, Source::count(&Box::new(("a", 1)))); + assert_eq!(2, Source::count(&vec![("a", 1), ("b", 2)])); + } + + #[test] + fn get() { + let source = vec![("a", 1), ("b", 2), ("a", 1)]; + assert_eq!( + value::inner::Token::I64(1), + Source::get(&source, Key::from_str("a")).unwrap().to_token() + ); + + let source = Box::new(None::<(&str, i32)>); + assert!(Source::get(&source, Key::from_str("a")).is_none()); + } + + #[test] + fn hash_map() { + let mut map = HashMap::new(); + map.insert("a", 1); + map.insert("b", 2); + + assert_eq!(2, Source::count(&map)); + assert_eq!( + value::inner::Token::I64(1), + Source::get(&map, Key::from_str("a")).unwrap().to_token() + ); + } + + #[test] + fn btree_map() { + let mut map = BTreeMap::new(); + map.insert("a", 1); + map.insert("b", 2); + + assert_eq!(2, Source::count(&map)); + assert_eq!( + value::inner::Token::I64(1), + Source::get(&map, Key::from_str("a")).unwrap().to_token() + ); + } + } +} + +// NOTE: Deprecated; but aliases can't carry this attribute +#[cfg(feature = "kv_unstable")] +pub use VisitSource as Visitor; + +#[cfg(test)] +mod tests { + use crate::kv::value; + + use super::*; + + #[test] + fn source_is_object_safe() { + fn _check(_: &dyn Source) {} + } + + #[test] + fn visitor_is_object_safe() { + fn _check(_: &dyn VisitSource) {} + } + + #[test] + fn count() { + struct OnePair { + key: &'static str, + value: i32, + } + + impl Source for OnePair { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + visitor.visit_pair(self.key.to_key(), self.value.to_value()) + } + } + + assert_eq!(1, Source::count(&("a", 1))); + assert_eq!(2, Source::count(&[("a", 1), ("b", 2)] as &[_])); + assert_eq!(0, Source::count(&None::<(&str, i32)>)); + assert_eq!(1, Source::count(&OnePair { key: "a", value: 1 })); + } + + #[test] + fn get() { + let source = &[("a", 1), ("b", 2), ("a", 1)] as &[_]; + assert_eq!( + value::inner::Token::I64(1), + Source::get(source, Key::from_str("a")).unwrap().to_token() + ); + assert_eq!( + value::inner::Token::I64(2), + Source::get(source, Key::from_str("b")).unwrap().to_token() + ); + assert!(Source::get(&source, Key::from_str("c")).is_none()); + + let source = None::<(&str, i32)>; + assert!(Source::get(&source, Key::from_str("a")).is_none()); + } +} diff --git a/src/mw_log/src/kv/value.rs b/src/mw_log/src/kv/value.rs new file mode 100644 index 0000000..e604c80 --- /dev/null +++ b/src/mw_log/src/kv/value.rs @@ -0,0 +1,1395 @@ +//! Structured values. +//! +//! This module defines the [`Value`] type and supporting APIs for +//! capturing and serializing them. + +use std::fmt; + +pub use crate::kv::Error; + +/// A type that can be converted into a [`Value`](struct.Value.html). +pub trait ToValue { + /// Perform the conversion. + fn to_value(&self) -> Value; +} + +impl<'a, T> ToValue for &'a T +where + T: ToValue + ?Sized, +{ + fn to_value(&self) -> Value { + (**self).to_value() + } +} + +impl<'v> ToValue for Value<'v> { + fn to_value(&self) -> Value { + Value { + inner: self.inner.clone(), + } + } +} + +/// A value in a key-value. +/// +/// Values are an anonymous bag containing some structured datum. +/// +/// # Capturing values +/// +/// There are a few ways to capture a value: +/// +/// - Using the `Value::from_*` methods. +/// - Using the `ToValue` trait. +/// - Using the standard `From` trait. +/// +/// ## Using the `Value::from_*` methods +/// +/// `Value` offers a few constructor methods that capture values of different kinds. +/// +/// ``` +/// use log::kv::Value; +/// +/// let value = Value::from_debug(&42i32); +/// +/// assert_eq!(None, value.to_i64()); +/// ``` +/// +/// ## Using the `ToValue` trait +/// +/// The `ToValue` trait can be used to capture values generically. +/// It's the bound used by `Source`. +/// +/// ``` +/// # use log::kv::ToValue; +/// let value = 42i32.to_value(); +/// +/// assert_eq!(Some(42), value.to_i64()); +/// ``` +/// +/// ## Using the standard `From` trait +/// +/// Standard types that implement `ToValue` also implement `From`. +/// +/// ``` +/// use log::kv::Value; +/// +/// let value = Value::from(42i32); +/// +/// assert_eq!(Some(42), value.to_i64()); +/// ``` +/// +/// # Data model +/// +/// Values can hold one of a number of types: +/// +/// - **Null:** The absence of any other meaningful value. Note that +/// `Some(Value::null())` is not the same as `None`. The former is +/// `null` while the latter is `undefined`. This is important to be +/// able to tell the difference between a key-value that was logged, +/// but its value was empty (`Some(Value::null())`) and a key-value +/// that was never logged at all (`None`). +/// - **Strings:** `str`, `char`. +/// - **Booleans:** `bool`. +/// - **Integers:** `u8`-`u128`, `i8`-`i128`, `NonZero*`. +/// - **Floating point numbers:** `f32`-`f64`. +/// - **Errors:** `dyn (Error + 'static)`. +/// - **`serde`:** Any type in `serde`'s data model. +/// - **`sval`:** Any type in `sval`'s data model. +/// +/// # Serialization +/// +/// Values provide a number of ways to be serialized. +/// +/// For basic types the [`Value::visit`] method can be used to extract the +/// underlying typed value. However, this is limited in the amount of types +/// supported (see the [`VisitValue`] trait methods). +/// +/// For more complex types one of the following traits can be used: +/// * `sval::Value`, requires the `kv_sval` feature. +/// * `serde::Serialize`, requires the `kv_serde` feature. +/// +/// You don't need a visitor to serialize values through `serde` or `sval`. +/// +/// A value can always be serialized using any supported framework, regardless +/// of how it was captured. If, for example, a value was captured using its +/// `Display` implementation, it will serialize through `serde` as a string. If it was +/// captured as a struct using `serde`, it will also serialize as a struct +/// through `sval`, or can be formatted using a `Debug`-compatible representation. +#[derive(Clone)] +pub struct Value<'v> { + inner: inner::Inner<'v>, +} + +impl<'v> Value<'v> { + /// Get a value from a type implementing `ToValue`. + pub fn from_any(value: &'v T) -> Self + where + T: ToValue, + { + value.to_value() + } + + /// Get a value from a type implementing `std::fmt::Debug`. + pub fn from_debug(value: &'v T) -> Self + where + T: fmt::Debug, + { + Value { + inner: inner::Inner::from_debug(value), + } + } + + /// Get a value from a type implementing `std::fmt::Display`. + pub fn from_display(value: &'v T) -> Self + where + T: fmt::Display, + { + Value { + inner: inner::Inner::from_display(value), + } + } + + /// Get a value from a type implementing `serde::Serialize`. + #[cfg(feature = "kv_serde")] + pub fn from_serde(value: &'v T) -> Self + where + T: serde::Serialize, + { + Value { + inner: inner::Inner::from_serde1(value), + } + } + + /// Get a value from a type implementing `sval::Value`. + #[cfg(feature = "kv_sval")] + pub fn from_sval(value: &'v T) -> Self + where + T: sval::Value, + { + Value { + inner: inner::Inner::from_sval2(value), + } + } + + /// Get a value from a dynamic `std::fmt::Debug`. + pub fn from_dyn_debug(value: &'v dyn fmt::Debug) -> Self { + Value { + inner: inner::Inner::from_dyn_debug(value), + } + } + + /// Get a value from a dynamic `std::fmt::Display`. + pub fn from_dyn_display(value: &'v dyn fmt::Display) -> Self { + Value { + inner: inner::Inner::from_dyn_display(value), + } + } + + /// Get a value from a dynamic error. + #[cfg(feature = "kv_std")] + pub fn from_dyn_error(err: &'v (dyn std::error::Error + 'static)) -> Self { + Value { + inner: inner::Inner::from_dyn_error(err), + } + } + + /// Get a `null` value. + pub fn null() -> Self { + Value { + inner: inner::Inner::empty(), + } + } + + /// Get a value from an internal primitive. + fn from_inner(value: T) -> Self + where + T: Into>, + { + Value { + inner: value.into(), + } + } + + /// Inspect this value using a simple visitor. + /// + /// When the `kv_serde` or `kv_sval` features are enabled, you can also + /// serialize a value using its `Serialize` or `Value` implementation. + pub fn visit(&self, visitor: impl VisitValue<'v>) -> Result<(), Error> { + inner::visit(&self.inner, visitor) + } +} + +impl<'v> fmt::Debug for Value<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.inner, f) + } +} + +impl<'v> fmt::Display for Value<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +#[cfg(feature = "kv_serde")] +impl<'v> serde::Serialize for Value<'v> { + fn serialize(&self, s: S) -> Result + where + S: serde::Serializer, + { + self.inner.serialize(s) + } +} + +#[cfg(feature = "kv_sval")] +impl<'v> sval::Value for Value<'v> { + fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> sval::Result { + sval::Value::stream(&self.inner, stream) + } +} + +#[cfg(feature = "kv_sval")] +impl<'v> sval_ref::ValueRef<'v> for Value<'v> { + fn stream_ref + ?Sized>(&self, stream: &mut S) -> sval::Result { + sval_ref::ValueRef::stream_ref(&self.inner, stream) + } +} + +impl ToValue for str { + fn to_value(&self) -> Value { + Value::from(self) + } +} + +impl<'v> From<&'v str> for Value<'v> { + fn from(value: &'v str) -> Self { + Value::from_inner(value) + } +} + +impl ToValue for () { + fn to_value(&self) -> Value { + Value::from_inner(()) + } +} + +impl ToValue for Option +where + T: ToValue, +{ + fn to_value(&self) -> Value { + match *self { + Some(ref value) => value.to_value(), + None => Value::from_inner(()), + } + } +} + +macro_rules! impl_to_value_primitive { + ($($into_ty:ty,)*) => { + $( + impl ToValue for $into_ty { + fn to_value(&self) -> Value { + Value::from(*self) + } + } + + impl<'v> From<$into_ty> for Value<'v> { + fn from(value: $into_ty) -> Self { + Value::from_inner(value) + } + } + + impl<'v> From<&'v $into_ty> for Value<'v> { + fn from(value: &'v $into_ty) -> Self { + Value::from_inner(*value) + } + } + )* + }; +} + +macro_rules! impl_to_value_nonzero_primitive { + ($($into_ty:ident,)*) => { + $( + impl ToValue for std::num::$into_ty { + fn to_value(&self) -> Value { + Value::from(self.get()) + } + } + + impl<'v> From for Value<'v> { + fn from(value: std::num::$into_ty) -> Self { + Value::from(value.get()) + } + } + + impl<'v> From<&'v std::num::$into_ty> for Value<'v> { + fn from(value: &'v std::num::$into_ty) -> Self { + Value::from(value.get()) + } + } + )* + }; +} + +macro_rules! impl_value_to_primitive { + ($(#[doc = $doc:tt] $into_name:ident -> $into_ty:ty,)*) => { + impl<'v> Value<'v> { + $( + #[doc = $doc] + pub fn $into_name(&self) -> Option<$into_ty> { + self.inner.$into_name() + } + )* + } + } +} + +impl_to_value_primitive![ + usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64, char, bool, +]; + +#[rustfmt::skip] +impl_to_value_nonzero_primitive![ + NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, + NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, +]; + +impl_value_to_primitive![ + #[doc = "Try convert this value into a `u64`."] + to_u64 -> u64, + #[doc = "Try convert this value into a `i64`."] + to_i64 -> i64, + #[doc = "Try convert this value into a `u128`."] + to_u128 -> u128, + #[doc = "Try convert this value into a `i128`."] + to_i128 -> i128, + #[doc = "Try convert this value into a `f64`."] + to_f64 -> f64, + #[doc = "Try convert this value into a `char`."] + to_char -> char, + #[doc = "Try convert this value into a `bool`."] + to_bool -> bool, +]; + +impl<'v> Value<'v> { + /// Try to convert this value into an error. + #[cfg(feature = "kv_std")] + pub fn to_borrowed_error(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.inner.to_borrowed_error() + } + + /// Try to convert this value into a borrowed string. + pub fn to_borrowed_str(&self) -> Option<&'v str> { + self.inner.to_borrowed_str() + } +} + +#[cfg(feature = "kv_std")] +mod std_support { + use std::borrow::Cow; + use std::rc::Rc; + use std::sync::Arc; + + use super::*; + + impl ToValue for Box + where + T: ToValue + ?Sized, + { + fn to_value(&self) -> Value { + (**self).to_value() + } + } + + impl ToValue for Arc + where + T: ToValue + ?Sized, + { + fn to_value(&self) -> Value { + (**self).to_value() + } + } + + impl ToValue for Rc + where + T: ToValue + ?Sized, + { + fn to_value(&self) -> Value { + (**self).to_value() + } + } + + impl ToValue for String { + fn to_value(&self) -> Value { + Value::from(&**self) + } + } + + impl<'v> ToValue for Cow<'v, str> { + fn to_value(&self) -> Value { + Value::from(&**self) + } + } + + impl<'v> Value<'v> { + /// Try convert this value into a string. + pub fn to_cow_str(&self) -> Option> { + self.inner.to_str() + } + } + + impl<'v> From<&'v String> for Value<'v> { + fn from(v: &'v String) -> Self { + Value::from(&**v) + } + } +} + +/// A visitor for a [`Value`]. +/// +/// Also see [`Value`'s documentation on serialization]. Value visitors are a simple alternative +/// to a more fully-featured serialization framework like `serde` or `sval`. A value visitor +/// can differentiate primitive types through methods like [`VisitValue::visit_bool`] and +/// [`VisitValue::visit_str`], but more complex types like maps and sequences +/// will fallthrough to [`VisitValue::visit_any`]. +/// +/// If you're trying to serialize a value to a format like JSON, you can use either `serde` +/// or `sval` directly with the value. You don't need a visitor. +/// +/// [`Value`'s documentation on serialization]: Value#serialization +pub trait VisitValue<'v> { + /// Visit a `Value`. + /// + /// This is the only required method on `VisitValue` and acts as a fallback for any + /// more specific methods that aren't overridden. + /// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation, + /// or serialized using its `sval::Value` or `serde::Serialize` implementation. + fn visit_any(&mut self, value: Value) -> Result<(), Error>; + + /// Visit an empty value. + fn visit_null(&mut self) -> Result<(), Error> { + self.visit_any(Value::null()) + } + + /// Visit an unsigned integer. + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a signed integer. + fn visit_i64(&mut self, value: i64) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a big unsigned integer. + fn visit_u128(&mut self, value: u128) -> Result<(), Error> { + self.visit_any((value).into()) + } + + /// Visit a big signed integer. + fn visit_i128(&mut self, value: i128) -> Result<(), Error> { + self.visit_any((value).into()) + } + + /// Visit a floating point. + fn visit_f64(&mut self, value: f64) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a boolean. + fn visit_bool(&mut self, value: bool) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a string. + fn visit_str(&mut self, value: &str) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a string. + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + self.visit_str(value) + } + + /// Visit a Unicode character. + fn visit_char(&mut self, value: char) -> Result<(), Error> { + let mut b = [0; 4]; + self.visit_str(&*value.encode_utf8(&mut b)) + } + + /// Visit an error. + #[cfg(feature = "kv_std")] + fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> { + self.visit_any(Value::from_dyn_error(err)) + } + + /// Visit an error. + #[cfg(feature = "kv_std")] + fn visit_borrowed_error( + &mut self, + err: &'v (dyn std::error::Error + 'static), + ) -> Result<(), Error> { + self.visit_any(Value::from_dyn_error(err)) + } +} + +impl<'a, 'v, T: ?Sized> VisitValue<'v> for &'a mut T +where + T: VisitValue<'v>, +{ + fn visit_any(&mut self, value: Value) -> Result<(), Error> { + (**self).visit_any(value) + } + + fn visit_null(&mut self) -> Result<(), Error> { + (**self).visit_null() + } + + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + (**self).visit_u64(value) + } + + fn visit_i64(&mut self, value: i64) -> Result<(), Error> { + (**self).visit_i64(value) + } + + fn visit_u128(&mut self, value: u128) -> Result<(), Error> { + (**self).visit_u128(value) + } + + fn visit_i128(&mut self, value: i128) -> Result<(), Error> { + (**self).visit_i128(value) + } + + fn visit_f64(&mut self, value: f64) -> Result<(), Error> { + (**self).visit_f64(value) + } + + fn visit_bool(&mut self, value: bool) -> Result<(), Error> { + (**self).visit_bool(value) + } + + fn visit_str(&mut self, value: &str) -> Result<(), Error> { + (**self).visit_str(value) + } + + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + (**self).visit_borrowed_str(value) + } + + fn visit_char(&mut self, value: char) -> Result<(), Error> { + (**self).visit_char(value) + } + + #[cfg(feature = "kv_std")] + fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> { + (**self).visit_error(err) + } + + #[cfg(feature = "kv_std")] + fn visit_borrowed_error( + &mut self, + err: &'v (dyn std::error::Error + 'static), + ) -> Result<(), Error> { + (**self).visit_borrowed_error(err) + } +} + +#[cfg(feature = "value-bag")] +pub(in crate::kv) mod inner { + /** + An implementation of `Value` based on a library called `value_bag`. + + `value_bag` was written specifically for use in `log`'s value, but was split out when it outgrew + the codebase here. It's a general-purpose type-erasure library that handles mapping between + more fully-featured serialization frameworks. + */ + use super::*; + + pub use value_bag::ValueBag as Inner; + + pub use value_bag::Error; + + #[cfg(test)] + pub use value_bag::test::TestToken as Token; + + pub fn visit<'v>( + inner: &Inner<'v>, + visitor: impl VisitValue<'v>, + ) -> Result<(), crate::kv::Error> { + struct InnerVisitValue(V); + + impl<'v, V> value_bag::visit::Visit<'v> for InnerVisitValue + where + V: VisitValue<'v>, + { + fn visit_any(&mut self, value: value_bag::ValueBag) -> Result<(), Error> { + self.0 + .visit_any(Value { inner: value }) + .map_err(crate::kv::Error::into_value) + } + + fn visit_empty(&mut self) -> Result<(), Error> { + self.0.visit_null().map_err(crate::kv::Error::into_value) + } + + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + self.0 + .visit_u64(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_i64(&mut self, value: i64) -> Result<(), Error> { + self.0 + .visit_i64(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_u128(&mut self, value: u128) -> Result<(), Error> { + self.0 + .visit_u128(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_i128(&mut self, value: i128) -> Result<(), Error> { + self.0 + .visit_i128(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_f64(&mut self, value: f64) -> Result<(), Error> { + self.0 + .visit_f64(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_bool(&mut self, value: bool) -> Result<(), Error> { + self.0 + .visit_bool(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_str(&mut self, value: &str) -> Result<(), Error> { + self.0 + .visit_str(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + self.0 + .visit_borrowed_str(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_char(&mut self, value: char) -> Result<(), Error> { + self.0 + .visit_char(value) + .map_err(crate::kv::Error::into_value) + } + + #[cfg(feature = "kv_std")] + fn visit_error( + &mut self, + err: &(dyn std::error::Error + 'static), + ) -> Result<(), Error> { + self.0 + .visit_error(err) + .map_err(crate::kv::Error::into_value) + } + + #[cfg(feature = "kv_std")] + fn visit_borrowed_error( + &mut self, + err: &'v (dyn std::error::Error + 'static), + ) -> Result<(), Error> { + self.0 + .visit_borrowed_error(err) + .map_err(crate::kv::Error::into_value) + } + } + + inner + .visit(&mut InnerVisitValue(visitor)) + .map_err(crate::kv::Error::from_value) + } +} + +#[cfg(not(feature = "value-bag"))] +pub(in crate::kv) mod inner { + /** + This is a dependency-free implementation of `Value` when there's no serialization frameworks involved. + In these simple cases a more fully featured solution like `value_bag` isn't needed, so we avoid pulling it in. + + There are a few things here that need to remain consistent with the `value_bag`-based implementation: + + 1. Conversions should always produce the same results. If a conversion here returns `Some`, then + the same `value_bag`-based conversion must also. Of particular note here are floats to ints; they're + based on the standard library's `TryInto` conversions, which need to be converted to `i32` or `u32`, + and then to `f64`. + 2. VisitValues should always be called in the same way. If a particular type of value calls `visit_i64`, + then the same `value_bag`-based visitor must also. + */ + use super::*; + + #[derive(Clone)] + pub enum Inner<'v> { + None, + Bool(bool), + Str(&'v str), + Char(char), + I64(i64), + U64(u64), + F64(f64), + I128(i128), + U128(u128), + Debug(&'v dyn fmt::Debug), + Display(&'v dyn fmt::Display), + } + + impl<'v> From<()> for Inner<'v> { + fn from(_: ()) -> Self { + Inner::None + } + } + + impl<'v> From for Inner<'v> { + fn from(v: bool) -> Self { + Inner::Bool(v) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: char) -> Self { + Inner::Char(v) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: f32) -> Self { + Inner::F64(v as f64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: f64) -> Self { + Inner::F64(v) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: i8) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: i16) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: i32) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: i64) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: isize) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: u8) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: u16) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: u32) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: u64) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: usize) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: i128) -> Self { + Inner::I128(v) + } + } + + impl<'v> From for Inner<'v> { + fn from(v: u128) -> Self { + Inner::U128(v) + } + } + + impl<'v> From<&'v str> for Inner<'v> { + fn from(v: &'v str) -> Self { + Inner::Str(v) + } + } + + impl<'v> fmt::Debug for Inner<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Inner::None => fmt::Debug::fmt(&None::<()>, f), + Inner::Bool(v) => fmt::Debug::fmt(v, f), + Inner::Str(v) => fmt::Debug::fmt(v, f), + Inner::Char(v) => fmt::Debug::fmt(v, f), + Inner::I64(v) => fmt::Debug::fmt(v, f), + Inner::U64(v) => fmt::Debug::fmt(v, f), + Inner::F64(v) => fmt::Debug::fmt(v, f), + Inner::I128(v) => fmt::Debug::fmt(v, f), + Inner::U128(v) => fmt::Debug::fmt(v, f), + Inner::Debug(v) => fmt::Debug::fmt(v, f), + Inner::Display(v) => fmt::Display::fmt(v, f), + } + } + } + + impl<'v> fmt::Display for Inner<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Inner::None => fmt::Debug::fmt(&None::<()>, f), + Inner::Bool(v) => fmt::Display::fmt(v, f), + Inner::Str(v) => fmt::Display::fmt(v, f), + Inner::Char(v) => fmt::Display::fmt(v, f), + Inner::I64(v) => fmt::Display::fmt(v, f), + Inner::U64(v) => fmt::Display::fmt(v, f), + Inner::F64(v) => fmt::Display::fmt(v, f), + Inner::I128(v) => fmt::Display::fmt(v, f), + Inner::U128(v) => fmt::Display::fmt(v, f), + Inner::Debug(v) => fmt::Debug::fmt(v, f), + Inner::Display(v) => fmt::Display::fmt(v, f), + } + } + } + + impl<'v> Inner<'v> { + pub fn from_debug(value: &'v T) -> Self { + Inner::Debug(value) + } + + pub fn from_display(value: &'v T) -> Self { + Inner::Display(value) + } + + pub fn from_dyn_debug(value: &'v dyn fmt::Debug) -> Self { + Inner::Debug(value) + } + + pub fn from_dyn_display(value: &'v dyn fmt::Display) -> Self { + Inner::Display(value) + } + + pub fn empty() -> Self { + Inner::None + } + + pub fn to_bool(&self) -> Option { + match self { + Inner::Bool(v) => Some(*v), + _ => None, + } + } + + pub fn to_char(&self) -> Option { + match self { + Inner::Char(v) => Some(*v), + _ => None, + } + } + + pub fn to_f64(&self) -> Option { + match self { + Inner::F64(v) => Some(*v), + Inner::I64(v) => { + let v: i32 = (*v).try_into().ok()?; + v.try_into().ok() + } + Inner::U64(v) => { + let v: u32 = (*v).try_into().ok()?; + v.try_into().ok() + } + Inner::I128(v) => { + let v: i32 = (*v).try_into().ok()?; + v.try_into().ok() + } + Inner::U128(v) => { + let v: u32 = (*v).try_into().ok()?; + v.try_into().ok() + } + _ => None, + } + } + + pub fn to_i64(&self) -> Option { + match self { + Inner::I64(v) => Some(*v), + Inner::U64(v) => (*v).try_into().ok(), + Inner::I128(v) => (*v).try_into().ok(), + Inner::U128(v) => (*v).try_into().ok(), + _ => None, + } + } + + pub fn to_u64(&self) -> Option { + match self { + Inner::U64(v) => Some(*v), + Inner::I64(v) => (*v).try_into().ok(), + Inner::I128(v) => (*v).try_into().ok(), + Inner::U128(v) => (*v).try_into().ok(), + _ => None, + } + } + + pub fn to_u128(&self) -> Option { + match self { + Inner::U128(v) => Some(*v), + Inner::I64(v) => (*v).try_into().ok(), + Inner::U64(v) => (*v).try_into().ok(), + Inner::I128(v) => (*v).try_into().ok(), + _ => None, + } + } + + pub fn to_i128(&self) -> Option { + match self { + Inner::I128(v) => Some(*v), + Inner::I64(v) => (*v).try_into().ok(), + Inner::U64(v) => (*v).try_into().ok(), + Inner::U128(v) => (*v).try_into().ok(), + _ => None, + } + } + + pub fn to_borrowed_str(&self) -> Option<&'v str> { + match self { + Inner::Str(v) => Some(v), + _ => None, + } + } + + #[cfg(test)] + pub fn to_test_token(&self) -> Token { + match self { + Inner::None => Token::None, + Inner::Bool(v) => Token::Bool(*v), + Inner::Str(v) => Token::Str(*v), + Inner::Char(v) => Token::Char(*v), + Inner::I64(v) => Token::I64(*v), + Inner::U64(v) => Token::U64(*v), + Inner::F64(v) => Token::F64(*v), + Inner::I128(_) => unimplemented!(), + Inner::U128(_) => unimplemented!(), + Inner::Debug(_) => unimplemented!(), + Inner::Display(_) => unimplemented!(), + } + } + } + + #[cfg(test)] + #[derive(Debug, PartialEq)] + pub enum Token<'v> { + None, + Bool(bool), + Char(char), + Str(&'v str), + F64(f64), + I64(i64), + U64(u64), + } + + pub fn visit<'v>( + inner: &Inner<'v>, + mut visitor: impl VisitValue<'v>, + ) -> Result<(), crate::kv::Error> { + match inner { + Inner::None => visitor.visit_null(), + Inner::Bool(v) => visitor.visit_bool(*v), + Inner::Str(v) => visitor.visit_borrowed_str(*v), + Inner::Char(v) => visitor.visit_char(*v), + Inner::I64(v) => visitor.visit_i64(*v), + Inner::U64(v) => visitor.visit_u64(*v), + Inner::F64(v) => visitor.visit_f64(*v), + Inner::I128(v) => visitor.visit_i128(*v), + Inner::U128(v) => visitor.visit_u128(*v), + Inner::Debug(v) => visitor.visit_any(Value::from_dyn_debug(*v)), + Inner::Display(v) => visitor.visit_any(Value::from_dyn_display(*v)), + } + } +} + +impl<'v> Value<'v> { + /// Get a value from a type implementing `std::fmt::Debug`. + #[cfg(feature = "kv_unstable")] + #[deprecated(note = "use `from_debug` instead")] + pub fn capture_debug(value: &'v T) -> Self + where + T: fmt::Debug + 'static, + { + Value::from_debug(value) + } + + /// Get a value from a type implementing `std::fmt::Display`. + #[cfg(feature = "kv_unstable")] + #[deprecated(note = "use `from_display` instead")] + pub fn capture_display(value: &'v T) -> Self + where + T: fmt::Display + 'static, + { + Value::from_display(value) + } + + /// Get a value from an error. + #[cfg(feature = "kv_unstable_std")] + #[deprecated(note = "use `from_dyn_error` instead")] + pub fn capture_error(err: &'v T) -> Self + where + T: std::error::Error + 'static, + { + Value::from_dyn_error(err) + } + + /// Get a value from a type implementing `serde::Serialize`. + #[cfg(feature = "kv_unstable_serde")] + #[deprecated(note = "use `from_serde` instead")] + pub fn capture_serde(value: &'v T) -> Self + where + T: serde::Serialize + 'static, + { + Value::from_serde(value) + } + + /// Get a value from a type implementing `sval::Value`. + #[cfg(feature = "kv_unstable_sval")] + #[deprecated(note = "use `from_sval` instead")] + pub fn capture_sval(value: &'v T) -> Self + where + T: sval::Value + 'static, + { + Value::from_sval(value) + } + + /// Check whether this value can be downcast to `T`. + #[cfg(feature = "kv_unstable")] + #[deprecated( + note = "downcasting has been removed; log an issue at https://github.com/rust-lang/log/issues if this is something you rely on" + )] + pub fn is(&self) -> bool { + false + } + + /// Try downcast this value to `T`. + #[cfg(feature = "kv_unstable")] + #[deprecated( + note = "downcasting has been removed; log an issue at https://github.com/rust-lang/log/issues if this is something you rely on" + )] + pub fn downcast_ref(&self) -> Option<&T> { + None + } +} + +// NOTE: Deprecated; but aliases can't carry this attribute +#[cfg(feature = "kv_unstable")] +pub use VisitValue as Visit; + +/// Get a value from a type implementing `std::fmt::Debug`. +#[cfg(feature = "kv_unstable")] +#[deprecated(note = "use the `key:? = value` macro syntax instead")] +#[macro_export] +macro_rules! as_debug { + ($capture:expr) => { + $crate::kv::Value::from_debug(&$capture) + }; +} + +/// Get a value from a type implementing `std::fmt::Display`. +#[cfg(feature = "kv_unstable")] +#[deprecated(note = "use the `key:% = value` macro syntax instead")] +#[macro_export] +macro_rules! as_display { + ($capture:expr) => { + $crate::kv::Value::from_display(&$capture) + }; +} + +/// Get a value from an error. +#[cfg(feature = "kv_unstable_std")] +#[deprecated(note = "use the `key:err = value` macro syntax instead")] +#[macro_export] +macro_rules! as_error { + ($capture:expr) => { + $crate::kv::Value::from_dyn_error(&$capture) + }; +} + +#[cfg(feature = "kv_unstable_serde")] +#[deprecated(note = "use the `key:serde = value` macro syntax instead")] +/// Get a value from a type implementing `serde::Serialize`. +#[macro_export] +macro_rules! as_serde { + ($capture:expr) => { + $crate::kv::Value::from_serde(&$capture) + }; +} + +/// Get a value from a type implementing `sval::Value`. +#[cfg(feature = "kv_unstable_sval")] +#[deprecated(note = "use the `key:sval = value` macro syntax instead")] +#[macro_export] +macro_rules! as_sval { + ($capture:expr) => { + $crate::kv::Value::from_sval(&$capture) + }; +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + + impl<'v> Value<'v> { + pub(crate) fn to_token(&self) -> inner::Token { + self.inner.to_test_token() + } + } + + fn unsigned() -> impl Iterator> { + vec![ + Value::from(8u8), + Value::from(16u16), + Value::from(32u32), + Value::from(64u64), + Value::from(1usize), + Value::from(std::num::NonZeroU8::new(8).unwrap()), + Value::from(std::num::NonZeroU16::new(16).unwrap()), + Value::from(std::num::NonZeroU32::new(32).unwrap()), + Value::from(std::num::NonZeroU64::new(64).unwrap()), + Value::from(std::num::NonZeroUsize::new(1).unwrap()), + ] + .into_iter() + } + + fn signed() -> impl Iterator> { + vec![ + Value::from(-8i8), + Value::from(-16i16), + Value::from(-32i32), + Value::from(-64i64), + Value::from(-1isize), + Value::from(std::num::NonZeroI8::new(-8).unwrap()), + Value::from(std::num::NonZeroI16::new(-16).unwrap()), + Value::from(std::num::NonZeroI32::new(-32).unwrap()), + Value::from(std::num::NonZeroI64::new(-64).unwrap()), + Value::from(std::num::NonZeroIsize::new(-1).unwrap()), + ] + .into_iter() + } + + fn float() -> impl Iterator> { + vec![Value::from(32.32f32), Value::from(64.64f64)].into_iter() + } + + fn bool() -> impl Iterator> { + vec![Value::from(true), Value::from(false)].into_iter() + } + + fn str() -> impl Iterator> { + vec![Value::from("a string"), Value::from("a loong string")].into_iter() + } + + fn char() -> impl Iterator> { + vec![Value::from('a'), Value::from('โ›ฐ')].into_iter() + } + + #[test] + fn test_to_value_display() { + assert_eq!(42u64.to_value().to_string(), "42"); + assert_eq!(42i64.to_value().to_string(), "42"); + assert_eq!(42.01f64.to_value().to_string(), "42.01"); + assert_eq!(true.to_value().to_string(), "true"); + assert_eq!('a'.to_value().to_string(), "a"); + assert_eq!("a loong string".to_value().to_string(), "a loong string"); + assert_eq!(Some(true).to_value().to_string(), "true"); + assert_eq!(().to_value().to_string(), "None"); + assert_eq!(None::.to_value().to_string(), "None"); + } + + #[test] + fn test_to_value_structured() { + assert_eq!(42u64.to_value().to_token(), inner::Token::U64(42)); + assert_eq!(42i64.to_value().to_token(), inner::Token::I64(42)); + assert_eq!(42.01f64.to_value().to_token(), inner::Token::F64(42.01)); + assert_eq!(true.to_value().to_token(), inner::Token::Bool(true)); + assert_eq!('a'.to_value().to_token(), inner::Token::Char('a')); + assert_eq!( + "a loong string".to_value().to_token(), + inner::Token::Str("a loong string".into()) + ); + assert_eq!(Some(true).to_value().to_token(), inner::Token::Bool(true)); + assert_eq!(().to_value().to_token(), inner::Token::None); + assert_eq!(None::.to_value().to_token(), inner::Token::None); + } + + #[test] + fn test_to_number() { + for v in unsigned() { + assert!(v.to_u64().is_some()); + assert!(v.to_i64().is_some()); + } + + for v in signed() { + assert!(v.to_i64().is_some()); + } + + for v in unsigned().chain(signed()).chain(float()) { + assert!(v.to_f64().is_some()); + } + + for v in bool().chain(str()).chain(char()) { + assert!(v.to_u64().is_none()); + assert!(v.to_i64().is_none()); + assert!(v.to_f64().is_none()); + } + } + + #[test] + fn test_to_float() { + // Only integers from i32::MIN..=u32::MAX can be converted into floats + assert!(Value::from(i32::MIN).to_f64().is_some()); + assert!(Value::from(u32::MAX).to_f64().is_some()); + + assert!(Value::from((i32::MIN as i64) - 1).to_f64().is_none()); + assert!(Value::from((u32::MAX as u64) + 1).to_f64().is_none()); + } + + #[test] + fn test_to_cow_str() { + for v in str() { + assert!(v.to_borrowed_str().is_some()); + + #[cfg(feature = "kv_std")] + assert!(v.to_cow_str().is_some()); + } + + let short_lived = String::from("short lived"); + let v = Value::from(&*short_lived); + + assert!(v.to_borrowed_str().is_some()); + + #[cfg(feature = "kv_std")] + assert!(v.to_cow_str().is_some()); + + for v in unsigned().chain(signed()).chain(float()).chain(bool()) { + assert!(v.to_borrowed_str().is_none()); + + #[cfg(feature = "kv_std")] + assert!(v.to_cow_str().is_none()); + } + } + + #[test] + fn test_to_bool() { + for v in bool() { + assert!(v.to_bool().is_some()); + } + + for v in unsigned() + .chain(signed()) + .chain(float()) + .chain(str()) + .chain(char()) + { + assert!(v.to_bool().is_none()); + } + } + + #[test] + fn test_to_char() { + for v in char() { + assert!(v.to_char().is_some()); + } + + for v in unsigned() + .chain(signed()) + .chain(float()) + .chain(str()) + .chain(bool()) + { + assert!(v.to_char().is_none()); + } + } + + #[test] + fn test_visit_integer() { + struct Extract(Option); + + impl<'v> VisitValue<'v> for Extract { + fn visit_any(&mut self, value: Value) -> Result<(), Error> { + unimplemented!("unexpected value: {value:?}") + } + + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + self.0 = Some(value); + + Ok(()) + } + } + + let mut extract = Extract(None); + Value::from(42u64).visit(&mut extract).unwrap(); + + assert_eq!(Some(42), extract.0); + } + + #[test] + fn test_visit_borrowed_str() { + struct Extract<'v>(Option<&'v str>); + + impl<'v> VisitValue<'v> for Extract<'v> { + fn visit_any(&mut self, value: Value) -> Result<(), Error> { + unimplemented!("unexpected value: {value:?}") + } + + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + self.0 = Some(value); + + Ok(()) + } + } + + let mut extract = Extract(None); + + let short_lived = String::from("A short-lived string"); + Value::from(&*short_lived).visit(&mut extract).unwrap(); + + assert_eq!(Some("A short-lived string"), extract.0); + } +} diff --git a/src/mw_log/src/lib.rs b/src/mw_log/src/lib.rs new file mode 100644 index 0000000..a73955b --- /dev/null +++ b/src/mw_log/src/lib.rs @@ -0,0 +1,1871 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A lightweight logging facade. +//! +//! The `log` crate provides a single logging API that abstracts over the +//! actual logging implementation. Libraries can use the logging API provided +//! by this crate, and the consumer of those libraries can choose the logging +//! implementation that is most suitable for its use case. +//! +//! If no logging implementation is selected, the facade falls back to a "noop" +//! implementation that ignores all log messages. The overhead in this case +//! is very small - just an integer load, comparison and jump. +//! +//! A log request consists of a _target_, a _level_, and a _body_. A target is a +//! string which defaults to the module path of the location of the log request, +//! though that default may be overridden. Logger implementations typically use +//! the target to filter requests based on some user configuration. +//! +//! # Usage +//! +//! The basic use of the log crate is through the five logging macros: [`error!`], +//! [`warn!`], [`info!`], [`debug!`] and [`trace!`] +//! where `error!` represents the highest-priority log messages +//! and `trace!` the lowest. The log messages are filtered by configuring +//! the log level to exclude messages with a lower priority. +//! Each of these macros accept format strings similarly to [`println!`]. +//! +//! +//! [`error!`]: ./macro.error.html +//! [`warn!`]: ./macro.warn.html +//! [`info!`]: ./macro.info.html +//! [`debug!`]: ./macro.debug.html +//! [`trace!`]: ./macro.trace.html +//! [`println!`]: https://doc.rust-lang.org/stable/std/macro.println.html +//! +//! Avoid writing expressions with side-effects in log statements. They may not be evaluated. +//! +//! ## In libraries +//! +//! Libraries should link only to the `log` crate, and use the provided +//! macros to log whatever information will be useful to downstream consumers. +//! +//! ### Examples +//! +//! ``` +//! # #[derive(Debug)] pub struct Yak(String); +//! # impl Yak { fn shave(&mut self, _: u32) {} } +//! # fn find_a_razor() -> Result { Ok(1) } +//! use log::{info, warn}; +//! +//! pub fn shave_the_yak(yak: &mut Yak) { +//! info!(target: "yak_events", "Commencing yak shaving for {yak:?}"); +//! +//! loop { +//! match find_a_razor() { +//! Ok(razor) => { +//! info!("Razor located: {razor}"); +//! yak.shave(razor); +//! break; +//! } +//! Err(err) => { +//! warn!("Unable to locate a razor: {err}, retrying"); +//! } +//! } +//! } +//! } +//! # fn main() {} +//! ``` +//! +//! ## In executables +//! +//! Executables should choose a logging implementation and initialize it early in the +//! runtime of the program. Logging implementations will typically include a +//! function to do this. Any log messages generated before +//! the implementation is initialized will be ignored. +//! +//! The executable itself may use the `log` crate to log as well. +//! +//! ### Warning +//! +//! The logging system may only be initialized once. +//! +//! ## Structured logging +//! +//! If you enable the `kv` feature you can associate structured values +//! with your log records. If we take the example from before, we can include +//! some additional context besides what's in the formatted message: +//! +//! ``` +//! # use serde::Serialize; +//! # #[derive(Debug, Serialize)] pub struct Yak(String); +//! # impl Yak { fn shave(&mut self, _: u32) {} } +//! # fn find_a_razor() -> Result { Ok(1) } +//! # #[cfg(feature = "kv_serde")] +//! # fn main() { +//! use log::{info, warn}; +//! +//! pub fn shave_the_yak(yak: &mut Yak) { +//! info!(target: "yak_events", yak:serde; "Commencing yak shaving"); +//! +//! loop { +//! match find_a_razor() { +//! Ok(razor) => { +//! info!(razor; "Razor located"); +//! yak.shave(razor); +//! break; +//! } +//! Err(e) => { +//! warn!(e:err; "Unable to locate a razor, retrying"); +//! } +//! } +//! } +//! } +//! # } +//! # #[cfg(not(feature = "kv_serde"))] +//! # fn main() {} +//! ``` +//! +//! See the [`kv`] module documentation for more details. +//! +//! # Available logging implementations +//! +//! In order to produce log output executables have to use +//! a logger implementation compatible with the facade. +//! There are many available implementations to choose from, +//! here are some of the most popular ones: +//! +//! * Simple minimal loggers: +//! * [env_logger] +//! * [colog] +//! * [simple_logger] +//! * [simplelog] +//! * [pretty_env_logger] +//! * [stderrlog] +//! * [flexi_logger] +//! * [call_logger] +//! * [structured-logger] +//! * [clang_log] +//! * [ftail] +//! * Complex configurable frameworks: +//! * [log4rs] +//! * [logforth] +//! * [fern] +//! * [spdlog-rs] +//! * Adaptors for other facilities: +//! * [syslog] +//! * [slog-stdlog] +//! * [systemd-journal-logger] +//! * [android_log] +//! * [win_dbg_logger] +//! * [db_logger] +//! * [log-to-defmt] +//! * [logcontrol-log] +//! * For WebAssembly binaries: +//! * [console_log] +//! * For dynamic libraries: +//! * You may need to construct an FFI-safe wrapper over `log` to initialize in your libraries +//! * Utilities: +//! * [log_err] +//! * [log-reload] +//! +//! # Implementing a Logger +//! +//! Loggers implement the [`Log`] trait. Here's a very basic example that simply +//! logs all messages at the [`Error`][level_link], [`Warn`][level_link] or +//! [`Info`][level_link] levels to stdout: +//! +//! ``` +//! use log::{Record, Level, Metadata}; +//! +//! struct SimpleLogger; +//! +//! impl log::Log for SimpleLogger { +//! fn enabled(&self, metadata: &Metadata) -> bool { +//! metadata.level() <= Level::Info +//! } +//! +//! fn log(&self, record: &Record) { +//! if self.enabled(record.metadata()) { +//! println!("{} - {}", record.level(), record.args()); +//! } +//! } +//! +//! fn flush(&self) {} +//! } +//! +//! # fn main() {} +//! ``` +//! +//! Loggers are installed by calling the [`set_logger`] function. The maximum +//! log level also needs to be adjusted via the [`set_max_level`] function. The +//! logging facade uses this as an optimization to improve performance of log +//! messages at levels that are disabled. It's important to set it, as it +//! defaults to [`Off`][filter_link], so no log messages will ever be captured! +//! In the case of our example logger, we'll want to set the maximum log level +//! to [`Info`][filter_link], since we ignore any [`Debug`][level_link] or +//! [`Trace`][level_link] level log messages. A logging implementation should +//! provide a function that wraps a call to [`set_logger`] and +//! [`set_max_level`], handling initialization of the logger: +//! +//! ``` +//! # use log::{Level, Metadata}; +//! # struct SimpleLogger; +//! # impl log::Log for SimpleLogger { +//! # fn enabled(&self, _: &Metadata) -> bool { false } +//! # fn log(&self, _: &log::Record) {} +//! # fn flush(&self) {} +//! # } +//! # fn main() {} +//! use log::{SetLoggerError, LevelFilter}; +//! +//! static LOGGER: SimpleLogger = SimpleLogger; +//! +//! pub fn init() -> Result<(), SetLoggerError> { +//! log::set_logger(&LOGGER) +//! .map(|()| log::set_max_level(LevelFilter::Info)) +//! } +//! ``` +//! +//! Implementations that adjust their configurations at runtime should take care +//! to adjust the maximum log level as well. +//! +//! # Use with `std` +//! +//! `set_logger` requires you to provide a `&'static Log`, which can be hard to +//! obtain if your logger depends on some runtime configuration. The +//! `set_boxed_logger` function is available with the `std` Cargo feature. It is +//! identical to `set_logger` except that it takes a `Box` rather than a +//! `&'static Log`: +//! +//! ``` +//! # use log::{Level, LevelFilter, Log, SetLoggerError, Metadata}; +//! # struct SimpleLogger; +//! # impl log::Log for SimpleLogger { +//! # fn enabled(&self, _: &Metadata) -> bool { false } +//! # fn log(&self, _: &log::Record) {} +//! # fn flush(&self) {} +//! # } +//! # fn main() {} +//! # #[cfg(feature = "std")] +//! pub fn init() -> Result<(), SetLoggerError> { +//! log::set_boxed_logger(Box::new(SimpleLogger)) +//! .map(|()| log::set_max_level(LevelFilter::Info)) +//! } +//! ``` +//! +//! # Compile time filters +//! +//! Log levels can be statically disabled at compile time by enabling one of these Cargo features: +//! +//! * `max_level_off` +//! * `max_level_error` +//! * `max_level_warn` +//! * `max_level_info` +//! * `max_level_debug` +//! * `max_level_trace` +//! +//! Log invocations at disabled levels will be skipped and will not even be present in the +//! resulting binary. These features control the value of the `STATIC_MAX_LEVEL` constant. The +//! logging macros check this value before logging a message. By default, no levels are disabled. +//! +//! It is possible to override this level for release builds only with the following features: +//! +//! * `release_max_level_off` +//! * `release_max_level_error` +//! * `release_max_level_warn` +//! * `release_max_level_info` +//! * `release_max_level_debug` +//! * `release_max_level_trace` +//! +//! Libraries should avoid using the max level features because they're global and can't be changed +//! once they're set. +//! +//! For example, a crate can disable trace level logs in debug builds and trace, debug, and info +//! level logs in release builds with the following configuration: +//! +//! ```toml +//! [dependencies] +//! log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] } +//! ``` +//! # Crate Feature Flags +//! +//! The following crate feature flags are available in addition to the filters. They are +//! configured in your `Cargo.toml`. +//! +//! * `std` allows use of `std` crate instead of the default `core`. Enables using `std::error` and +//! `set_boxed_logger` functionality. +//! * `serde` enables support for serialization and deserialization of `Level` and `LevelFilter`. +//! +//! ```toml +//! [dependencies] +//! log = { version = "0.4", features = ["std", "serde"] } +//! ``` +//! +//! # Version compatibility +//! +//! The 0.3 and 0.4 versions of the `log` crate are almost entirely compatible. Log messages +//! made using `log` 0.3 will forward transparently to a logger implementation using `log` 0.4. Log +//! messages made using `log` 0.4 will forward to a logger implementation using `log` 0.3, but the +//! module path and file name information associated with the message will unfortunately be lost. +//! +//! [`Log`]: trait.Log.html +//! [level_link]: enum.Level.html +//! [filter_link]: enum.LevelFilter.html +//! [`set_logger`]: fn.set_logger.html +//! [`set_max_level`]: fn.set_max_level.html +//! [`try_set_logger_raw`]: fn.try_set_logger_raw.html +//! [`shutdown_logger_raw`]: fn.shutdown_logger_raw.html +//! [env_logger]: https://docs.rs/env_logger/*/env_logger/ +//! [colog]: https://docs.rs/colog/*/colog/ +//! [simple_logger]: https://github.com/borntyping/rust-simple_logger +//! [simplelog]: https://github.com/drakulix/simplelog.rs +//! [pretty_env_logger]: https://docs.rs/pretty_env_logger/*/pretty_env_logger/ +//! [stderrlog]: https://docs.rs/stderrlog/*/stderrlog/ +//! [flexi_logger]: https://docs.rs/flexi_logger/*/flexi_logger/ +//! [call_logger]: https://docs.rs/call_logger/*/call_logger/ +//! [syslog]: https://docs.rs/syslog/*/syslog/ +//! [slog-stdlog]: https://docs.rs/slog-stdlog/*/slog_stdlog/ +//! [log4rs]: https://docs.rs/log4rs/*/log4rs/ +//! [logforth]: https://docs.rs/logforth/*/logforth/ +//! [fern]: https://docs.rs/fern/*/fern/ +//! [spdlog-rs]: https://docs.rs/spdlog-rs/*/spdlog/ +//! [systemd-journal-logger]: https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/ +//! [android_log]: https://docs.rs/android_log/*/android_log/ +//! [win_dbg_logger]: https://docs.rs/win_dbg_logger/*/win_dbg_logger/ +//! [db_logger]: https://docs.rs/db_logger/*/db_logger/ +//! [log-to-defmt]: https://docs.rs/log-to-defmt/*/log_to_defmt/ +//! [console_log]: https://docs.rs/console_log/*/console_log/ +//! [structured-logger]: https://docs.rs/structured-logger/latest/structured_logger/ +//! [logcontrol-log]: https://docs.rs/logcontrol-log/*/logcontrol_log/ +//! [log_err]: https://docs.rs/log_err/*/log_err/ +//! [log-reload]: https://docs.rs/log-reload/*/log_reload/ +//! [clang_log]: https://docs.rs/clang_log/latest/clang_log +//! [ftail]: https://docs.rs/ftail/latest/ftail + +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://docs.rs/log/0.4.27" +)] +#![warn(missing_docs)] +#![deny(missing_debug_implementations, unconditional_recursion)] +#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] + +#[cfg(any( + all(feature = "max_level_off", feature = "max_level_error"), + all(feature = "max_level_off", feature = "max_level_warn"), + all(feature = "max_level_off", feature = "max_level_info"), + all(feature = "max_level_off", feature = "max_level_debug"), + all(feature = "max_level_off", feature = "max_level_trace"), + all(feature = "max_level_error", feature = "max_level_warn"), + all(feature = "max_level_error", feature = "max_level_info"), + all(feature = "max_level_error", feature = "max_level_debug"), + all(feature = "max_level_error", feature = "max_level_trace"), + all(feature = "max_level_warn", feature = "max_level_info"), + all(feature = "max_level_warn", feature = "max_level_debug"), + all(feature = "max_level_warn", feature = "max_level_trace"), + all(feature = "max_level_info", feature = "max_level_debug"), + all(feature = "max_level_info", feature = "max_level_trace"), + all(feature = "max_level_debug", feature = "max_level_trace"), +))] +compile_error!("multiple max_level_* features set"); + +#[rustfmt::skip] +#[cfg(any( + all(feature = "release_max_level_off", feature = "release_max_level_error"), + all(feature = "release_max_level_off", feature = "release_max_level_warn"), + all(feature = "release_max_level_off", feature = "release_max_level_info"), + all(feature = "release_max_level_off", feature = "release_max_level_debug"), + all(feature = "release_max_level_off", feature = "release_max_level_trace"), + all(feature = "release_max_level_error", feature = "release_max_level_warn"), + all(feature = "release_max_level_error", feature = "release_max_level_info"), + all(feature = "release_max_level_error", feature = "release_max_level_debug"), + all(feature = "release_max_level_error", feature = "release_max_level_trace"), + all(feature = "release_max_level_warn", feature = "release_max_level_info"), + all(feature = "release_max_level_warn", feature = "release_max_level_debug"), + all(feature = "release_max_level_warn", feature = "release_max_level_trace"), + all(feature = "release_max_level_info", feature = "release_max_level_debug"), + all(feature = "release_max_level_info", feature = "release_max_level_trace"), + all(feature = "release_max_level_debug", feature = "release_max_level_trace"), +))] +compile_error!("multiple release_max_level_* features set"); + +#[cfg(all(not(feature = "std"), not(test)))] +extern crate core as std; + +use std::cfg; +#[cfg(feature = "std")] +use std::error; +use std::str::FromStr; +use std::{cmp, fmt, mem}; + +#[macro_use] +mod macros; +mod serde; + +#[cfg(feature = "kv")] +pub mod kv; + +#[cfg(target_has_atomic = "ptr")] +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[cfg(not(target_has_atomic = "ptr"))] +use std::cell::Cell; +#[cfg(not(target_has_atomic = "ptr"))] +use std::sync::atomic::Ordering; + +#[cfg(not(target_has_atomic = "ptr"))] +struct AtomicUsize { + v: Cell, +} + +#[cfg(not(target_has_atomic = "ptr"))] +impl AtomicUsize { + const fn new(v: usize) -> AtomicUsize { + AtomicUsize { v: Cell::new(v) } + } + + fn load(&self, _order: Ordering) -> usize { + self.v.get() + } + + fn store(&self, val: usize, _order: Ordering) { + self.v.set(val) + } +} + +// Any platform without atomics is unlikely to have multiple cores, so +// writing via Cell will not be a race condition. +#[cfg(not(target_has_atomic = "ptr"))] +unsafe impl Sync for AtomicUsize {} + +// The LOGGER static holds a pointer to the global logger. It is protected by +// the STATE static which determines whether LOGGER has been initialized yet. +static mut LOGGER: &dyn Log = &NopLogger; + +static STATE: AtomicUsize = AtomicUsize::new(0); + +// There are three different states that we care about: the logger's +// uninitialized, the logger's initializing (set_logger's been called but +// LOGGER hasn't actually been set yet), or the logger's active. +const UNINITIALIZED: usize = 0; +const INITIALIZING: usize = 1; +const INITIALIZED: usize = 2; + +static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(0); + +static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; + +static SET_LOGGER_ERROR: &str = "attempted to set a logger after the logging system \ + was already initialized"; +static LEVEL_PARSE_ERROR: &str = + "attempted to convert a string that doesn't match an existing log level"; + +/// An enum representing the available verbosity levels of the logger. +/// +/// Typical usage includes: checking if a certain `Level` is enabled with +/// [`log_enabled!`](macro.log_enabled.html), specifying the `Level` of +/// [`log!`](macro.log.html), and comparing a `Level` directly to a +/// [`LevelFilter`](enum.LevelFilter.html). +#[repr(usize)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Level { + /// The "error" level. + /// + /// Designates very serious errors. + // This way these line up with the discriminants for LevelFilter below + // This works because Rust treats field-less enums the same way as C does: + // https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-field-less-enumerations + Error = 1, + /// The "warn" level. + /// + /// Designates hazardous situations. + Warn, + /// The "info" level. + /// + /// Designates useful information. + Info, + /// The "debug" level. + /// + /// Designates lower priority information. + Debug, + /// The "trace" level. + /// + /// Designates very low priority, often extremely verbose, information. + Trace, +} + +impl PartialEq for Level { + #[inline] + fn eq(&self, other: &LevelFilter) -> bool { + *self as usize == *other as usize + } +} + +impl PartialOrd for Level { + #[inline] + fn partial_cmp(&self, other: &LevelFilter) -> Option { + Some((*self as usize).cmp(&(*other as usize))) + } +} + +impl FromStr for Level { + type Err = ParseLevelError; + fn from_str(level: &str) -> Result { + LOG_LEVEL_NAMES + .iter() + .position(|&name| name.eq_ignore_ascii_case(level)) + .into_iter() + .filter(|&idx| idx != 0) + .map(|idx| Level::from_usize(idx).unwrap()) + .next() + .ok_or(ParseLevelError(())) + } +} + +impl fmt::Display for Level { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.pad(self.as_str()) + } +} + +impl Level { + fn from_usize(u: usize) -> Option { + match u { + 1 => Some(Level::Error), + 2 => Some(Level::Warn), + 3 => Some(Level::Info), + 4 => Some(Level::Debug), + 5 => Some(Level::Trace), + _ => None, + } + } + + /// Returns the most verbose logging level. + #[inline] + pub fn max() -> Level { + Level::Trace + } + + /// Converts the `Level` to the equivalent `LevelFilter`. + #[inline] + pub fn to_level_filter(&self) -> LevelFilter { + LevelFilter::from_usize(*self as usize).unwrap() + } + + /// Returns the string representation of the `Level`. + /// + /// This returns the same string as the `fmt::Display` implementation. + pub fn as_str(&self) -> &'static str { + LOG_LEVEL_NAMES[*self as usize] + } + + /// Iterate through all supported logging levels. + /// + /// The order of iteration is from more severe to less severe log messages. + /// + /// # Examples + /// + /// ``` + /// use log::Level; + /// + /// let mut levels = Level::iter(); + /// + /// assert_eq!(Some(Level::Error), levels.next()); + /// assert_eq!(Some(Level::Trace), levels.last()); + /// ``` + pub fn iter() -> impl Iterator { + (1..6).map(|i| Self::from_usize(i).unwrap()) + } +} + +/// An enum representing the available verbosity level filters of the logger. +/// +/// A `LevelFilter` may be compared directly to a [`Level`]. Use this type +/// to get and set the maximum log level with [`max_level()`] and [`set_max_level`]. +/// +/// [`Level`]: enum.Level.html +/// [`max_level()`]: fn.max_level.html +/// [`set_max_level`]: fn.set_max_level.html +#[repr(usize)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum LevelFilter { + /// A level lower than all log levels. + Off, + /// Corresponds to the `Error` log level. + Error, + /// Corresponds to the `Warn` log level. + Warn, + /// Corresponds to the `Info` log level. + Info, + /// Corresponds to the `Debug` log level. + Debug, + /// Corresponds to the `Trace` log level. + Trace, +} + +impl PartialEq for LevelFilter { + #[inline] + fn eq(&self, other: &Level) -> bool { + other.eq(self) + } +} + +impl PartialOrd for LevelFilter { + #[inline] + fn partial_cmp(&self, other: &Level) -> Option { + Some((*self as usize).cmp(&(*other as usize))) + } +} + +impl FromStr for LevelFilter { + type Err = ParseLevelError; + fn from_str(level: &str) -> Result { + LOG_LEVEL_NAMES + .iter() + .position(|&name| name.eq_ignore_ascii_case(level)) + .map(|p| LevelFilter::from_usize(p).unwrap()) + .ok_or(ParseLevelError(())) + } +} + +impl fmt::Display for LevelFilter { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.pad(self.as_str()) + } +} + +impl LevelFilter { + fn from_usize(u: usize) -> Option { + match u { + 0 => Some(LevelFilter::Off), + 1 => Some(LevelFilter::Error), + 2 => Some(LevelFilter::Warn), + 3 => Some(LevelFilter::Info), + 4 => Some(LevelFilter::Debug), + 5 => Some(LevelFilter::Trace), + _ => None, + } + } + + /// Returns the most verbose logging level filter. + #[inline] + pub fn max() -> LevelFilter { + LevelFilter::Trace + } + + /// Converts `self` to the equivalent `Level`. + /// + /// Returns `None` if `self` is `LevelFilter::Off`. + #[inline] + pub fn to_level(&self) -> Option { + Level::from_usize(*self as usize) + } + + /// Returns the string representation of the `LevelFilter`. + /// + /// This returns the same string as the `fmt::Display` implementation. + pub fn as_str(&self) -> &'static str { + LOG_LEVEL_NAMES[*self as usize] + } + + /// Iterate through all supported filtering levels. + /// + /// The order of iteration is from less to more verbose filtering. + /// + /// # Examples + /// + /// ``` + /// use log::LevelFilter; + /// + /// let mut levels = LevelFilter::iter(); + /// + /// assert_eq!(Some(LevelFilter::Off), levels.next()); + /// assert_eq!(Some(LevelFilter::Trace), levels.last()); + /// ``` + pub fn iter() -> impl Iterator { + (0..6).map(|i| Self::from_usize(i).unwrap()) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +enum MaybeStaticStr<'a> { + Static(&'static str), + Borrowed(&'a str), +} + +impl<'a> MaybeStaticStr<'a> { + #[inline] + fn get(&self) -> &'a str { + match *self { + MaybeStaticStr::Static(s) => s, + MaybeStaticStr::Borrowed(s) => s, + } + } +} + +/// The "payload" of a log message. +/// +/// # Use +/// +/// `Record` structures are passed as parameters to the [`log`][method.log] +/// method of the [`Log`] trait. Logger implementors manipulate these +/// structures in order to display log messages. `Record`s are automatically +/// created by the [`log!`] macro and so are not seen by log users. +/// +/// Note that the [`level()`] and [`target()`] accessors are equivalent to +/// `self.metadata().level()` and `self.metadata().target()` respectively. +/// These methods are provided as a convenience for users of this structure. +/// +/// # Example +/// +/// The following example shows a simple logger that displays the level, +/// module path, and message of any `Record` that is passed to it. +/// +/// ``` +/// struct SimpleLogger; +/// +/// impl log::Log for SimpleLogger { +/// fn enabled(&self, _metadata: &log::Metadata) -> bool { +/// true +/// } +/// +/// fn log(&self, record: &log::Record) { +/// if !self.enabled(record.metadata()) { +/// return; +/// } +/// +/// println!("{}:{} -- {}", +/// record.level(), +/// record.target(), +/// record.args()); +/// } +/// fn flush(&self) {} +/// } +/// ``` +/// +/// [method.log]: trait.Log.html#tymethod.log +/// [`Log`]: trait.Log.html +/// [`log!`]: macro.log.html +/// [`level()`]: struct.Record.html#method.level +/// [`target()`]: struct.Record.html#method.target +#[derive(Clone, Debug)] +pub struct Record<'a> { + metadata: Metadata<'a>, + args: fmt::Arguments<'a>, + module_path: Option>, + file: Option>, + line: Option, + #[cfg(feature = "kv")] + key_values: KeyValues<'a>, +} + +// This wrapper type is only needed so we can +// `#[derive(Debug)]` on `Record`. It also +// provides a useful `Debug` implementation for +// the underlying `Source`. +#[cfg(feature = "kv")] +#[derive(Clone)] +struct KeyValues<'a>(&'a dyn kv::Source); + +#[cfg(feature = "kv")] +impl<'a> fmt::Debug for KeyValues<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut visitor = f.debug_map(); + self.0.visit(&mut visitor).map_err(|_| fmt::Error)?; + visitor.finish() + } +} + +impl<'a> Record<'a> { + /// Returns a new builder. + #[inline] + pub fn builder() -> RecordBuilder<'a> { + RecordBuilder::new() + } + + /// The message body. + #[inline] + pub fn args(&self) -> &fmt::Arguments<'a> { + &self.args + } + + /// Metadata about the log directive. + #[inline] + pub fn metadata(&self) -> &Metadata<'a> { + &self.metadata + } + + /// The verbosity level of the message. + #[inline] + pub fn level(&self) -> Level { + self.metadata.level() + } + + /// The name of the target of the directive. + #[inline] + pub fn target(&self) -> &'a str { + self.metadata.target() + } + + /// The module path of the message. + #[inline] + pub fn module_path(&self) -> Option<&'a str> { + self.module_path.map(|s| s.get()) + } + + /// The module path of the message, if it is a `'static` string. + #[inline] + pub fn module_path_static(&self) -> Option<&'static str> { + match self.module_path { + Some(MaybeStaticStr::Static(s)) => Some(s), + _ => None, + } + } + + /// The source file containing the message. + #[inline] + pub fn file(&self) -> Option<&'a str> { + self.file.map(|s| s.get()) + } + + /// The source file containing the message, if it is a `'static` string. + #[inline] + pub fn file_static(&self) -> Option<&'static str> { + match self.file { + Some(MaybeStaticStr::Static(s)) => Some(s), + _ => None, + } + } + + /// The line containing the message. + #[inline] + pub fn line(&self) -> Option { + self.line + } + + /// The structured key-value pairs associated with the message. + #[cfg(feature = "kv")] + #[inline] + pub fn key_values(&self) -> &dyn kv::Source { + self.key_values.0 + } + + /// Create a new [`RecordBuilder`](struct.RecordBuilder.html) based on this record. + #[cfg(feature = "kv")] + #[inline] + pub fn to_builder(&self) -> RecordBuilder { + RecordBuilder { + record: Record { + metadata: Metadata { + level: self.metadata.level, + target: self.metadata.target, + }, + args: self.args, + module_path: self.module_path, + file: self.file, + line: self.line, + key_values: self.key_values.clone(), + }, + } + } +} + +/// Builder for [`Record`](struct.Record.html). +/// +/// Typically should only be used by log library creators or for testing and "shim loggers". +/// The `RecordBuilder` can set the different parameters of `Record` object, and returns +/// the created object when `build` is called. +/// +/// # Examples +/// +/// ``` +/// use log::{Level, Record}; +/// +/// let record = Record::builder() +/// .args(format_args!("Error!")) +/// .level(Level::Error) +/// .target("myApp") +/// .file(Some("server.rs")) +/// .line(Some(144)) +/// .module_path(Some("server")) +/// .build(); +/// ``` +/// +/// Alternatively, use [`MetadataBuilder`](struct.MetadataBuilder.html): +/// +/// ``` +/// use log::{Record, Level, MetadataBuilder}; +/// +/// let error_metadata = MetadataBuilder::new() +/// .target("myApp") +/// .level(Level::Error) +/// .build(); +/// +/// let record = Record::builder() +/// .metadata(error_metadata) +/// .args(format_args!("Error!")) +/// .line(Some(433)) +/// .file(Some("app.rs")) +/// .module_path(Some("server")) +/// .build(); +/// ``` +#[derive(Debug)] +pub struct RecordBuilder<'a> { + record: Record<'a>, +} + +impl<'a> RecordBuilder<'a> { + /// Construct new `RecordBuilder`. + /// + /// The default options are: + /// + /// - `args`: [`format_args!("")`] + /// - `metadata`: [`Metadata::builder().build()`] + /// - `module_path`: `None` + /// - `file`: `None` + /// - `line`: `None` + /// + /// [`format_args!("")`]: https://doc.rust-lang.org/std/macro.format_args.html + /// [`Metadata::builder().build()`]: struct.MetadataBuilder.html#method.build + #[inline] + pub fn new() -> RecordBuilder<'a> { + RecordBuilder { + record: Record { + args: format_args!(""), + metadata: Metadata::builder().build(), + module_path: None, + file: None, + line: None, + #[cfg(feature = "kv")] + key_values: KeyValues(&None::<(kv::Key, kv::Value)>), + }, + } + } + + /// Set [`args`](struct.Record.html#method.args). + #[inline] + pub fn args(&mut self, args: fmt::Arguments<'a>) -> &mut RecordBuilder<'a> { + self.record.args = args; + self + } + + /// Set [`metadata`](struct.Record.html#method.metadata). Construct a `Metadata` object with [`MetadataBuilder`](struct.MetadataBuilder.html). + #[inline] + pub fn metadata(&mut self, metadata: Metadata<'a>) -> &mut RecordBuilder<'a> { + self.record.metadata = metadata; + self + } + + /// Set [`Metadata::level`](struct.Metadata.html#method.level). + #[inline] + pub fn level(&mut self, level: Level) -> &mut RecordBuilder<'a> { + self.record.metadata.level = level; + self + } + + /// Set [`Metadata::target`](struct.Metadata.html#method.target) + #[inline] + pub fn target(&mut self, target: &'a str) -> &mut RecordBuilder<'a> { + self.record.metadata.target = target; + self + } + + /// Set [`module_path`](struct.Record.html#method.module_path) + #[inline] + pub fn module_path(&mut self, path: Option<&'a str>) -> &mut RecordBuilder<'a> { + self.record.module_path = path.map(MaybeStaticStr::Borrowed); + self + } + + /// Set [`module_path`](struct.Record.html#method.module_path) to a `'static` string + #[inline] + pub fn module_path_static(&mut self, path: Option<&'static str>) -> &mut RecordBuilder<'a> { + self.record.module_path = path.map(MaybeStaticStr::Static); + self + } + + /// Set [`file`](struct.Record.html#method.file) + #[inline] + pub fn file(&mut self, file: Option<&'a str>) -> &mut RecordBuilder<'a> { + self.record.file = file.map(MaybeStaticStr::Borrowed); + self + } + + /// Set [`file`](struct.Record.html#method.file) to a `'static` string. + #[inline] + pub fn file_static(&mut self, file: Option<&'static str>) -> &mut RecordBuilder<'a> { + self.record.file = file.map(MaybeStaticStr::Static); + self + } + + /// Set [`line`](struct.Record.html#method.line) + #[inline] + pub fn line(&mut self, line: Option) -> &mut RecordBuilder<'a> { + self.record.line = line; + self + } + + /// Set [`key_values`](struct.Record.html#method.key_values) + #[cfg(feature = "kv")] + #[inline] + pub fn key_values(&mut self, kvs: &'a dyn kv::Source) -> &mut RecordBuilder<'a> { + self.record.key_values = KeyValues(kvs); + self + } + + /// Invoke the builder and return a `Record` + #[inline] + pub fn build(&self) -> Record<'a> { + self.record.clone() + } +} + +impl Default for RecordBuilder<'_> { + fn default() -> Self { + Self::new() + } +} + +/// Metadata about a log message. +/// +/// # Use +/// +/// `Metadata` structs are created when users of the library use +/// logging macros. +/// +/// They are consumed by implementations of the `Log` trait in the +/// `enabled` method. +/// +/// `Record`s use `Metadata` to determine the log message's severity +/// and target. +/// +/// Users should use the `log_enabled!` macro in their code to avoid +/// constructing expensive log messages. +/// +/// # Examples +/// +/// ``` +/// use log::{Record, Level, Metadata}; +/// +/// struct MyLogger; +/// +/// impl log::Log for MyLogger { +/// fn enabled(&self, metadata: &Metadata) -> bool { +/// metadata.level() <= Level::Info +/// } +/// +/// fn log(&self, record: &Record) { +/// if self.enabled(record.metadata()) { +/// println!("{} - {}", record.level(), record.args()); +/// } +/// } +/// fn flush(&self) {} +/// } +/// +/// # fn main(){} +/// ``` +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct Metadata<'a> { + level: Level, + target: &'a str, +} + +impl<'a> Metadata<'a> { + /// Returns a new builder. + #[inline] + pub fn builder() -> MetadataBuilder<'a> { + MetadataBuilder::new() + } + + /// The verbosity level of the message. + #[inline] + pub fn level(&self) -> Level { + self.level + } + + /// The name of the target of the directive. + #[inline] + pub fn target(&self) -> &'a str { + self.target + } +} + +/// Builder for [`Metadata`](struct.Metadata.html). +/// +/// Typically should only be used by log library creators or for testing and "shim loggers". +/// The `MetadataBuilder` can set the different parameters of a `Metadata` object, and returns +/// the created object when `build` is called. +/// +/// # Example +/// +/// ``` +/// let target = "myApp"; +/// use log::{Level, MetadataBuilder}; +/// let metadata = MetadataBuilder::new() +/// .level(Level::Debug) +/// .target(target) +/// .build(); +/// ``` +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct MetadataBuilder<'a> { + metadata: Metadata<'a>, +} + +impl<'a> MetadataBuilder<'a> { + /// Construct a new `MetadataBuilder`. + /// + /// The default options are: + /// + /// - `level`: `Level::Info` + /// - `target`: `""` + #[inline] + pub fn new() -> MetadataBuilder<'a> { + MetadataBuilder { + metadata: Metadata { + level: Level::Info, + target: "", + }, + } + } + + /// Setter for [`level`](struct.Metadata.html#method.level). + #[inline] + pub fn level(&mut self, arg: Level) -> &mut MetadataBuilder<'a> { + self.metadata.level = arg; + self + } + + /// Setter for [`target`](struct.Metadata.html#method.target). + #[inline] + pub fn target(&mut self, target: &'a str) -> &mut MetadataBuilder<'a> { + self.metadata.target = target; + self + } + + /// Returns a `Metadata` object. + #[inline] + pub fn build(&self) -> Metadata<'a> { + self.metadata.clone() + } +} + +impl Default for MetadataBuilder<'_> { + fn default() -> Self { + Self::new() + } +} + +/// A trait encapsulating the operations required of a logger. +pub trait Log: Sync + Send { + /// Determines if a log message with the specified metadata would be + /// logged. + /// + /// This is used by the `log_enabled!` macro to allow callers to avoid + /// expensive computation of log message arguments if the message would be + /// discarded anyway. + /// + /// # For implementors + /// + /// This method isn't called automatically by the `log!` macros. + /// It's up to an implementation of the `Log` trait to call `enabled` in its own + /// `log` method implementation to guarantee that filtering is applied. + fn enabled(&self, metadata: &Metadata) -> bool; + + /// Logs the `Record`. + /// + /// # For implementors + /// + /// Note that `enabled` is *not* necessarily called before this method. + /// Implementations of `log` should perform all necessary filtering + /// internally. + fn log(&self, record: &Record); + + /// Flushes any buffered records. + /// + /// # For implementors + /// + /// This method isn't called automatically by the `log!` macros. + /// It can be called manually on shut-down to ensure any in-flight records are flushed. + fn flush(&self); +} + +/// A dummy initial value for LOGGER. +struct NopLogger; + +impl Log for NopLogger { + fn enabled(&self, _: &Metadata) -> bool { + false + } + + fn log(&self, _: &Record) {} + fn flush(&self) {} +} + +impl Log for &'_ T +where + T: ?Sized + Log, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + (**self).enabled(metadata) + } + + fn log(&self, record: &Record) { + (**self).log(record); + } + fn flush(&self) { + (**self).flush(); + } +} + +#[cfg(feature = "std")] +impl Log for std::boxed::Box +where + T: ?Sized + Log, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + self.as_ref().enabled(metadata) + } + + fn log(&self, record: &Record) { + self.as_ref().log(record); + } + fn flush(&self) { + self.as_ref().flush(); + } +} + +#[cfg(feature = "std")] +impl Log for std::sync::Arc +where + T: ?Sized + Log, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + self.as_ref().enabled(metadata) + } + + fn log(&self, record: &Record) { + self.as_ref().log(record); + } + fn flush(&self) { + self.as_ref().flush(); + } +} + +/// Sets the global maximum log level. +/// +/// Generally, this should only be called by the active logging implementation. +/// +/// Note that `Trace` is the maximum level, because it provides the maximum amount of detail in the emitted logs. +#[inline] +#[cfg(target_has_atomic = "ptr")] +pub fn set_max_level(level: LevelFilter) { + MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed); +} + +/// A thread-unsafe version of [`set_max_level`]. +/// +/// This function is available on all platforms, even those that do not have +/// support for atomics that is needed by [`set_max_level`]. +/// +/// In almost all cases, [`set_max_level`] should be preferred. +/// +/// # Safety +/// +/// This function is only safe to call when it cannot race with any other +/// calls to `set_max_level` or `set_max_level_racy`. +/// +/// This can be upheld by (for example) making sure that **there are no other +/// threads**, and (on embedded) that **interrupts are disabled**. +/// +/// It is safe to use all other logging functions while this function runs +/// (including all logging macros). +/// +/// [`set_max_level`]: fn.set_max_level.html +#[inline] +pub unsafe fn set_max_level_racy(level: LevelFilter) { + // `MAX_LOG_LEVEL_FILTER` uses a `Cell` as the underlying primitive when a + // platform doesn't support `target_has_atomic = "ptr"`, so even though this looks the same + // as `set_max_level` it may have different safety properties. + MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed); +} + +/// Returns the current maximum log level. +/// +/// The [`log!`], [`error!`], [`warn!`], [`info!`], [`debug!`], and [`trace!`] macros check +/// this value and discard any message logged at a higher level. The maximum +/// log level is set by the [`set_max_level`] function. +/// +/// [`log!`]: macro.log.html +/// [`error!`]: macro.error.html +/// [`warn!`]: macro.warn.html +/// [`info!`]: macro.info.html +/// [`debug!`]: macro.debug.html +/// [`trace!`]: macro.trace.html +/// [`set_max_level`]: fn.set_max_level.html +#[inline(always)] +pub fn max_level() -> LevelFilter { + // Since `LevelFilter` is `repr(usize)`, + // this transmute is sound if and only if `MAX_LOG_LEVEL_FILTER` + // is set to a usize that is a valid discriminant for `LevelFilter`. + // Since `MAX_LOG_LEVEL_FILTER` is private, the only time it's set + // is by `set_max_level` above, i.e. by casting a `LevelFilter` to `usize`. + // So any usize stored in `MAX_LOG_LEVEL_FILTER` is a valid discriminant. + unsafe { mem::transmute(MAX_LOG_LEVEL_FILTER.load(Ordering::Relaxed)) } +} + +/// Sets the global logger to a `Box`. +/// +/// This is a simple convenience wrapper over `set_logger`, which takes a +/// `Box` rather than a `&'static Log`. See the documentation for +/// [`set_logger`] for more details. +/// +/// Requires the `std` feature. +/// +/// # Errors +/// +/// An error is returned if a logger has already been set. +/// +/// [`set_logger`]: fn.set_logger.html +#[cfg(all(feature = "std", target_has_atomic = "ptr"))] +pub fn set_boxed_logger(logger: Box) -> Result<(), SetLoggerError> { + set_logger_inner(|| Box::leak(logger)) +} + +/// Sets the global logger to a `&'static Log`. +/// +/// This function may only be called once in the lifetime of a program. Any log +/// events that occur before the call to `set_logger` completes will be ignored. +/// +/// This function does not typically need to be called manually. Logger +/// implementations should provide an initialization method that installs the +/// logger internally. +/// +/// # Availability +/// +/// This method is available even when the `std` feature is disabled. However, +/// it is currently unavailable on `thumbv6` targets, which lack support for +/// some atomic operations which are used by this function. Even on those +/// targets, [`set_logger_racy`] will be available. +/// +/// # Errors +/// +/// An error is returned if a logger has already been set. +/// +/// # Examples +/// +/// ``` +/// use log::{error, info, warn, Record, Level, Metadata, LevelFilter}; +/// +/// static MY_LOGGER: MyLogger = MyLogger; +/// +/// struct MyLogger; +/// +/// impl log::Log for MyLogger { +/// fn enabled(&self, metadata: &Metadata) -> bool { +/// metadata.level() <= Level::Info +/// } +/// +/// fn log(&self, record: &Record) { +/// if self.enabled(record.metadata()) { +/// println!("{} - {}", record.level(), record.args()); +/// } +/// } +/// fn flush(&self) {} +/// } +/// +/// # fn main(){ +/// log::set_logger(&MY_LOGGER).unwrap(); +/// log::set_max_level(LevelFilter::Info); +/// +/// info!("hello log"); +/// warn!("warning"); +/// error!("oops"); +/// # } +/// ``` +/// +/// [`set_logger_racy`]: fn.set_logger_racy.html +#[cfg(target_has_atomic = "ptr")] +pub fn set_logger(logger: &'static dyn Log) -> Result<(), SetLoggerError> { + set_logger_inner(|| logger) +} + +#[cfg(target_has_atomic = "ptr")] +fn set_logger_inner(make_logger: F) -> Result<(), SetLoggerError> +where + F: FnOnce() -> &'static dyn Log, +{ + match STATE.compare_exchange( + UNINITIALIZED, + INITIALIZING, + Ordering::Acquire, + Ordering::Relaxed, + ) { + Ok(UNINITIALIZED) => { + unsafe { + LOGGER = make_logger(); + } + STATE.store(INITIALIZED, Ordering::Release); + Ok(()) + } + Err(INITIALIZING) => { + while STATE.load(Ordering::Relaxed) == INITIALIZING { + std::hint::spin_loop(); + } + Err(SetLoggerError(())) + } + _ => Err(SetLoggerError(())), + } +} + +/// A thread-unsafe version of [`set_logger`]. +/// +/// This function is available on all platforms, even those that do not have +/// support for atomics that is needed by [`set_logger`]. +/// +/// In almost all cases, [`set_logger`] should be preferred. +/// +/// # Safety +/// +/// This function is only safe to call when it cannot race with any other +/// calls to `set_logger` or `set_logger_racy`. +/// +/// This can be upheld by (for example) making sure that **there are no other +/// threads**, and (on embedded) that **interrupts are disabled**. +/// +/// It is safe to use other logging functions while this function runs +/// (including all logging macros). +/// +/// [`set_logger`]: fn.set_logger.html +pub unsafe fn set_logger_racy(logger: &'static dyn Log) -> Result<(), SetLoggerError> { + match STATE.load(Ordering::Acquire) { + UNINITIALIZED => { + LOGGER = logger; + STATE.store(INITIALIZED, Ordering::Release); + Ok(()) + } + INITIALIZING => { + // This is just plain UB, since we were racing another initialization function + unreachable!("set_logger_racy must not be used with other initialization functions") + } + _ => Err(SetLoggerError(())), + } +} + +/// The type returned by [`set_logger`] if [`set_logger`] has already been called. +/// +/// [`set_logger`]: fn.set_logger.html +#[allow(missing_copy_implementations)] +#[derive(Debug)] +pub struct SetLoggerError(()); + +impl fmt::Display for SetLoggerError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(SET_LOGGER_ERROR) + } +} + +// The Error trait is not available in libcore +#[cfg(feature = "std")] +impl error::Error for SetLoggerError {} + +/// The type returned by [`from_str`] when the string doesn't match any of the log levels. +/// +/// [`from_str`]: https://doc.rust-lang.org/std/str/trait.FromStr.html#tymethod.from_str +#[allow(missing_copy_implementations)] +#[derive(Debug, PartialEq, Eq)] +pub struct ParseLevelError(()); + +impl fmt::Display for ParseLevelError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(LEVEL_PARSE_ERROR) + } +} + +// The Error trait is not available in libcore +#[cfg(feature = "std")] +impl error::Error for ParseLevelError {} + +/// Returns a reference to the logger. +/// +/// If a logger has not been set, a no-op implementation is returned. +pub fn logger() -> &'static dyn Log { + // Acquire memory ordering guarantees that current thread would see any + // memory writes that happened before store of the value + // into `STATE` with memory ordering `Release` or stronger. + // + // Since the value `INITIALIZED` is written only after `LOGGER` was + // initialized, observing it after `Acquire` load here makes both + // write to the `LOGGER` static and initialization of the logger + // internal state synchronized with current thread. + if STATE.load(Ordering::Acquire) != INITIALIZED { + static NOP: NopLogger = NopLogger; + &NOP + } else { + unsafe { LOGGER } + } +} + +// WARNING: this is not part of the crate's public API and is subject to change at any time +#[doc(hidden)] +pub mod __private_api; + +/// The statically resolved maximum log level. +/// +/// See the crate level documentation for information on how to configure this. +/// +/// This value is checked by the log macros, but not by the `Log`ger returned by +/// the [`logger`] function. Code that manually calls functions on that value +/// should compare the level against this value. +/// +/// [`logger`]: fn.logger.html +pub const STATIC_MAX_LEVEL: LevelFilter = match cfg!(debug_assertions) { + false if cfg!(feature = "release_max_level_off") => LevelFilter::Off, + false if cfg!(feature = "release_max_level_error") => LevelFilter::Error, + false if cfg!(feature = "release_max_level_warn") => LevelFilter::Warn, + false if cfg!(feature = "release_max_level_info") => LevelFilter::Info, + false if cfg!(feature = "release_max_level_debug") => LevelFilter::Debug, + false if cfg!(feature = "release_max_level_trace") => LevelFilter::Trace, + _ if cfg!(feature = "max_level_off") => LevelFilter::Off, + _ if cfg!(feature = "max_level_error") => LevelFilter::Error, + _ if cfg!(feature = "max_level_warn") => LevelFilter::Warn, + _ if cfg!(feature = "max_level_info") => LevelFilter::Info, + _ if cfg!(feature = "max_level_debug") => LevelFilter::Debug, + _ => LevelFilter::Trace, +}; + +#[cfg(test)] +mod tests { + use super::{Level, LevelFilter, ParseLevelError, STATIC_MAX_LEVEL}; + + #[test] + fn test_levelfilter_from_str() { + let tests = [ + ("off", Ok(LevelFilter::Off)), + ("error", Ok(LevelFilter::Error)), + ("warn", Ok(LevelFilter::Warn)), + ("info", Ok(LevelFilter::Info)), + ("debug", Ok(LevelFilter::Debug)), + ("trace", Ok(LevelFilter::Trace)), + ("OFF", Ok(LevelFilter::Off)), + ("ERROR", Ok(LevelFilter::Error)), + ("WARN", Ok(LevelFilter::Warn)), + ("INFO", Ok(LevelFilter::Info)), + ("DEBUG", Ok(LevelFilter::Debug)), + ("TRACE", Ok(LevelFilter::Trace)), + ("asdf", Err(ParseLevelError(()))), + ]; + for &(s, ref expected) in &tests { + assert_eq!(expected, &s.parse()); + } + } + + #[test] + fn test_level_from_str() { + let tests = [ + ("OFF", Err(ParseLevelError(()))), + ("error", Ok(Level::Error)), + ("warn", Ok(Level::Warn)), + ("info", Ok(Level::Info)), + ("debug", Ok(Level::Debug)), + ("trace", Ok(Level::Trace)), + ("ERROR", Ok(Level::Error)), + ("WARN", Ok(Level::Warn)), + ("INFO", Ok(Level::Info)), + ("DEBUG", Ok(Level::Debug)), + ("TRACE", Ok(Level::Trace)), + ("asdf", Err(ParseLevelError(()))), + ]; + for &(s, ref expected) in &tests { + assert_eq!(expected, &s.parse()); + } + } + + #[test] + fn test_level_as_str() { + let tests = &[ + (Level::Error, "ERROR"), + (Level::Warn, "WARN"), + (Level::Info, "INFO"), + (Level::Debug, "DEBUG"), + (Level::Trace, "TRACE"), + ]; + for (input, expected) in tests { + assert_eq!(*expected, input.as_str()); + } + } + + #[test] + fn test_level_show() { + assert_eq!("INFO", Level::Info.to_string()); + assert_eq!("ERROR", Level::Error.to_string()); + } + + #[test] + fn test_levelfilter_show() { + assert_eq!("OFF", LevelFilter::Off.to_string()); + assert_eq!("ERROR", LevelFilter::Error.to_string()); + } + + #[test] + fn test_cross_cmp() { + assert!(Level::Debug > LevelFilter::Error); + assert!(LevelFilter::Warn < Level::Trace); + assert!(LevelFilter::Off < Level::Error); + } + + #[test] + fn test_cross_eq() { + assert!(Level::Error == LevelFilter::Error); + assert!(LevelFilter::Off != Level::Error); + assert!(Level::Trace == LevelFilter::Trace); + } + + #[test] + fn test_to_level() { + assert_eq!(Some(Level::Error), LevelFilter::Error.to_level()); + assert_eq!(None, LevelFilter::Off.to_level()); + assert_eq!(Some(Level::Debug), LevelFilter::Debug.to_level()); + } + + #[test] + fn test_to_level_filter() { + assert_eq!(LevelFilter::Error, Level::Error.to_level_filter()); + assert_eq!(LevelFilter::Trace, Level::Trace.to_level_filter()); + } + + #[test] + fn test_level_filter_as_str() { + let tests = &[ + (LevelFilter::Off, "OFF"), + (LevelFilter::Error, "ERROR"), + (LevelFilter::Warn, "WARN"), + (LevelFilter::Info, "INFO"), + (LevelFilter::Debug, "DEBUG"), + (LevelFilter::Trace, "TRACE"), + ]; + for (input, expected) in tests { + assert_eq!(*expected, input.as_str()); + } + } + + #[test] + #[cfg_attr(not(debug_assertions), ignore)] + fn test_static_max_level_debug() { + if cfg!(feature = "max_level_off") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Off); + } else if cfg!(feature = "max_level_error") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Error); + } else if cfg!(feature = "max_level_warn") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Warn); + } else if cfg!(feature = "max_level_info") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Info); + } else if cfg!(feature = "max_level_debug") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Debug); + } else { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Trace); + } + } + + #[test] + #[cfg_attr(debug_assertions, ignore)] + fn test_static_max_level_release() { + if cfg!(feature = "release_max_level_off") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Off); + } else if cfg!(feature = "release_max_level_error") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Error); + } else if cfg!(feature = "release_max_level_warn") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Warn); + } else if cfg!(feature = "release_max_level_info") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Info); + } else if cfg!(feature = "release_max_level_debug") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Debug); + } else if cfg!(feature = "release_max_level_trace") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Trace); + } else if cfg!(feature = "max_level_off") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Off); + } else if cfg!(feature = "max_level_error") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Error); + } else if cfg!(feature = "max_level_warn") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Warn); + } else if cfg!(feature = "max_level_info") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Info); + } else if cfg!(feature = "max_level_debug") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Debug); + } else { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Trace); + } + } + + #[test] + #[cfg(feature = "std")] + fn test_error_trait() { + use super::SetLoggerError; + let e = SetLoggerError(()); + assert_eq!( + &e.to_string(), + "attempted to set a logger after the logging system \ + was already initialized" + ); + } + + #[test] + fn test_metadata_builder() { + use super::MetadataBuilder; + let target = "myApp"; + let metadata_test = MetadataBuilder::new() + .level(Level::Debug) + .target(target) + .build(); + assert_eq!(metadata_test.level(), Level::Debug); + assert_eq!(metadata_test.target(), "myApp"); + } + + #[test] + fn test_metadata_convenience_builder() { + use super::Metadata; + let target = "myApp"; + let metadata_test = Metadata::builder() + .level(Level::Debug) + .target(target) + .build(); + assert_eq!(metadata_test.level(), Level::Debug); + assert_eq!(metadata_test.target(), "myApp"); + } + + #[test] + fn test_record_builder() { + use super::{MetadataBuilder, RecordBuilder}; + let target = "myApp"; + let metadata = MetadataBuilder::new().target(target).build(); + let fmt_args = format_args!("hello"); + let record_test = RecordBuilder::new() + .args(fmt_args) + .metadata(metadata) + .module_path(Some("foo")) + .file(Some("bar")) + .line(Some(30)) + .build(); + assert_eq!(record_test.metadata().target(), "myApp"); + assert_eq!(record_test.module_path(), Some("foo")); + assert_eq!(record_test.file(), Some("bar")); + assert_eq!(record_test.line(), Some(30)); + } + + #[test] + fn test_record_convenience_builder() { + use super::{Metadata, Record}; + let target = "myApp"; + let metadata = Metadata::builder().target(target).build(); + let fmt_args = format_args!("hello"); + let record_test = Record::builder() + .args(fmt_args) + .metadata(metadata) + .module_path(Some("foo")) + .file(Some("bar")) + .line(Some(30)) + .build(); + assert_eq!(record_test.target(), "myApp"); + assert_eq!(record_test.module_path(), Some("foo")); + assert_eq!(record_test.file(), Some("bar")); + assert_eq!(record_test.line(), Some(30)); + } + + #[test] + fn test_record_complete_builder() { + use super::{Level, Record}; + let target = "myApp"; + let record_test = Record::builder() + .module_path(Some("foo")) + .file(Some("bar")) + .line(Some(30)) + .target(target) + .level(Level::Error) + .build(); + assert_eq!(record_test.target(), "myApp"); + assert_eq!(record_test.level(), Level::Error); + assert_eq!(record_test.module_path(), Some("foo")); + assert_eq!(record_test.file(), Some("bar")); + assert_eq!(record_test.line(), Some(30)); + } + + #[test] + #[cfg(feature = "kv")] + fn test_record_key_values_builder() { + use super::Record; + use crate::kv::{self, VisitSource}; + + struct TestVisitSource { + seen_pairs: usize, + } + + impl<'kvs> VisitSource<'kvs> for TestVisitSource { + fn visit_pair( + &mut self, + _: kv::Key<'kvs>, + _: kv::Value<'kvs>, + ) -> Result<(), kv::Error> { + self.seen_pairs += 1; + Ok(()) + } + } + + let kvs: &[(&str, i32)] = &[("a", 1), ("b", 2)]; + let record_test = Record::builder().key_values(&kvs).build(); + + let mut visitor = TestVisitSource { seen_pairs: 0 }; + + record_test.key_values().visit(&mut visitor).unwrap(); + + assert_eq!(2, visitor.seen_pairs); + } + + #[test] + #[cfg(feature = "kv")] + fn test_record_key_values_get_coerce() { + use super::Record; + + let kvs: &[(&str, &str)] = &[("a", "1"), ("b", "2")]; + let record = Record::builder().key_values(&kvs).build(); + + assert_eq!( + "2", + record + .key_values() + .get("b".into()) + .expect("missing key") + .to_borrowed_str() + .expect("invalid value") + ); + } + + // Test that the `impl Log for Foo` blocks work + // This test mostly operates on a type level, so failures will be compile errors + #[test] + fn test_foreign_impl() { + use super::Log; + #[cfg(feature = "std")] + use std::sync::Arc; + + fn assert_is_log() {} + + assert_is_log::<&dyn Log>(); + + #[cfg(feature = "std")] + assert_is_log::>(); + + #[cfg(feature = "std")] + assert_is_log::>(); + + // Assert these statements for all T: Log + ?Sized + #[allow(unused)] + fn forall() { + #[cfg(feature = "std")] + assert_is_log::>(); + + assert_is_log::<&T>(); + + #[cfg(feature = "std")] + assert_is_log::>(); + } + } +} diff --git a/src/mw_log/src/macros.rs b/src/mw_log/src/macros.rs new file mode 100644 index 0000000..14e4ac6 --- /dev/null +++ b/src/mw_log/src/macros.rs @@ -0,0 +1,579 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The standard logging macro. +/// +/// This macro will generically log with the specified `Level` and `format!` +/// based argument list. +/// +/// ``` +/// use log::{log, Level}; +/// +/// let data = (42, "Forty-two"); +/// let private_data = "private"; +/// +/// log!(Level::Error, "Received errors: {}, {}", data.0, data.1); +/// ``` +/// +/// Optionally, you can specify a `target` argument to attach a specific target +/// to the log record. By default, the target is the module path of the caller. +/// +/// ``` +/// use log::{log, Level}; +/// +/// let data = (42, "Forty-two"); +/// let private_data = "private"; +/// +/// log!( +/// target: "app_events", +/// Level::Error, +/// "Received errors: {}, {}", +/// data.0, data.1 +/// ); +/// ``` +/// +/// And optionally, you can specify a `logger` argument to use a specific logger +/// instead of the default global logger. +/// +/// ``` +/// # struct MyLogger {} +/// # impl Log for MyLogger { +/// # fn enabled(&self, _metadata: &log::Metadata) -> bool { +/// # false +/// # } +/// # fn log(&self, _record: &log::Record) {} +/// # fn flush(&self) {} +/// # } +/// use log::{log, Level, Log}; +/// +/// let data = (42, "Forty-two"); +/// let private_data = "private"; +/// +/// let my_logger = MyLogger {}; +/// log!( +/// logger: my_logger, +/// Level::Error, +/// "Received errors: {}, {}", +/// data.0, data.1 +/// ); +/// ``` +/// +/// The `logger` argument accepts a value that implements the `Log` trait. The value +/// will be borrowed within the macro. +/// +/// Note that the global level set via Cargo features, or through `set_max_level` will +/// still apply, even when a custom logger is supplied with the `logger` argument. +#[macro_export] +#[clippy::format_args] +macro_rules! log { + // log!(logger: my_logger, target: "my_target", Level::Info, "a {} event", "log"); + (logger: $logger:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + $crate::__log!( + logger: $crate::__log_logger!($logger), + target: $target, + $lvl, + $($arg)+ + ) + }); + + // log!(logger: my_logger, Level::Info, "a log event") + (logger: $logger:expr, $lvl:expr, $($arg:tt)+) => ({ + $crate::__log!( + logger: $crate::__log_logger!($logger), + target: $crate::__private_api::module_path!(), + $lvl, + $($arg)+ + ) + }); + + // log!(target: "my_target", Level::Info, "a log event") + (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + $crate::__log!( + logger: $crate::__log_logger!(__log_global_logger), + target: $target, + $lvl, + $($arg)+ + ) + }); + + // log!(Level::Info, "a log event") + ($lvl:expr, $($arg:tt)+) => ({ + $crate::__log!( + logger: $crate::__log_logger!(__log_global_logger), + target: $crate::__private_api::module_path!(), + $lvl, + $($arg)+ + ) + }); +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __log { + // log!(logger: my_logger, target: "my_target", Level::Info, key1:? = 42, key2 = true; "a {} event", "log"); + (logger: $logger:expr, target: $target:expr, $lvl:expr, $($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { + $crate::__private_api::log( + $logger, + $crate::__private_api::format_args!($($arg)+), + lvl, + &($target, $crate::__private_api::module_path!(), $crate::__private_api::loc()), + &[$(($crate::__log_key!($key), $crate::__log_value!($key $(:$capture)* = $($value)*))),+] as &[_], + ); + } + }); + + // log!(logger: my_logger, target: "my_target", Level::Info, "a {} event", "log"); + (logger: $logger:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { + $crate::__private_api::log( + $logger, + $crate::__private_api::format_args!($($arg)+), + lvl, + &($target, $crate::__private_api::module_path!(), $crate::__private_api::loc()), + (), + ); + } + }); +} + +/// Logs a message at the error level. +/// +/// # Examples +/// +/// ``` +/// use log::error; +/// +/// # let my_logger = log::__private_api::GlobalLogger; +/// let (err_info, port) = ("No connection", 22); +/// +/// error!("Error: {err_info} on port {port}"); +/// error!(target: "app_events", "App Error: {err_info}, Port: {port}"); +/// error!(logger: my_logger, "App Error: {err_info}, Port: {port}"); +/// ``` +#[macro_export] +#[clippy::format_args] +macro_rules! error { + // error!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // error!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), target: $target, $crate::Level::Error, $($arg)+) + }); + + // error!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // error!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), $crate::Level::Error, $($arg)+) + }); + + // error!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // error!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Error, $($arg)+) + }); + + // error!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Error, $($arg)+)) +} + +/// Logs a message at the warn level. +/// +/// # Examples +/// +/// ``` +/// use log::warn; +/// +/// # let my_logger = log::__private_api::GlobalLogger; +/// let warn_description = "Invalid Input"; +/// +/// warn!("Warning! {warn_description}!"); +/// warn!(target: "input_events", "App received warning: {warn_description}"); +/// warn!(logger: my_logger, "App received warning: {warn_description}"); +/// ``` +#[macro_export] +#[clippy::format_args] +macro_rules! warn { + // warn!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // warn!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), target: $target, $crate::Level::Warn, $($arg)+) + }); + + // warn!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // warn!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), $crate::Level::Warn, $($arg)+) + }); + + // warn!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // warn!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Warn, $($arg)+) + }); + + // warn!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Warn, $($arg)+)) +} + +/// Logs a message at the info level. +/// +/// # Examples +/// +/// ``` +/// use log::info; +/// +/// # let my_logger = log::__private_api::GlobalLogger; +/// # struct Connection { port: u32, speed: f32 } +/// let conn_info = Connection { port: 40, speed: 3.20 }; +/// +/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed); +/// info!( +/// target: "connection_events", +/// "Successful connection, port: {}, speed: {}", +/// conn_info.port, conn_info.speed +/// ); +/// info!( +/// logger: my_logger, +/// "Successful connection, port: {}, speed: {}", +/// conn_info.port, conn_info.speed +/// ); +/// ``` +#[macro_export] +#[clippy::format_args] +macro_rules! info { + // info!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // info!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), target: $target, $crate::Level::Info, $($arg)+) + }); + + // info!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // info!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), $crate::Level::Info, $($arg)+) + }); + + // info!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // info!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Info, $($arg)+) + }); + + // info!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Info, $($arg)+)) +} + +/// Logs a message at the debug level. +/// +/// # Examples +/// +/// ``` +/// use log::debug; +/// +/// # let my_logger = log::__private_api::GlobalLogger; +/// # struct Position { x: f32, y: f32 } +/// let pos = Position { x: 3.234, y: -1.223 }; +/// +/// debug!("New position: x: {}, y: {}", pos.x, pos.y); +/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y); +/// debug!(logger: my_logger, "New position: x: {}, y: {}", pos.x, pos.y); +/// ``` +#[macro_export] +#[clippy::format_args] +macro_rules! debug { + // debug!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // debug!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), target: $target, $crate::Level::Debug, $($arg)+) + }); + + // debug!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // debug!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), $crate::Level::Debug, $($arg)+) + }); + + // debug!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // debug!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Debug, $($arg)+) + }); + + // debug!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Debug, $($arg)+)) +} + +/// Logs a message at the trace level. +/// +/// # Examples +/// +/// ``` +/// use log::trace; +/// +/// # let my_logger = log::__private_api::GlobalLogger; +/// # struct Position { x: f32, y: f32 } +/// let pos = Position { x: 3.234, y: -1.223 }; +/// +/// trace!("Position is: x: {}, y: {}", pos.x, pos.y); +/// trace!(target: "app_events", "x is {} and y is {}", +/// if pos.x >= 0.0 { "positive" } else { "negative" }, +/// if pos.y >= 0.0 { "positive" } else { "negative" }); +/// trace!(logger: my_logger, "x is {} and y is {}", +/// if pos.x >= 0.0 { "positive" } else { "negative" }, +/// if pos.y >= 0.0 { "positive" } else { "negative" }); +/// ``` +#[macro_export] +#[clippy::format_args] +macro_rules! trace { + // trace!(logger: my_logger, target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // trace!(logger: my_logger, target: "my_target", "a {} event", "log") + (logger: $logger:expr, target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), target: $target, $crate::Level::Trace, $($arg)+) + }); + + // trace!(logger: my_logger, key1 = 42, key2 = true; "a {} event", "log") + // trace!(logger: my_logger, "a {} event", "log") + (logger: $logger:expr, $($arg:tt)+) => ({ + $crate::log!(logger: $crate::__log_logger!($logger), $crate::Level::Trace, $($arg)+) + }); + + // trace!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // trace!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ({ + $crate::log!(target: $target, $crate::Level::Trace, $($arg)+) + }); + + // trace!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Trace, $($arg)+)) +} + +/// Determines if a message logged at the specified level in that module will +/// be logged. +/// +/// This can be used to avoid expensive computation of log message arguments if +/// the message would be ignored anyway. +/// +/// # Examples +/// +/// ``` +/// use log::{debug, log_enabled, Level}; +/// +/// # struct Data { x: u32, y: u32 } +/// # fn expensive_call() -> Data { Data { x: 0, y: 0 } } +/// # let my_logger = log::__private_api::GlobalLogger; +/// if log_enabled!(Level::Debug) { +/// let data = expensive_call(); +/// debug!("expensive debug data: {} {}", data.x, data.y); +/// } +/// +/// if log_enabled!(target: "Global", Level::Debug) { +/// let data = expensive_call(); +/// debug!(target: "Global", "expensive debug data: {} {}", data.x, data.y); +/// } +/// +/// if log_enabled!(logger: my_logger, Level::Debug) { +/// let data = expensive_call(); +/// debug!(target: "Global", "expensive debug data: {} {}", data.x, data.y); +/// } +/// ``` +/// +/// This macro accepts the same `target` and `logger` arguments as [`macro@log`]. +#[macro_export] +macro_rules! log_enabled { + // log_enabled!(logger: my_logger, target: "my_target", Level::Info) + (logger: $logger:expr, target: $target:expr, $lvl:expr) => ({ + $crate::__log_enabled!(logger: $crate::__log_logger!($logger), target: $target, $lvl) + }); + + // log_enabled!(logger: my_logger, Level::Info) + (logger: $logger:expr, $lvl:expr) => ({ + $crate::__log_enabled!(logger: $crate::__log_logger!($logger), target: $crate::__private_api::module_path!(), $lvl) + }); + + // log_enabled!(target: "my_target", Level::Info) + (target: $target:expr, $lvl:expr) => ({ + $crate::__log_enabled!(logger: $crate::__log_logger!(__log_global_logger), target: $target, $lvl) + }); + + // log_enabled!(Level::Info) + ($lvl:expr) => ({ + $crate::__log_enabled!(logger: $crate::__log_logger!(__log_global_logger), target: $crate::__private_api::module_path!(), $lvl) + }); +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __log_enabled { + // log_enabled!(logger: my_logger, target: "my_target", Level::Info) + (logger: $logger:expr, target: $target:expr, $lvl:expr) => {{ + let lvl = $lvl; + lvl <= $crate::STATIC_MAX_LEVEL + && lvl <= $crate::max_level() + && $crate::__private_api::enabled($logger, lvl, $target) + }}; +} + +// Determine the logger to use, and whether to take it by-value or by reference + +#[doc(hidden)] +#[macro_export] +macro_rules! __log_logger { + (__log_global_logger) => {{ + $crate::__private_api::GlobalLogger + }}; + + ($logger:expr) => {{ + &($logger) + }}; +} + +// These macros use a pattern of #[cfg]s to produce nicer error +// messages when log features aren't available + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv")] +macro_rules! __log_key { + // key1 = 42 + ($($args:ident)*) => { + $crate::__private_api::stringify!($($args)*) + }; + // "key1" = 42 + ($($args:expr)*) => { + $($args)* + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv"))] +macro_rules! __log_key { + ($($args:tt)*) => { + compile_error!("key value support requires the `kv` feature of `log`") + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv")] +macro_rules! __log_value { + // Entrypoint + ($key:tt = $args:expr) => { + $crate::__log_value!(($args):value) + }; + ($key:tt :$capture:tt = $args:expr) => { + $crate::__log_value!(($args):$capture) + }; + ($key:ident =) => { + $crate::__log_value!(($key):value) + }; + ($key:ident :$capture:tt =) => { + $crate::__log_value!(($key):$capture) + }; + // ToValue + (($args:expr):value) => { + $crate::__private_api::capture_to_value(&&$args) + }; + // Debug + (($args:expr):?) => { + $crate::__private_api::capture_debug(&&$args) + }; + (($args:expr):debug) => { + $crate::__private_api::capture_debug(&&$args) + }; + // Display + (($args:expr):%) => { + $crate::__private_api::capture_display(&&$args) + }; + (($args:expr):display) => { + $crate::__private_api::capture_display(&&$args) + }; + //Error + (($args:expr):err) => { + $crate::__log_value_error!($args) + }; + // sval::Value + (($args:expr):sval) => { + $crate::__log_value_sval!($args) + }; + // serde::Serialize + (($args:expr):serde) => { + $crate::__log_value_serde!($args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv"))] +macro_rules! __log_value { + ($($args:tt)*) => { + compile_error!("key value support requires the `kv` feature of `log`") + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_sval")] +macro_rules! __log_value_sval { + ($args:expr) => { + $crate::__private_api::capture_sval(&&$args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_sval"))] +macro_rules! __log_value_sval { + ($args:expr) => { + compile_error!("capturing values as `sval::Value` requites the `kv_sval` feature of `log`") + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_serde")] +macro_rules! __log_value_serde { + ($args:expr) => { + $crate::__private_api::capture_serde(&&$args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_serde"))] +macro_rules! __log_value_serde { + ($args:expr) => { + compile_error!( + "capturing values as `serde::Serialize` requites the `kv_serde` feature of `log`" + ) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_std")] +macro_rules! __log_value_error { + ($args:expr) => { + $crate::__private_api::capture_error(&$args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_std"))] +macro_rules! __log_value_error { + ($args:expr) => { + compile_error!( + "capturing values as `std::error::Error` requites the `kv_std` feature of `log`" + ) + }; +} diff --git a/src/mw_log/src/serde.rs b/src/mw_log/src/serde.rs new file mode 100644 index 0000000..db73239 --- /dev/null +++ b/src/mw_log/src/serde.rs @@ -0,0 +1,397 @@ +#![cfg(feature = "serde")] + +use serde::de::{ + Deserialize, DeserializeSeed, Deserializer, EnumAccess, Error, Unexpected, VariantAccess, + Visitor, +}; +use serde::ser::{Serialize, Serializer}; + +use crate::{Level, LevelFilter, LOG_LEVEL_NAMES}; + +use std::fmt; +use std::str::{self, FromStr}; + +// The Deserialize impls are handwritten to be case-insensitive using FromStr. + +impl Serialize for Level { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + Level::Error => serializer.serialize_unit_variant("Level", 0, "ERROR"), + Level::Warn => serializer.serialize_unit_variant("Level", 1, "WARN"), + Level::Info => serializer.serialize_unit_variant("Level", 2, "INFO"), + Level::Debug => serializer.serialize_unit_variant("Level", 3, "DEBUG"), + Level::Trace => serializer.serialize_unit_variant("Level", 4, "TRACE"), + } + } +} + +impl<'de> Deserialize<'de> for Level { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LevelIdentifier; + + impl<'de> Visitor<'de> for LevelIdentifier { + type Value = Level; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level") + } + + fn visit_u64(self, v: u64) -> Result + where + E: Error, + { + let variant = LOG_LEVEL_NAMES[1..] + .get(v as usize) + .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?; + + self.visit_str(variant) + } + + fn visit_str(self, s: &str) -> Result + where + E: Error, + { + // Case-insensitive. + FromStr::from_str(s).map_err(|_| Error::unknown_variant(s, &LOG_LEVEL_NAMES[1..])) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + let variant = str::from_utf8(value) + .map_err(|_| Error::invalid_value(Unexpected::Bytes(value), &self))?; + + self.visit_str(variant) + } + } + + impl<'de> DeserializeSeed<'de> for LevelIdentifier { + type Value = Level; + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(LevelIdentifier) + } + } + + struct LevelEnum; + + impl<'de> Visitor<'de> for LevelEnum { + type Value = Level; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level") + } + + fn visit_enum(self, value: A) -> Result + where + A: EnumAccess<'de>, + { + let (level, variant) = value.variant_seed(LevelIdentifier)?; + // Every variant is a unit variant. + variant.unit_variant()?; + Ok(level) + } + } + + deserializer.deserialize_enum("Level", &LOG_LEVEL_NAMES[1..], LevelEnum) + } +} + +impl Serialize for LevelFilter { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + LevelFilter::Off => serializer.serialize_unit_variant("LevelFilter", 0, "OFF"), + LevelFilter::Error => serializer.serialize_unit_variant("LevelFilter", 1, "ERROR"), + LevelFilter::Warn => serializer.serialize_unit_variant("LevelFilter", 2, "WARN"), + LevelFilter::Info => serializer.serialize_unit_variant("LevelFilter", 3, "INFO"), + LevelFilter::Debug => serializer.serialize_unit_variant("LevelFilter", 4, "DEBUG"), + LevelFilter::Trace => serializer.serialize_unit_variant("LevelFilter", 5, "TRACE"), + } + } +} + +impl<'de> Deserialize<'de> for LevelFilter { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LevelFilterIdentifier; + + impl<'de> Visitor<'de> for LevelFilterIdentifier { + type Value = LevelFilter; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level filter") + } + + fn visit_u64(self, v: u64) -> Result + where + E: Error, + { + let variant = LOG_LEVEL_NAMES + .get(v as usize) + .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?; + + self.visit_str(variant) + } + + fn visit_str(self, s: &str) -> Result + where + E: Error, + { + // Case-insensitive. + FromStr::from_str(s).map_err(|_| Error::unknown_variant(s, &LOG_LEVEL_NAMES)) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + let variant = str::from_utf8(value) + .map_err(|_| Error::invalid_value(Unexpected::Bytes(value), &self))?; + + self.visit_str(variant) + } + } + + impl<'de> DeserializeSeed<'de> for LevelFilterIdentifier { + type Value = LevelFilter; + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(LevelFilterIdentifier) + } + } + + struct LevelFilterEnum; + + impl<'de> Visitor<'de> for LevelFilterEnum { + type Value = LevelFilter; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level filter") + } + + fn visit_enum(self, value: A) -> Result + where + A: EnumAccess<'de>, + { + let (level_filter, variant) = value.variant_seed(LevelFilterIdentifier)?; + // Every variant is a unit variant. + variant.unit_variant()?; + Ok(level_filter) + } + } + + deserializer.deserialize_enum("LevelFilter", &LOG_LEVEL_NAMES, LevelFilterEnum) + } +} + +#[cfg(test)] +mod tests { + use crate::{Level, LevelFilter}; + use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token}; + + fn level_token(variant: &'static str) -> Token { + Token::UnitVariant { + name: "Level", + variant, + } + } + + fn level_bytes_tokens(variant: &'static [u8]) -> [Token; 3] { + [ + Token::Enum { name: "Level" }, + Token::Bytes(variant), + Token::Unit, + ] + } + + fn level_variant_tokens(variant: u32) -> [Token; 3] { + [ + Token::Enum { name: "Level" }, + Token::U32(variant), + Token::Unit, + ] + } + + fn level_filter_token(variant: &'static str) -> Token { + Token::UnitVariant { + name: "LevelFilter", + variant, + } + } + + fn level_filter_bytes_tokens(variant: &'static [u8]) -> [Token; 3] { + [ + Token::Enum { + name: "LevelFilter", + }, + Token::Bytes(variant), + Token::Unit, + ] + } + + fn level_filter_variant_tokens(variant: u32) -> [Token; 3] { + [ + Token::Enum { + name: "LevelFilter", + }, + Token::U32(variant), + Token::Unit, + ] + } + + #[test] + fn test_level_ser_de() { + let cases = &[ + (Level::Error, [level_token("ERROR")]), + (Level::Warn, [level_token("WARN")]), + (Level::Info, [level_token("INFO")]), + (Level::Debug, [level_token("DEBUG")]), + (Level::Trace, [level_token("TRACE")]), + ]; + + for (s, expected) in cases { + assert_tokens(s, expected); + } + } + + #[test] + fn test_level_case_insensitive() { + let cases = &[ + (Level::Error, [level_token("error")]), + (Level::Warn, [level_token("warn")]), + (Level::Info, [level_token("info")]), + (Level::Debug, [level_token("debug")]), + (Level::Trace, [level_token("trace")]), + ]; + + for (s, expected) in cases { + assert_de_tokens(s, expected); + } + } + + #[test] + fn test_level_de_bytes() { + let cases = &[ + (Level::Error, level_bytes_tokens(b"ERROR")), + (Level::Warn, level_bytes_tokens(b"WARN")), + (Level::Info, level_bytes_tokens(b"INFO")), + (Level::Debug, level_bytes_tokens(b"DEBUG")), + (Level::Trace, level_bytes_tokens(b"TRACE")), + ]; + + for (value, tokens) in cases { + assert_de_tokens(value, tokens); + } + } + + #[test] + fn test_level_de_variant_index() { + let cases = &[ + (Level::Error, level_variant_tokens(0)), + (Level::Warn, level_variant_tokens(1)), + (Level::Info, level_variant_tokens(2)), + (Level::Debug, level_variant_tokens(3)), + (Level::Trace, level_variant_tokens(4)), + ]; + + for (value, tokens) in cases { + assert_de_tokens(value, tokens); + } + } + + #[test] + fn test_level_de_error() { + let msg = "unknown variant `errorx`, expected one of \ + `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`"; + assert_de_tokens_error::(&[level_token("errorx")], msg); + } + + #[test] + fn test_level_filter_ser_de() { + let cases = &[ + (LevelFilter::Off, [level_filter_token("OFF")]), + (LevelFilter::Error, [level_filter_token("ERROR")]), + (LevelFilter::Warn, [level_filter_token("WARN")]), + (LevelFilter::Info, [level_filter_token("INFO")]), + (LevelFilter::Debug, [level_filter_token("DEBUG")]), + (LevelFilter::Trace, [level_filter_token("TRACE")]), + ]; + + for (s, expected) in cases { + assert_tokens(s, expected); + } + } + + #[test] + fn test_level_filter_case_insensitive() { + let cases = &[ + (LevelFilter::Off, [level_filter_token("off")]), + (LevelFilter::Error, [level_filter_token("error")]), + (LevelFilter::Warn, [level_filter_token("warn")]), + (LevelFilter::Info, [level_filter_token("info")]), + (LevelFilter::Debug, [level_filter_token("debug")]), + (LevelFilter::Trace, [level_filter_token("trace")]), + ]; + + for (s, expected) in cases { + assert_de_tokens(s, expected); + } + } + + #[test] + fn test_level_filter_de_bytes() { + let cases = &[ + (LevelFilter::Off, level_filter_bytes_tokens(b"OFF")), + (LevelFilter::Error, level_filter_bytes_tokens(b"ERROR")), + (LevelFilter::Warn, level_filter_bytes_tokens(b"WARN")), + (LevelFilter::Info, level_filter_bytes_tokens(b"INFO")), + (LevelFilter::Debug, level_filter_bytes_tokens(b"DEBUG")), + (LevelFilter::Trace, level_filter_bytes_tokens(b"TRACE")), + ]; + + for (value, tokens) in cases { + assert_de_tokens(value, tokens); + } + } + + #[test] + fn test_level_filter_de_variant_index() { + let cases = &[ + (LevelFilter::Off, level_filter_variant_tokens(0)), + (LevelFilter::Error, level_filter_variant_tokens(1)), + (LevelFilter::Warn, level_filter_variant_tokens(2)), + (LevelFilter::Info, level_filter_variant_tokens(3)), + (LevelFilter::Debug, level_filter_variant_tokens(4)), + (LevelFilter::Trace, level_filter_variant_tokens(5)), + ]; + + for (value, tokens) in cases { + assert_de_tokens(value, tokens); + } + } + + #[test] + fn test_level_filter_de_error() { + let msg = "unknown variant `errorx`, expected one of \ + `OFF`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`"; + assert_de_tokens_error::(&[level_filter_token("errorx")], msg); + } +} From 3b3dd625111be9530eee70652762246cd5e3569e Mon Sep 17 00:00:00 2001 From: Pawel Rutka Date: Tue, 19 Aug 2025 15:42:30 +0200 Subject: [PATCH 3/4] logging: add subscriber based on baselibs logging Signed-off-by: Pawel Rutka --- .bazelrc | 14 + .bazelversion | 1 + .gitignore | 24 + .vscode/extensions.json | 21 + .vscode/restructuredtext.code-snippets | 347 +++ .vscode/settings.json | 106 + .yamlfmt | 3 + Cargo.lock | 208 +- Cargo.toml | 10 +- MODULE.bazel | 40 +- MODULE.bazel.lock | 1921 +++++++++++++++++ src/BUILD | 0 src/mw_log/BUILD | 23 + src/mw_log/Cargo.toml | 15 +- src/mw_log_subscriber/BUILD | 50 + src/mw_log_subscriber/Cargo.toml | 12 + .../examples/config/logging.json | 7 + src/mw_log_subscriber/examples/main.rs | 74 + src/mw_log_subscriber/src/lib.rs | 214 ++ src/mw_log_subscriber/src/mw_log_ffi.cpp | 113 + src/mw_log_subscriber/src/mw_log_ffi.rs | 66 + 21 files changed, 3050 insertions(+), 219 deletions(-) create mode 100644 .bazelrc create mode 100644 .bazelversion create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/restructuredtext.code-snippets create mode 100644 .vscode/settings.json create mode 100644 .yamlfmt create mode 100644 MODULE.bazel.lock delete mode 100644 src/BUILD create mode 100644 src/mw_log/BUILD create mode 100644 src/mw_log_subscriber/BUILD create mode 100644 src/mw_log_subscriber/Cargo.toml create mode 100644 src/mw_log_subscriber/examples/config/logging.json create mode 100644 src/mw_log_subscriber/examples/main.rs create mode 100644 src/mw_log_subscriber/src/lib.rs create mode 100644 src/mw_log_subscriber/src/mw_log_ffi.cpp create mode 100644 src/mw_log_subscriber/src/mw_log_ffi.rs diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..5fb4945 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,14 @@ +build --java_language_version=17 +build --tool_java_language_version=17 +build --java_runtime_version=remotejdk_17 +build --tool_java_runtime_version=remotejdk_17 + +test --test_output=errors + +common --registry=https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/ +common --registry=https://bcr.bazel.build + +# magic logging configuration +common --@score-baselibs//score/mw/log/detail/flags:KUse_Stub_Implementation_Only=False +common --@score-baselibs//score/mw/log/flags:KRemote_Logging=False +common --@score-baselibs//score/json:base_library=nlohmann diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..2bf50aa --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +8.3.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d3a5b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Generated by Cargo +# will have compiled files and executables +debug +target + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Generated by cargo mutants +# Contains mutation testing data +**/mutants.out*/ + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +build/ +bazel-* diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..ce87b69 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,21 @@ +{ + "recommendations": [ + // Editing *.drawio.svg files directly in VS Code + "hediet.vscode-drawio", + + // Some convenient extensions for editing reStructuredText files + "lextudio.restructuredtext", + + // Linting and live preview for score docs + "swyddfa.esbonio", + + // ErrorLens highlights errors and warnings in your code / docs + "usernamehw.errorlens", + + // Linting and formatting for Python (LSP via ruff server) + "charliermarsh.ruff", + + // BasedPyright for python various type checking improvements and pylance features + "detachhead.basedpyright", + ] +} diff --git a/.vscode/restructuredtext.code-snippets b/.vscode/restructuredtext.code-snippets new file mode 100644 index 0000000..bde982f --- /dev/null +++ b/.vscode/restructuredtext.code-snippets @@ -0,0 +1,347 @@ +{ + "std_req_directive": { + "prefix": "std_req__", + "description": "Create a Standard Requirement", + "body": [ + ".. std_req:: $1", + " :id: std_req__$2", + " :status: ${3|valid|}", + "", + " ${4}" + ] + }, + "std_wp_directive": { + "prefix": "std_wp__", + "description": "Create a Standard Work Product", + "body": [ + ".. std_wp:: $1", + " :id: std_wp__$2", + " :status: ${3|valid|}", + "", + " ${4}" + ] + }, + "workflow_directive": { + "prefix": "wf__", + "description": "Create a Workflow", + "body": [ + ".. workflow:: $1", + " :id: wf__$2", + " :status: ${3|valid, draft|}", + " :input: ${4}", + " :output: ${5}", + " :approved_by: ${6}", + " :responsible: ${7}", + "", + " ${8}" + ] + }, + "gd_req_directive": { + "prefix": "gd_req__", + "description": "Create a Process Requirements", + "body": [ + ".. gd_req:: $1", + " :id: gd_req__$2", + " :status: ${3|valid, draft|}", + "", + " ${4}" + ] + }, + "gd_temp_directive": { + "prefix": "gd_temp__", + "description": "Create a Process Template", + "body": [ + ".. gd_temp:: $1", + " :id: gd_temp__$2", + " :status: ${3|valid, draft|}", + "", + " ${4}" + ] + }, + "gd_chklst_directive": { + "prefix": "gd_chklst__", + "description": "Create a Process Checklist", + "body": [ + ".. gd_chklst:: $1", + " :id: gd_chklst__$2", + " :status: ${3|valid, draft|}", + "", + " ${4}" + ] + }, + "gd_guidl_directive": { + "prefix": "gd_guidl__", + "description": "Create a Process Guideline", + "body": [ + ".. gd_guidl:: $1", + " :id: gd_guidl__$2", + " :status: ${3|valid, draft|}", + "", + " ${4}" + ] + }, + "gd_method_directive": { + "prefix": "gd_meth__", + "description": "Create a Process Method", + "body": [ + ".. gd_method:: $1", + " :id: gd_meth__$2", + " :status: ${3|valid, draft|}", + "", + " ${4}" + ] + }, + "workproduct_directive": { + "prefix": "wp__", + "description": "Create a Workproduct", + "body": [ + ".. workproduct:: $1", + " :id: wp__$2", + " :status: ${3|valid, draft|}", + "", + " ${4}" + ] + }, + "role_directive": { + "prefix": "rl__", + "description": "Create a Role", + "body": [ + ".. role:: $1", + " :id: rl__$2", + "", + " ${3}" + ] + }, + "doc_concept_directive": { + "prefix": "doc_concept__", + "description": "Create a Concept Definition", + "body": [ + ".. doc_concept:: $1", + " :id: doc_concept__$2", + " :status: ${3|valid, draft|}", + "", + " ${4}" + ] + }, + "doc_getstrt_directive": { + "prefix": "doc_getstrt__", + "description": "Create a Getting Startet", + "body": [ + ".. doc_getstrt:: $1", + " :id: doc_getstrt__$2", + " :status: ${3|valid, draft|}", + "", + " ${4}" + ] + }, + "document_directive": { + "prefix": "doc__", + "description": "Create a Generic Document", + "body": [ + ".. document:: $1", + " :id: doc__$2", + " :safety: ${3|QM, ASIL_B, ASIL_D|}", + " :status: ${4|valid, draft, invalid|}", + "", + " ${5}" + ] + }, + "stkh_req_directive": { + "prefix": "stkh_req__", + "description": "Create a Stakeholder Requirement", + "body": [ + ".. stkh_req:: $1", + " :id: stkh_req__$2", + " :reqtype: ${3|Functional, Interface, Process, Legal, Non-Functional|}", + " :security: ${4|YES, NO|}", + " :safety: ${5|QM, ASIL_B, ASIL_D|}", + " :status: ${6|valid, invalid|}", + " :rationale: ${7}", + "", + " ${8}" + ] + }, + "feat_req_directive": { + "prefix": "feat_req__", + "description": "Create a Feature Requirement", + "body": [ + ".. feat_req:: $1", + " :id: feat_req__$2", + " :reqtype: ${3|Functional, Interface, Process, Legal, Non-Functional|}", + " :security: ${4|YES, NO|}", + " :safety: ${5|QM, ASIL_B, ASIL_D|}", + " :status: ${6|valid, invalid|}", + " :satisfies: ${7}", + "", + " ${8}" + ] + }, + "comp_req_directive": { + "prefix": "comp_req__", + "description": "Create a Component Requirement", + "body": [ + ".. comp_req:: $1", + " :id: comp_req__$2", + " :reqtype: ${3|Functional, Interface, Process, Legal, Non-Functional|}", + " :security: ${4|YES, NO|}", + " :safety: ${5|QM, ASIL_B, ASIL_D|}", + " :status: ${6|valid, invalid|}", + " :satisfies: ${7}", + "", + " ${8}" + ] + }, + "tool_req_directive": { + "prefix": "tool_req__", + "description": "Create a Tool Requirement", + "body": [ + ".. tool_req:: $1", + " :id: tool_req__$2", + " :reqtype: ${3|Functional, Interface, Process, Legal, Non-Functional|}", + " :security: ${4|YES, NO|}", + " :safety: ${5|QM, ASIL_B, ASIL_D|}", + " :status: ${6|valid, invalid|}", + " :satisfies: ${7}", + "", + " ${8}" + ] + }, + "aou_req_directive": { + "prefix": "aou_req__", + "description": "Create a Assumption of Use", + "body": [ + ".. aou_req:: $1", + " :id: aou_req__$2", + " :reqtype: ${3|Functional, Interface, Process, Legal, Non-Functional|}", + " :security: ${4|YES, NO|}", + " :safety: ${5|QM, ASIL_B, ASIL_D|}", + " :status: ${6|valid, invalid|}", + "", + " ${7}" + ] + }, + "feat_arc_sta_directive": { + "prefix": "feat_arc_sta__", + "description": "Create a Feature Architecture Static View", + "body": [ + ".. feat_arc_sta:: $1", + " :id: feat_arc_sta__$2", + " :security: ${3|YES, NO|}", + " :safety: ${4|QM, ASIL_B, ASIL_D|}", + " :status: ${5|valid, invalid|}", + " :satisfies: ${6}", + "", + " ${7}" + ] + }, + "feat_arc_dyn_directive": { + "prefix": "feat_arc_dyn__", + "description": "Create a Feature Architecture Dynamic View", + "body": [ + ".. feat_arc_dyn:: $1", + " :id: feat_arc_dyn__$2", + " :security: ${3|YES, NO|}", + " :safety: ${4|QM, ASIL_B, ASIL_D|}", + " :status: ${5|valid, invalid|}", + " :satisfies: ${6}", + "", + " ${7}" + ] + }, + "feat_arc_int_directive": { + "prefix": "feat_arc_int__", + "description": "Create a Feature Architecture Interfaces", + "body": [ + ".. feat_arc_int:: $1", + " :id: feat_arc_int__$2", + " :security: ${3|YES, NO|}", + " :safety: ${4|QM, ASIL_B, ASIL_D|}", + " :status: ${5|valid, invalid|}", + " :satisfies: ${6}", + "", + " ${7}" + ] + }, + "feat_arc_int_op_directive": { + "prefix": "feat_arc_int_op__", + "description": "Create a Feature Architecture Interface Operation", + "body": [ + ".. feat_arc_int_op:: $1", + " :id: feat_arc_int_op__$2", + " :security: ${3|YES, NO|}", + " :safety: ${4|QM, ASIL_B, ASIL_D|}", + " :status: ${5|valid, invalid|}", + "", + " ${6}" + ] + }, + "mod_arc_sta_directive": { + "prefix": "mod_arc_sta__", + "description": "Create a Module Architecture Static View", + "body": [ + ".. mod_arc_sta:: $1", + " :id: mod_arc_sta__$2", + " :security: ${3|YES, NO|}", + " :safety: ${4|QM, ASIL_B, ASIL_D|}", + " :status: ${5|valid, invalid|}", + "", + " ${6}" + ] + }, + "comp_arc_sta_directive": { + "prefix": "comp_arc_sta__", + "description": "Create a Component Architecture Static View", + "body": [ + ".. comp_arc_sta:: $1", + " :id: comp_arc_sta__$2", + " :security: ${3|YES, NO|}", + " :safety: ${4|QM, ASIL_B, ASIL_D|}", + " :status: ${5|valid, invalid|}", + " :satisfies: ${6}", + "", + " ${7}" + ] + }, + "comp_arc_dyn_directive": { + "prefix": "comp_arc_dyn__", + "description": "Create a Component Architecture Dynamic View", + "body": [ + ".. comp_arc_dyn:: $1", + " :id: comp_arc_dyn__$2", + " :security: ${3|YES, NO|}", + " :safety: ${4|QM, ASIL_B, ASIL_D|}", + " :status: ${5|valid, invalid|}", + " :satisfies: ${6}", + "", + " ${7}" + ] + }, + "comp_arc_int_directive": { + "prefix": "comp_arc_int__", + "description": "Create a Component Architecture Interfaces", + "body": [ + ".. comp_arc_int:: $1", + " :id: comp_arc_int__$2", + " :security: ${3|YES, NO|}", + " :safety: ${4|QM, ASIL_B, ASIL_D|}", + " :status: ${5|valid, invalid|}", + " :satisfies: ${6}", + "", + " ${7}" + ] + }, + "comp_arc_int_op_directive": { + "prefix": "comp_arc_int_op__", + "description": "Create a Component Architecture Interface Operation", + "body": [ + ".. comp_arc_int_op:: $1", + " :id: comp_arc_int_op__$2", + " :security: ${3|YES, NO|}", + " :safety: ${4|QM, ASIL_B, ASIL_D|}", + " :status: ${5|valid, invalid|}", + " :satisfies: ${6}", + "", + " ${7}" + ] + } +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..47cb954 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,106 @@ +{ + // General Settings + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "files.trimTrailingWhitespace": true, + "editor.insertSpaces": true, + "editor.tabCompletion": "on", + + // Default for any filetype + "editor.rulers": [ + 99 + ], + + // Exclude build, temp and cache folders + "files.watcherExclude": { + ".*/**": true, + "**/__pycache__/**": true, + "bazel-*/**": true, + ".venv*/**": true, + "_build/**": true, + }, + + // Python Settings + // Exclude build, temp and cache folders + "python.analysis.exclude": [ + // Note: this overrides the default setting, so we need to re-exclude defaults like .* and **/__pycache__ + ".*", + "**/__pycache__", + "bazel-*", + ".venv*", + "_build", + ], + "[python]": { + // In python using 80 characters per line is the standard. + "editor.rulers": [ + 79 + ], + // Opinionated option for the future: + // "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.sortImports": "explicit" + }, + "editor.defaultFormatter": "charliermarsh.ruff", + }, + + // Markdown Settings + "[markdown]": { + // We mostly write markdown in some combination with python, + // so we use the same rulers as python. + "editor.rulers": [ + 79, 99 + ] + }, + + "bazel.lsp.command": "bazel", + "bazel.lsp.args": [ + "run", + "//:starpls_server" + ], + + // RST Settings + "[restructuredtext]": { + "editor.tabSize": 3, + }, + // + // + // Esbonio 0.x (Current) + // see https://github.com/swyddfa/esbonio/blob/0.x/docs/lsp/getting-started.rst + // and https://github.com/swyddfa/esbonio/blob/0.x/docs/lsp/editors/vscode/_configuration.rst + "esbonio.server.pythonPath": "${workspaceFolder}/.venv_docs/bin/python", + "esbonio.sphinx.srcDir": "${workspaceFolder}/docs", + "esbonio.sphinx.confDir": "${workspaceFolder}/docs", + "esbonio.sphinx.buildDir": "${workspaceFolder}/_build", + "esbonio.server.logLevel": "info", + // Do not auto-install. We'll use the one in the venv. + "esbonio.server.installBehavior": "nothing", + // Enable port forwarding for preview if working on remote workstation + "remote.autoForwardPorts": true, + "remote.autoForwardPortsSource": "process", + // + // + // Esbonio 1.x (Preview) + "esbonio.sphinx.pythonCommand": [ + ".venv_docs/bin/python" + ], + "esbonio.sphinx.buildCommand": [ + "docs", + "_build", + "-T", // show details in case of errors in extensions + "--jobs", + "auto", + "--conf-dir", + "docs" + ], + // default is "error", which doesn't show anything. + "esbonio.logging.level": "warning", + "python.testing.pytestArgs": [ + ".", + "--ignore-glob=bazel-*/*", + "--ignore-glob=.venv_docs/*", + "--ignore-glob=_build/*", + + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, +} diff --git a/.yamlfmt b/.yamlfmt new file mode 100644 index 0000000..2078769 --- /dev/null +++ b/.yamlfmt @@ -0,0 +1,3 @@ +formatter: + type: basic + retain_line_breaks: true diff --git a/Cargo.lock b/Cargo.lock index 3d38df6..8ddffc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,214 +3,28 @@ version = 3 [[package]] -name = "erased-serde" -version = "0.4.6" +name = "cc" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" dependencies = [ - "serde", - "typeid", + "shlex", ] -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - [[package]] name = "mw_log" version = "0.0.1" -dependencies = [ - "value-bag", -] - -[[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 = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[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_fmt" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" -dependencies = [ - "serde", -] - -[[package]] -name = "sval" -version = "2.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9739f56c5d0c44a5ed45473ec868af02eb896af8c05f616673a31e1d1bb09" - -[[package]] -name = "sval_buffer" -version = "2.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f39b07436a8c271b34dad5070c634d1d3d76d6776e938ee97b4a66a5e8003d0b" -dependencies = [ - "sval", - "sval_ref", -] - -[[package]] -name = "sval_dynamic" -version = "2.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffcb072d857431bf885580dacecf05ed987bac931230736739a79051dbf3499b" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_fmt" -version = "2.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f214f427ad94a553e5ca5514c95c6be84667cbc5568cce957f03f3477d03d5c" -dependencies = [ - "itoa", - "ryu", - "sval", -] [[package]] -name = "sval_json" -version = "2.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ed34b32e638dec9a99c8ac92d0aa1220d40041026b625474c2b6a4d6f4feb" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_nested" -version = "2.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14bae8fcb2f24fee2c42c1f19037707f7c9a29a0cda936d2188d48a961c4bb2a" +name = "mw_log_subscriber" +version = "0.1.0" dependencies = [ - "sval", - "sval_buffer", - "sval_ref", + "cc", + "mw_log", ] [[package]] -name = "sval_ref" -version = "2.14.1" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4eaea3821d3046dcba81d4b8489421da42961889902342691fb7eab491d79e" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_serde" -version = "2.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172dd4aa8cb3b45c8ac8f3b4111d644cd26938b0643ede8f93070812b87fb339" -dependencies = [ - "serde", - "sval", - "sval_nested", -] - -[[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 = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "value-bag" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" -dependencies = [ - "value-bag-serde1", - "value-bag-sval2", -] - -[[package]] -name = "value-bag-serde1" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35540706617d373b118d550d41f5dfe0b78a0c195dc13c6815e92e2638432306" -dependencies = [ - "erased-serde", - "serde", - "serde_fmt", -] - -[[package]] -name = "value-bag-sval2" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe7e140a2658cc16f7ee7a86e413e803fc8f9b5127adc8755c19f9fefa63a52" -dependencies = [ - "sval", - "sval_buffer", - "sval_dynamic", - "sval_fmt", - "sval_json", - "sval_ref", - "sval_serde", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" diff --git a/Cargo.toml b/Cargo.toml index a3b18d2..d8182fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,14 @@ [workspace] members = [ - "src/mw_log" + "src/mw_log", + "src/mw_log_subscriber" ] resolver = "2" -[workspace.dependencies] +[workspace.package] +version = "0.1.0" +edition = "2021" +[workspace.dependencies] +mw_log = { path = "src/mw_log", features = ["std"] } +cc = "1.2.26" \ No newline at end of file diff --git a/MODULE.bazel b/MODULE.bazel index 44009a0..d021c75 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -11,8 +11,8 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* module( - name = "cpp_rust_template_repository", - version = "1.0", + name = "score-mw-log", + version = "0.1", ) bazel_dep(name = "rules_python", version = "1.4.1") @@ -27,7 +27,7 @@ python.toolchain( use_repo(python) # Add GoogleTest dependency -bazel_dep(name = "googletest", version = "1.14.0") +bazel_dep(name = "googletest", version = "1.15.0") # Rust rules for Bazel bazel_dep(name = "rules_rust", version = "0.56.0") @@ -41,17 +41,18 @@ bazel_dep(name = "score_starpls_lsp", version = "0.1.0") bazel_dep(name = "rules_cc", version = "0.1.1") # LLVM Toolchains Rules - host configuration -bazel_dep(name = "toolchains_llvm", version = "1.2.0") +bazel_dep(name = "toolchains_llvm", version = "1.3.0") -llvm = use_extension("@toolchains_llvm//toolchain/extensions:llvm.bzl", "llvm") -llvm.toolchain( - cxx_standard = {"": "c++17"}, - llvm_version = "19.1.0", +# Configure and register the toolchain. +bazel_dep(name = "score_toolchains_gcc", version = "0.4", dev_dependency=True) +gcc = use_extension("@score_toolchains_gcc//extentions:gcc.bzl", "gcc", dev_dependency=True) +gcc.toolchain( + url = "https://github.com/eclipse-score/toolchains_gcc_packages/releases/download/0.0.1/x86_64-unknown-linux-gnu_gcc12.tar.gz", + sha256 = "457f5f20f57528033cb840d708b507050d711ae93e009388847e113b11bf3600", + strip_prefix = "x86_64-unknown-linux-gnu", ) -use_repo(llvm, "llvm_toolchain") -use_repo(llvm, "llvm_toolchain_llvm") -register_toolchains("@llvm_toolchain//:all") +use_repo(gcc, "gcc_toolchain", "gcc_toolchain_gcc") # Dash license checker bazel_dep(name = "score_dash_license_checker", version = "0.1.2") @@ -63,3 +64,20 @@ bazel_dep(name = "buildifier_prebuilt", version = "8.2.0.2") #docs-as-code bazel_dep(name = "score_docs_as_code", version = "1.0.1") + +# Dev + +# Has to be done since baselib have this as dep without version. +bazel_dep(name = "rules_boost", repo_name = "com_github_nelhage_rules_boost") +archive_override( + module_name = "rules_boost", + strip_prefix = "rules_boost-master", + urls = ["https://github.com/nelhage/rules_boost/archive/refs/heads/master.tar.gz"], +) + +bazel_dep(name = "score-baselibs", version = "0.1.0") +git_override( + module_name = "score-baselibs", + commit = "46923f5", # or use "tag" or "branch" + remote = "https://github.com/eclipse-score/baselibs.git", +) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock new file mode 100644 index 0000000..2ed1127 --- /dev/null +++ b/MODULE.bazel.lock @@ -0,0 +1,1921 @@ +{ + "lockFileVersion": 18, + "registryFileHashes": { + "https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2", + "https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589", + "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16", + "https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.2/MODULE.bazel": "73939767a4686cd9a520d16af5ab440071ed75cec1a876bf2fcfaf1f71987a16", + "https://bcr.bazel.build/modules/abseil-cpp/20240116.2/source.json": "750d5e29326fb59cbe61116a7b803c8a1d0a7090a9c8ed89888d188e3c473fc7", + "https://bcr.bazel.build/modules/ape/1.0.1/MODULE.bazel": "37411cfd13bfc28cd264674d660a3ecb3b5b35b9dbe4c0b2be098683641b3fee", + "https://bcr.bazel.build/modules/ape/1.0.1/source.json": "96bc5909d1e3ccc4203272815ef874dbfd99651e240c05049f12193d16c1110b", + "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", + "https://bcr.bazel.build/modules/apple_support/1.17.1/MODULE.bazel": "655c922ab1209978a94ef6ca7d9d43e940cd97d9c172fb55f94d91ac53f8610b", + "https://bcr.bazel.build/modules/apple_support/1.17.1/source.json": "6b2b8c74d14e8d485528a938e44bdb72a5ba17632b9e14ef6e68a5ee96c8347f", + "https://bcr.bazel.build/modules/aspect_bazel_lib/1.31.2/MODULE.bazel": "7bee702b4862612f29333590f4b658a5832d433d6f8e4395f090e8f4e85d442f", + "https://bcr.bazel.build/modules/aspect_bazel_lib/1.38.0/MODULE.bazel": "6307fec451ba9962c1c969eb516ebfe1e46528f7fa92e1c9ac8646bef4cdaa3f", + "https://bcr.bazel.build/modules/aspect_bazel_lib/1.42.2/MODULE.bazel": "2e0d8ab25c57a14f56ace1c8e881b69050417ff91b2fb7718dc00d201f3c3478", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.16.0/MODULE.bazel": "852f9ebbda017572a7c113a2434592dd3b2f55cd9a0faea3d4be5a09a59e4900", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.16.0/source.json": "87ffed720a2ba7cfe209d9ccc1be59e21ec3d434124ec126ab90e5913e9cb13b", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.7.7/MODULE.bazel": "491f8681205e31bb57892d67442ce448cda4f472a8e6b3dc062865e29a64f89c", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.9.4/MODULE.bazel": "ccc41028429f894b02fde7ef67d416cba3ba5084ed9ddb9bb6107aa82d118776", + "https://bcr.bazel.build/modules/aspect_rules_js/1.33.1/MODULE.bazel": "db3e7f16e471cf6827059d03af7c21859e7a0d2bc65429a3a11f005d46fc501b", + "https://bcr.bazel.build/modules/aspect_rules_js/1.40.0/MODULE.bazel": "01a1014e95e6816b68ecee2584ae929c7d6a1b72e4333ab1ff2d2c6c30babdf1", + "https://bcr.bazel.build/modules/aspect_rules_js/1.40.0/source.json": "b6fd491369e9ef888fdef64b839023a2360caaea8eb370d2cfbfdd2a96721311", + "https://bcr.bazel.build/modules/aspect_rules_lint/0.12.0/MODULE.bazel": "e767c5dbfeb254ec03275a7701b5cfde2c4d2873676804bc7cb27ddff3728fed", + "https://bcr.bazel.build/modules/aspect_rules_lint/1.0.3/MODULE.bazel": "ed0fe929647ba21d2041e14ea3d757133ca306b72d4998e8a3d0d2f515196765", + "https://bcr.bazel.build/modules/aspect_rules_lint/1.3.1/MODULE.bazel": "06ce330900a7d6403bc8d88e5dfad6aeeb8ae40179f66bb89e69c8bf6f6b1a0b", + "https://bcr.bazel.build/modules/aspect_rules_lint/1.4.2/MODULE.bazel": "78d025facf6fa675fd6f0b62fd6a9a2bec7ef5ae1e288e5b53f4383b98017105", + "https://bcr.bazel.build/modules/aspect_rules_lint/1.4.4/MODULE.bazel": "24459eeeeb084bc3e7628c338e494746718bc17b3a3cbd94415c8df5c7c6dc37", + "https://bcr.bazel.build/modules/aspect_rules_lint/1.4.4/source.json": "68a98376c993a8113ac3a5e75eaab588bd61f67e5d633dee31770f9a9a6c785b", + "https://bcr.bazel.build/modules/aspect_rules_py/1.0.0/MODULE.bazel": "8eb29876512d3242af50a424300bec5c5f8957b455963df5f618cb7fd4e8ae19", + "https://bcr.bazel.build/modules/aspect_rules_py/1.4.0/MODULE.bazel": "6fd29b93207a31445d5d3ab9d9882fd5511e43c95e8e82e7492872663720fd44", + "https://bcr.bazel.build/modules/aspect_rules_py/1.4.0/source.json": "fb1ba946478fb6dbb26d49307d756b0fd2ff88be339af23c39c0397d59143d2c", + "https://bcr.bazel.build/modules/bazel_features/0.1.0/MODULE.bazel": "47011d645b0f949f42ee67f2e8775188a9cf4a0a1528aa2fa4952f2fd00906fd", + "https://bcr.bazel.build/modules/bazel_features/1.0.0/MODULE.bazel": "d7f022dc887efb96e1ee51cec7b2e48d41e36ff59a6e4f216c40e4029e1585bf", + "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", + "https://bcr.bazel.build/modules/bazel_features/1.10.0/MODULE.bazel": "f75e8807570484a99be90abcd52b5e1f390362c258bcb73106f4544957a48101", + "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", + "https://bcr.bazel.build/modules/bazel_features/1.13.0/MODULE.bazel": "c14c33c7c3c730612bdbe14ebbb5e61936b6f11322ea95a6e91cd1ba962f94df", + "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", + "https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d", + "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a", + "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", + "https://bcr.bazel.build/modules/bazel_features/1.2.0/MODULE.bazel": "122b2b606622afbaa498913d54f52d9bcd2d19a5edd1bd6d6c5aa17441c4d5f9", + "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", + "https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc", + "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", + "https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b", + "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a", + "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", + "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", + "https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a", + "https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d", + "https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651", + "https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138", + "https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d", + "https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6", + "https://bcr.bazel.build/modules/bazel_skylib/1.8.1/source.json": "7ebaefba0b03efe59cac88ed5bbc67bcf59a3eff33af937345ede2a38b2d368a", + "https://bcr.bazel.build/modules/boringssl/0.0.0-20240530-2db0eb3/MODULE.bazel": "d0405b762c5e87cd445b7015f2b8da5400ef9a8dbca0bfefa6c1cea79d528a97", + "https://bcr.bazel.build/modules/boringssl/0.0.0-20240530-2db0eb3/source.json": "0d413869349e82e5d679802abe9ce23e0326bbf56daa97ae9e7dbdcec72982fc", + "https://bcr.bazel.build/modules/buildifier_prebuilt/6.1.2/MODULE.bazel": "2ef4962c8b0b6d8d21928a89190755619254459bc67f870dc0ccb9ba9952d444", + "https://bcr.bazel.build/modules/buildifier_prebuilt/7.3.1/MODULE.bazel": "537faf0ad9f5892910074b8e43b4c91c96f1d5d86b6ed04bdbe40cf68aa48b68", + "https://bcr.bazel.build/modules/buildifier_prebuilt/8.2.0.2/MODULE.bazel": "a9b689711d5b69f9db741649b218c119b9fdf82924ba390415037e09798edd03", + "https://bcr.bazel.build/modules/buildifier_prebuilt/8.2.0.2/source.json": "51eb0a4b38aaaeab7fa64361576d616c4d8bfd0f17a0a10184aeab7084d79f8e", + "https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84", + "https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8", + "https://bcr.bazel.build/modules/bzip2/1.0.8/MODULE.bazel": "83ee443b286b0b91566e5ee77e74ba6445895f3135467893871560f9e4ebc159", + "https://bcr.bazel.build/modules/bzip2/1.0.8/source.json": "b64f3a2f973749cf5f6ee32b3d804af56a35a746228a7845ed5daa31c8cc8af1", + "https://bcr.bazel.build/modules/download_utils/1.0.1/MODULE.bazel": "f1d0afade59e37de978506d6bbf08d7fe5f94964e86944aaf58efcead827b41b", + "https://bcr.bazel.build/modules/download_utils/1.0.1/source.json": "05ddc5a3b1f7d8f3e5e0fd1617479e1cf72d63d59ab2b1f0463557a14fc6be0a", + "https://bcr.bazel.build/modules/gazelle/0.27.0/MODULE.bazel": "3446abd608295de6d90b4a8a118ed64a9ce11dcb3dda2dc3290a22056bd20996", + "https://bcr.bazel.build/modules/gazelle/0.30.0/MODULE.bazel": "f888a1effe338491f35f0e0e85003b47bb9d8295ccba73c37e07702d8d31c65b", + "https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb", + "https://bcr.bazel.build/modules/google_benchmark/1.9.1/MODULE.bazel": "1abfd0395aea2db11943d817afb6c03208bee28b2e47eccc3042f67a22d3ce1b", + "https://bcr.bazel.build/modules/google_benchmark/1.9.1/source.json": "5d68196f85589340f5ca834d0bea3a49129d8cebf486d51b3d29419b0437bbbc", + "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4", + "https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6", + "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f", + "https://bcr.bazel.build/modules/googletest/1.15.0/MODULE.bazel": "c4515ecca65378b9035bb6ccee496c1a362b31311c2380ca7740a73bfdaccb51", + "https://bcr.bazel.build/modules/googletest/1.15.0/source.json": "c235880d343a5758da581c839653abeebb5f5cd9d987ff879ca68bf08a59f879", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075", + "https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d", + "https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902", + "https://bcr.bazel.build/modules/libpfm/4.11.0/source.json": "caaffb3ac2b59b8aac456917a4ecf3167d40478ee79f15ab7a877ec9273937c9", + "https://bcr.bazel.build/modules/nlohmann_json/3.11.3/MODULE.bazel": "87023db2f55fc3a9949c7b08dc711fae4d4be339a80a99d04453c4bb3998eefc", + "https://bcr.bazel.build/modules/nlohmann_json/3.11.3/source.json": "296c63a90c6813e53b3812d24245711981fc7e563d98fe15625f55181494488a", + "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5", + "https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f", + "https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29", + "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee", + "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37", + "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615", + "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814", + "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d", + "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc", + "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7", + "https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c", + "https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d", + "https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df", + "https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92", + "https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e", + "https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981", + "https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0", + "https://bcr.bazel.build/modules/protobuf/3.19.2/MODULE.bazel": "532ffe5f2186b69fdde039efe6df13ba726ff338c6bc82275ad433013fa10573", + "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858", + "https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e", + "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34", + "https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/source.json": "6900fdc8a9e95866b8c0d4ad4aba4d4236317b5c1cd04c502df3f0d33afed680", + "https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206", + "https://bcr.bazel.build/modules/re2/2024-07-02/MODULE.bazel": "0eadc4395959969297cbcf31a249ff457f2f1d456228c67719480205aa306daa", + "https://bcr.bazel.build/modules/re2/2024-07-02/source.json": "547d0111a9d4f362db32196fef805abbf3676e8d6afbe44d395d87816c1130ca", + "https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8", + "https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e", + "https://bcr.bazel.build/modules/rules_buf/0.1.1/MODULE.bazel": "6189aec18a4f7caff599ad41b851ab7645d4f1e114aa6431acf9b0666eb92162", + "https://bcr.bazel.build/modules/rules_buf/0.1.1/source.json": "021363d254f7438f3f10725355969c974bb2c67e0c28667782ade31a9cdb747f", + "https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647", + "https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002", + "https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191", + "https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac", + "https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc", + "https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87", + "https://bcr.bazel.build/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a", + "https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c", + "https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f", + "https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e", + "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513", + "https://bcr.bazel.build/modules/rules_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c", + "https://bcr.bazel.build/modules/rules_diff/1.0.0/MODULE.bazel": "1739509d8db9a6cd7d3584822340d3dfe1f9f27e62462fbca60aa061d88741b2", + "https://bcr.bazel.build/modules/rules_diff/1.0.0/source.json": "fc3824aed007b4db160ffb994036c6e558550857b6634a8e9ccee3e74c659312", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.10.1/MODULE.bazel": "b9527010e5fef060af92b6724edb3691970a5b1f76f74b21d39f7d433641be60", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.10.1/source.json": "9300e71df0cdde0952f10afff1401fa664e9fc5d9ae6204660ba1b158d90d6a6", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8", + "https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e", + "https://bcr.bazel.build/modules/rules_go/0.33.0/MODULE.bazel": "a2b11b64cd24bf94f57454f53288a5dacfe6cb86453eee7761b7637728c1910c", + "https://bcr.bazel.build/modules/rules_go/0.38.1/MODULE.bazel": "fb8e73dd3b6fc4ff9d260ceacd830114891d49904f5bda1c16bc147bcc254f71", + "https://bcr.bazel.build/modules/rules_go/0.39.1/MODULE.bazel": "d34fb2a249403a5f4339c754f1e63dc9e5ad70b47c5e97faee1441fc6636cd61", + "https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74", + "https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86", + "https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39", + "https://bcr.bazel.build/modules/rules_java/6.3.0/MODULE.bazel": "a97c7678c19f236a956ad260d59c86e10a463badb7eb2eda787490f4c969b963", + "https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6", + "https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31", + "https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a", + "https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6", + "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", + "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", + "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", + "https://bcr.bazel.build/modules/rules_java/8.11.0/MODULE.bazel": "c3d280bc5ff1038dcb3bacb95d3f6b83da8dd27bba57820ec89ea4085da767ad", + "https://bcr.bazel.build/modules/rules_java/8.12.0/MODULE.bazel": "8e6590b961f2defdfc2811c089c75716cb2f06c8a4edeb9a8d85eaa64ee2a761", + "https://bcr.bazel.build/modules/rules_java/8.13.0/MODULE.bazel": "0444ebf737d144cf2bb2ccb368e7f1cce735264285f2a3711785827c1686625e", + "https://bcr.bazel.build/modules/rules_java/8.13.0/source.json": "4605c0f676b87dd9d1fabd4d743b71f04d97503bd1a79aad53f87399fb5396de", + "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", + "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", + "https://bcr.bazel.build/modules/rules_java/8.6.3/MODULE.bazel": "e90505b7a931d194245ffcfb6ff4ca8ef9d46b4e830d12e64817752e0198e2ed", + "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7", + "https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909", + "https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036", + "https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d", + "https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0", + "https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3", + "https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5", + "https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0", + "https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d", + "https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c", + "https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb", + "https://bcr.bazel.build/modules/rules_multirun/0.9.0/MODULE.bazel": "32d628ef586b5b23f67e55886b7bc38913ea4160420d66ae90521dda2ff37df0", + "https://bcr.bazel.build/modules/rules_multirun/0.9.0/source.json": "e882ba77962fa6c5fe68619e5c7d0374ec9a219fb8d03c42eadaf6d0243771bd", + "https://bcr.bazel.build/modules/rules_multitool/0.4.0/MODULE.bazel": "15517987d5c00c9e7faab41fbe22ee67a350b6eabcc1e08baded5c6d9025897f", + "https://bcr.bazel.build/modules/rules_multitool/1.2.0/MODULE.bazel": "8d818d6104f4030930291bbbbc5684702c237dbcdee7229097543e6a6035adaa", + "https://bcr.bazel.build/modules/rules_multitool/1.2.0/source.json": "f612a878968675509f66254fed1f48f1047072d7415755d0dff4f1c28fe8def9", + "https://bcr.bazel.build/modules/rules_nodejs/5.8.2/MODULE.bazel": "6bc03c8f37f69401b888023bf511cb6ee4781433b0cb56236b2e55a21e3a026a", + "https://bcr.bazel.build/modules/rules_nodejs/5.8.2/source.json": "6e82cf5753d835ea18308200bc79b9c2e782efe2e2a4edc004a9162ca93382ca", + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", + "https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff", + "https://bcr.bazel.build/modules/rules_pkg/1.1.0/MODULE.bazel": "9db8031e71b6ef32d1846106e10dd0ee2deac042bd9a2de22b4761b0c3036453", + "https://bcr.bazel.build/modules/rules_pkg/1.1.0/source.json": "fef768df13a92ce6067e1cd0cdc47560dace01354f1d921cfb1d632511f7d608", + "https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06", + "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7", + "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483", + "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc2/MODULE.bazel": "e17f94f8a347e2c808517b65d74988839d2d62daceb50073e44060193b785eb1", + "https://bcr.bazel.build/modules/rules_proto/6.0.0/MODULE.bazel": "b531d7f09f58dce456cd61b4579ce8c86b38544da75184eadaf0a7cb7966453f", + "https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2", + "https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1", + "https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f", + "https://bcr.bazel.build/modules/rules_python/0.20.0/MODULE.bazel": "bfe14d17f20e3fe900b9588f526f52c967a6f281e47a1d6b988679bd15082286", + "https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300", + "https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382", + "https://bcr.bazel.build/modules/rules_python/0.27.1/MODULE.bazel": "65dc875cc1a06c30d5bbdba7ab021fd9e551a6579e408a3943a61303e2228a53", + "https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed", + "https://bcr.bazel.build/modules/rules_python/0.29.0/MODULE.bazel": "2ac8cd70524b4b9ec49a0b8284c79e4cd86199296f82f6e0d5da3f783d660c82", + "https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58", + "https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937", + "https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c", + "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7", + "https://bcr.bazel.build/modules/rules_python/1.0.0/MODULE.bazel": "898a3d999c22caa585eb062b600f88654bf92efb204fa346fb55f6f8edffca43", + "https://bcr.bazel.build/modules/rules_python/1.4.1/MODULE.bazel": "8991ad45bdc25018301d6b7e1d3626afc3c8af8aaf4bc04f23d0b99c938b73a6", + "https://bcr.bazel.build/modules/rules_python/1.4.1/source.json": "8ec8c90c70ccacc4de8ca1b97f599e756fb59173e898ee08b733006650057c07", + "https://bcr.bazel.build/modules/rules_rust/0.56.0/MODULE.bazel": "3295b00757db397122092322fe1e920be7f5c9fbfb8619138977e820f2cbbbae", + "https://bcr.bazel.build/modules/rules_rust/0.56.0/source.json": "7dc294c3decd40af8f7b83897a5936e764d3ae8584b4056862978fb3870ab8d7", + "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c", + "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b", + "https://bcr.bazel.build/modules/rules_shell/0.4.0/MODULE.bazel": "0f8f11bb3cd11755f0b48c1de0bbcf62b4b34421023aa41a2fc74ef68d9584f0", + "https://bcr.bazel.build/modules/rules_shell/0.4.0/source.json": "1d7fa7f941cd41dc2704ba5b4edc2e2230eea1cc600d80bd2b65838204c50b95", + "https://bcr.bazel.build/modules/stardoc/0.5.0/MODULE.bazel": "f9f1f46ba8d9c3362648eea571c6f9100680efc44913618811b58cc9c02cd678", + "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8", + "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c", + "https://bcr.bazel.build/modules/stardoc/0.5.4/MODULE.bazel": "6569966df04610b8520957cb8e97cf2e9faac2c0309657c537ab51c16c18a2a4", + "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef", + "https://bcr.bazel.build/modules/stardoc/0.6.2/MODULE.bazel": "7060193196395f5dd668eda046ccbeacebfd98efc77fed418dbe2b82ffaa39fd", + "https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c", + "https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7", + "https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5", + "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", + "https://bcr.bazel.build/modules/toolchain_utils/1.0.2/MODULE.bazel": "9b8be503a4fcfd3b8b952525bff0869177a5234d5c35dc3e566b9f5ca2f755a1", + "https://bcr.bazel.build/modules/toolchain_utils/1.0.2/source.json": "88769ec576dddacafd8cca4631812cf8eead89f10a29d9405d9f7a553de6bf87", + "https://bcr.bazel.build/modules/toolchains_llvm/1.3.0/MODULE.bazel": "6e02731e51f7eb2ec4b01c5e79e722bf738a631f6e03d9b4917cbf2cb027bee1", + "https://bcr.bazel.build/modules/toolchains_llvm/1.3.0/source.json": "4ce0373a89c6df34dd37cd67285bb871d8e225d30dcb67dd093e077a04bbbb71", + "https://bcr.bazel.build/modules/toolchains_protoc/0.2.1/MODULE.bazel": "2f08433ff5e659069b3a1abfee2377d68f510f2de1da50678ed992c455b4ff91", + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", + "https://bcr.bazel.build/modules/xz/5.4.5.bcr.5/MODULE.bazel": "b93d7035ac14c900dfdf7624e42cfcbfc34415aa8d3c83a6f225867c48d28dad", + "https://bcr.bazel.build/modules/xz/5.4.5.bcr.5/source.json": "30c4e5c856087a60d92e2522eafd316c0661671f4478ca94c6b7bd877010210a", + "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", + "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.6/MODULE.bazel": "e937cf0a3772f93ad91f3c7af4f330b76a878bbfee06527ca1a9673b790eb896", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.6/source.json": "5f397158198f338129c865a4c3ae21bc5626a9664b3c3b40fa3b3c2ec1ff83bf", + "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198", + "https://bcr.bazel.build/modules/zstd/1.5.6/MODULE.bazel": "471ebe7d3cdd8c6469390fcf623eb4779ff55fbee0a87f1dc57a1def468b96d4", + "https://bcr.bazel.build/modules/zstd/1.5.6/source.json": "02010c3333fc89b44fe861db049968decb6e688411f7f9d4f6791d74f9adfb51", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20210324.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20211102.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230125.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230802.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20230802.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20240116.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/abseil-cpp/20240116.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/ape/1.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.15.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/apple_support/1.17.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/1.31.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/1.38.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/1.42.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.16.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.7.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_bazel_lib/2.9.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_js/1.33.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_js/1.40.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_lint/0.12.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_lint/1.0.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_lint/1.3.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_lint/1.4.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_lint/1.4.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_py/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/aspect_rules_py/1.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/0.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.10.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.13.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.15.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.17.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.18.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.19.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.21.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.30.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_features/1.9.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.0.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.2.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.4.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.5.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.7.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.7.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.8.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/boringssl/0.0.0-20240530-2db0eb3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildifier_prebuilt/6.1.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildifier_prebuilt/7.3.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildifier_prebuilt/8.2.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/buildozer/7.1.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bzip2/1.0.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/download_utils/1.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.27.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/gazelle/0.30.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/google_benchmark/1.8.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/google_benchmark/1.9.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.14.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/googletest/1.15.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/jsoncpp/1.9.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/libpfm/4.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/nlohmann_json/3.11.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.10/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.11/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/platforms/0.0.9/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/21.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/27.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/27.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.0-rc2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.0-rc3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/29.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/3.19.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/3.19.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/protobuf/3.19.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/pybind11_bazel/2.11.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/pybind11_bazel/2.12.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/re2/2023-09-01/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/re2/2024-07-02/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_android/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_buf/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.10/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.13/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.14/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.15/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.16/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.17/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.8/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.0.9/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_cc/0.1.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_diff/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_foreign_cc/0.10.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_fuzzing/0.5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.33.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.38.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_go/0.39.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/4.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/5.3.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/6.5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.10.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.12.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.3.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/7.6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.11.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.12.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.13.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.3.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_java/8.6.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/4.4.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/5.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/5.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_jvm_external/6.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/1.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_kotlin/1.9.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/0.0.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_license/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multirun/0.9.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multitool/0.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_multitool/1.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_nodejs/5.8.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/0.7.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/1.0.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_pkg/1.1.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/4.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.0-rc2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/6.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_proto/7.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.10.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.20.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.23.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.25.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.27.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.28.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.29.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.31.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.33.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/0.40.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_python/1.4.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_rust/0.56.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.2.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/rules_shell/0.4.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_cr_checker/0.2.2/MODULE.bazel": "dc36d9c35543db918c3fb5b93a8e684431f56c7c784cf2a1b90f35802a373c98", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_cr_checker/0.3.1/MODULE.bazel": "f49e037d7fbc0b2a8b2734fc6b47334e8cc8589ca7a5aa0f3ccca85cc5f79fac", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_cr_checker/0.3.1/source.json": "ad038d99c0e2a59cca3a7fa1aac6d87cd0d752314b65b52a91451ab0bd0f7171", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_dash_license_checker/0.1.1/MODULE.bazel": "76681dbd2d45b5c540869a2337174086c56c54953aab1d02cd878b59d31d13a5", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_dash_license_checker/0.1.2/MODULE.bazel": "89ba8c942dfd8d05cbf59b48868c919207aa189c670f97194078d5b8e09c6a8a", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_dash_license_checker/0.1.2/source.json": "f327a8103a33b0668c5cb35a4e68593c5ead8c946abab347b4a09481aaf28465", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/0.2.4/MODULE.bazel": "ea4801e96c87e2b8650a0fa9e5fed9b8bdbef05c1bc3e30003ba527d5af60a43", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/0.2.6/MODULE.bazel": "1af2963e91c6472555e222f0aba3dc2f5492d04598298209a361978ee3e321e3", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/0.3.3/MODULE.bazel": "95d2b7d44d461c1cf9bd016605f740716fd4ea1303f5f2ed93de3566b90feb1b", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/1.0.0-RC1/MODULE.bazel": "e118b5cbdc453cde83b5ce481107d8e4a713d3458550b9d10445046d4bba2ff3", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/1.0.0/MODULE.bazel": "a3ad204b7412c02a899034d78de62b5549bafba5530a256d1007cb3f4ed20a11", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/1.0.1/MODULE.bazel": "f8921ba95caf0c221199be593886929fb11880d15328128b04eb42483233c2fe", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_docs_as_code/1.0.1/source.json": "64fe1f7a2928dae40ad4a1a533fe4f3d5aa93fc95c3f4bc0aa67981ab8e22060", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_format_checker/0.1.1/MODULE.bazel": "1acc254faa90e9f97b79ac69af25b6c21c561f8d6079914f6352b9b20d26bd37", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_format_checker/0.1.1/source.json": "e3d10be878d1a007967b75a5c6d9c3ec3d47c320685904b7f31c689b3cbcdb3a", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_platform/0.1.0/MODULE.bazel": "cc9eae86e76f2a930510ed6e50ec991bb5661687e24881685b39c322087adf6f", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_platform/0.1.1/MODULE.bazel": "eb086ba99f9319371fbbd0a9252dfd27b0817039b88bd4d691602974b1ada005", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_process/1.0.4/MODULE.bazel": "f74302cb90a7c4878db302276afae82966878099861dcfca3ef43256131dab52", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_process/1.0.5/MODULE.bazel": "ed17c232ebd65e9d50fd5c1832f90f95ffe95b2a1113d63a176295a2af64d111", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_process/1.1.0/MODULE.bazel": "97dd927309f87ecb73629725683028a5dbb37a49b1159c771292e6993569055b", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_process/1.1.0/source.json": "c73a2c19f5e02119cd61fb1b90b74ada4a3df11705bb476a4dfab1d39395cd94", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_python_basics/0.3.0/MODULE.bazel": "785ddd5295213e36c31ab86bdc34f29c0f7d1b72e9abd931bb08f42c0e48e2e9", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_python_basics/0.3.1/MODULE.bazel": "99c491109937542e61df090222666a8613ef946fa7bb2b2d5ba648b2baba03ad", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_python_basics/0.3.2/MODULE.bazel": "f25490f64035a0e3a0d53ad9cb6164e8325ce6cf2a7ee68c6ae153840cb2497e", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_python_basics/0.3.4/MODULE.bazel": "53bd16dfbb1fb8ecf6822fb26f9f4e8333bac7b14d12bb02bf84078063820a31", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_python_basics/0.3.4/source.json": "28ed0207a16f8498a84fae4983fa73060805003a4fbd68c6d749e2f1cc14601f", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_starpls_lsp/0.1.0/MODULE.bazel": "b2f8c4c8d8e851706255ff9002b448bff6e040b8f0c6adedbde2a09375aa16cc", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_starpls_lsp/0.1.0/source.json": "3c7c49977b01a990f4f0a6d65c00e75a88433af7ad21a564db338cd079a92913", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_gcc/0.4/MODULE.bazel": "370458c92c6cd9f91ea316d7658a5b82ee7422e22186328c86c570947508dc64", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_toolchains_gcc/0.4/source.json": "2a15ebc5b338c0223d172e2b1eca9d459c20f767ab406661ff82f76da8434bc3", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.3/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.4/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.5.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.6.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/stardoc/0.7.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/toolchain_utils/1.0.2/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/toolchains_llvm/1.3.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/toolchains_protoc/0.2.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/xz/5.4.5.bcr.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.2.11/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.2.12/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1.bcr.6/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zlib/1.3.1/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/zstd/1.5.6/MODULE.bazel": "not found" + }, + "selectedYankedVersions": {}, + "moduleExtensions": { + "@@apple_support+//crosstool:setup.bzl%apple_cc_configure_extension": { + "general": { + "bzlTransitiveDigest": "xcBTf2+GaloFpg7YEh/Bv+1yAczRkiCt3DGws4K7kSk=", + "usagesDigest": "3L+PK6aRnliv0iIS8m3kdo+LjmvjJWoFCm3qZcPSg+8=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_apple_cc_toolchains": { + "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf_toolchains", + "attributes": {} + }, + "local_config_apple_cc": { + "repoRuleId": "@@apple_support+//crosstool:setup.bzl%_apple_cc_autoconf", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "apple_support+", + "bazel_tools", + "bazel_tools" + ], + [ + "bazel_tools", + "rules_cc", + "rules_cc+" + ] + ] + } + }, + "@@aspect_rules_py+//py:extensions.bzl%py_tools": { + "general": { + "bzlTransitiveDigest": "uOkPJqwGpftAAWEOPNUUC7IiIJs4lOs9sxelXwtTGnw=", + "usagesDigest": "NC1b49l5tenTBVWEUGzzC0j5Kg1GH+l5lBw5JRCldIU=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "bsd_tar_darwin_amd64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "darwin_amd64" + } + }, + "bsd_tar_darwin_arm64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "darwin_arm64" + } + }, + "bsd_tar_linux_amd64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "linux_amd64" + } + }, + "bsd_tar_linux_arm64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "linux_arm64" + } + }, + "bsd_tar_windows_amd64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "windows_amd64" + } + }, + "bsd_tar_windows_arm64": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%bsdtar_binary_repo", + "attributes": { + "platform": "windows_arm64" + } + }, + "bsd_tar_toolchains": { + "repoRuleId": "@@aspect_bazel_lib+//lib/private:tar_toolchain.bzl%tar_toolchains_repo", + "attributes": { + "user_repository_name": "bsd_tar" + } + }, + "rules_py_tools.darwin_amd64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "darwin_amd64" + } + }, + "rules_py_tools.darwin_arm64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "darwin_arm64" + } + }, + "rules_py_tools.linux_amd64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "linux_amd64" + } + }, + "rules_py_tools.linux_arm64": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:tools.bzl%prebuilt_tool_repo", + "attributes": { + "platform": "linux_arm64" + } + }, + "rules_py_tools": { + "repoRuleId": "@@aspect_rules_py+//py/private/toolchain:repo.bzl%toolchains_repo", + "attributes": { + "user_repository_name": "rules_py_tools" + } + }, + "rules_py_pex_2_3_1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", + "attributes": { + "urls": [ + "https://files.pythonhosted.org/packages/e7/d0/fbda2a4d41d62d86ce53f5ae4fbaaee8c34070f75bb7ca009090510ae874/pex-2.3.1-py2.py3-none-any.whl" + ], + "sha256": "64692a5bf6f298403aab930d22f0d836ae4736c5bc820e262e9092fe8c56f830", + "downloaded_file_path": "pex-2.3.1-py2.py3-none-any.whl" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "aspect_bazel_lib+", + "bazel_tools", + "bazel_tools" + ], + [ + "aspect_rules_py+", + "aspect_bazel_lib", + "aspect_bazel_lib+" + ], + [ + "aspect_rules_py+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_buf+//buf:extensions.bzl%ext": { + "general": { + "bzlTransitiveDigest": "Fk4QK5kj/HKxKVGAfyZEJNUAOC+Ic+kKMcXb20jecDE=", + "usagesDigest": "RTc2BMQ2b0wGU8CRvN3EoPz34m3LMe+K/oSkFkN83+M=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "rules_buf_toolchains": { + "repoRuleId": "@@rules_buf+//buf/internal:toolchain.bzl%buf_download_releases", + "attributes": { + "version": "v1.27.0" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_buf+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_foreign_cc+//foreign_cc:extensions.bzl%tools": { + "general": { + "bzlTransitiveDigest": "ginC3lIGOKKivBi0nyv2igKvSiz42Thm8yaX2RwVaHg=", + "usagesDigest": "9LXdVp01HkdYQT8gYPjYLO6VLVJHo9uFfxWaU1ymiRE=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "rules_foreign_cc_framework_toolchain_linux": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:linux_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:linux" + ] + } + }, + "rules_foreign_cc_framework_toolchain_freebsd": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:freebsd_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:freebsd" + ] + } + }, + "rules_foreign_cc_framework_toolchain_windows": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:windows_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:windows" + ] + } + }, + "rules_foreign_cc_framework_toolchain_macos": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:macos_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:macos" + ] + } + }, + "rules_foreign_cc_framework_toolchains": { + "repoRuleId": "@@rules_foreign_cc+//foreign_cc/private/framework:toolchain.bzl%framework_toolchain_repository_hub", + "attributes": {} + }, + "cmake_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "f316b40053466f9a416adf981efda41b160ca859e97f6a484b447ea299ff26aa", + "strip_prefix": "cmake-3.23.2", + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2.tar.gz" + ] + } + }, + "gnumake_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "581f4d4e872da74b3941c874215898a7d35802f03732bdccee1d4a7979105d18", + "strip_prefix": "make-4.4", + "urls": [ + "https://mirror.bazel.build/ftpmirror.gnu.org/gnu/make/make-4.4.tar.gz", + "http://ftpmirror.gnu.org/gnu/make/make-4.4.tar.gz" + ] + } + }, + "ninja_build_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "31747ae633213f1eda3842686f83c2aa1412e0f5691d1c14dbbcc67fe7400cea", + "strip_prefix": "ninja-1.11.1", + "urls": [ + "https://github.com/ninja-build/ninja/archive/v1.11.1.tar.gz" + ] + } + }, + "meson_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "exports_files([\"meson.py\"])\n\nfilegroup(\n name = \"runtime\",\n srcs = glob([\"mesonbuild/**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "strip_prefix": "meson-1.1.1", + "url": "https://github.com/mesonbuild/meson/releases/download/1.1.1/meson-1.1.1.tar.gz" + } + }, + "glib_dev": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nload(\"@rules_cc//cc:defs.bzl\", \"cc_library\")\n\ncc_import(\n name = \"glib_dev\",\n hdrs = glob([\"include/**\"]),\n shared_library = \"@glib_runtime//:bin/libglib-2.0-0.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "bdf18506df304d38be98a4b3f18055b8b8cca81beabecad0eece6ce95319c369", + "urls": [ + "https://download.gnome.org/binaries/win64/glib/2.26/glib-dev_2.26.1-1_win64.zip" + ] + } + }, + "glib_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\ncc_import(\n name = \"msvc_hdr\",\n hdrs = [\"msvc_recommended_pragmas.h\"],\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "bc96f63112823b7d6c9f06572d2ad626ddac7eb452c04d762592197f6e07898e", + "strip_prefix": "glib-2.26.1", + "urls": [ + "https://download.gnome.org/sources/glib/2.26/glib-2.26.1.tar.gz" + ] + } + }, + "glib_runtime": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\nexports_files(\n [\n \"bin/libgio-2.0-0.dll\",\n \"bin/libglib-2.0-0.dll\",\n \"bin/libgmodule-2.0-0.dll\",\n \"bin/libgobject-2.0-0.dll\",\n \"bin/libgthread-2.0-0.dll\",\n ],\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "88d857087e86f16a9be651ee7021880b3f7ba050d34a1ed9f06113b8799cb973", + "urls": [ + "https://download.gnome.org/binaries/win64/glib/2.26/glib_2.26.1-1_win64.zip" + ] + } + }, + "gettext_runtime": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "\ncc_import(\n name = \"gettext_runtime\",\n shared_library = \"bin/libintl-8.dll\",\n visibility = [\"//visibility:public\"],\n)\n ", + "sha256": "1f4269c0e021076d60a54e98da6f978a3195013f6de21674ba0edbc339c5b079", + "urls": [ + "https://download.gnome.org/binaries/win64/dependencies/gettext-runtime_0.18.1.1-2_win64.zip" + ] + } + }, + "pkgconfig_src": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591", + "strip_prefix": "pkg-config-0.29.2", + "patches": [ + "@@rules_foreign_cc+//toolchains:pkgconfig-detectenv.patch", + "@@rules_foreign_cc+//toolchains:pkgconfig-makefile-vc.patch" + ], + "urls": [ + "https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz" + ] + } + }, + "bazel_skylib": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz" + ], + "sha256": "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728" + } + }, + "rules_python": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841", + "strip_prefix": "rules_python-0.23.1", + "url": "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.23.1.tar.gz" + } + }, + "cmake-3.23.2-linux-aarch64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-aarch64.tar.gz" + ], + "sha256": "f2654bf780b53f170bbbec44d8ac67d401d24788e590faa53036a89476efa91e", + "strip_prefix": "cmake-3.23.2-linux-aarch64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-linux-x86_64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-x86_64.tar.gz" + ], + "sha256": "aaced6f745b86ce853661a595bdac6c5314a60f8181b6912a0a4920acfa32708", + "strip_prefix": "cmake-3.23.2-linux-x86_64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-macos-universal": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-macos-universal.tar.gz" + ], + "sha256": "853a0f9af148c5ef47282ffffee06c4c9f257be2635936755f39ca13c3286c88", + "strip_prefix": "cmake-3.23.2-macos-universal/CMake.app/Contents", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-windows-i386": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-i386.zip" + ], + "sha256": "6a4fcd6a2315b93cb23c93507efccacc30c449c2bf98f14d6032bb226c582e07", + "strip_prefix": "cmake-3.23.2-windows-i386", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-windows-x86_64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-x86_64.zip" + ], + "sha256": "2329387f3166b84c25091c86389fb891193967740c9bcf01e7f6d3306f7ffda0", + "strip_prefix": "cmake-3.23.2-windows-x86_64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake_3.23.2_toolchains": { + "repoRuleId": "@@rules_foreign_cc+//toolchains:prebuilt_toolchains_repository.bzl%prebuilt_toolchains_repository", + "attributes": { + "repos": { + "cmake-3.23.2-linux-aarch64": [ + "@platforms//cpu:aarch64", + "@platforms//os:linux" + ], + "cmake-3.23.2-linux-x86_64": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "cmake-3.23.2-macos-universal": [ + "@platforms//os:macos" + ], + "cmake-3.23.2-windows-i386": [ + "@platforms//cpu:x86_32", + "@platforms//os:windows" + ], + "cmake-3.23.2-windows-x86_64": [ + "@platforms//cpu:x86_64", + "@platforms//os:windows" + ] + }, + "tool": "cmake" + } + }, + "ninja_1.11.1_linux": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-linux.zip" + ], + "sha256": "b901ba96e486dce377f9a070ed4ef3f79deb45f4ffe2938f8e7ddc69cfb3df77", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.11.1_mac": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-mac.zip" + ], + "sha256": "482ecb23c59ae3d4f158029112de172dd96bb0e97549c4b1ca32d8fad11f873e", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.11.1_win": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-win.zip" + ], + "sha256": "524b344a1a9a55005eaf868d991e090ab8ce07fa109f1820d40e74642e289abc", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja.exe\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_1.11.1_toolchains": { + "repoRuleId": "@@rules_foreign_cc+//toolchains:prebuilt_toolchains_repository.bzl%prebuilt_toolchains_repository", + "attributes": { + "repos": { + "ninja_1.11.1_linux": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "ninja_1.11.1_mac": [ + "@platforms//cpu:x86_64", + "@platforms//os:macos" + ], + "ninja_1.11.1_win": [ + "@platforms//cpu:x86_64", + "@platforms//os:windows" + ] + }, + "tool": "ninja" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_foreign_cc+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_foreign_cc+", + "rules_foreign_cc", + "rules_foreign_cc+" + ] + ] + } + }, + "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { + "general": { + "bzlTransitiveDigest": "hUTp2w+RUVdL7ma5esCXZJAFnX7vLbVfLd7FwnQI6bU=", + "usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "com_github_jetbrains_kotlin_git": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_compiler_git_repository", + "attributes": { + "urls": [ + "https://github.com/JetBrains/kotlin/releases/download/v1.9.23/kotlin-compiler-1.9.23.zip" + ], + "sha256": "93137d3aab9afa9b27cb06a824c2324195c6b6f6179d8a8653f440f5bd58be88" + } + }, + "com_github_jetbrains_kotlin": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:compiler.bzl%kotlin_capabilities_repository", + "attributes": { + "git_repository_name": "com_github_jetbrains_kotlin_git", + "compiler_version": "1.9.23" + } + }, + "com_github_google_ksp": { + "repoRuleId": "@@rules_kotlin+//src/main/starlark/core/repositories:ksp.bzl%ksp_compiler_plugin_repository", + "attributes": { + "urls": [ + "https://github.com/google/ksp/releases/download/1.9.23-1.0.20/artifacts.zip" + ], + "sha256": "ee0618755913ef7fd6511288a232e8fad24838b9af6ea73972a76e81053c8c2d", + "strip_version": "1.9.23-1.0.20" + } + }, + "com_github_pinterest_ktlint": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_file", + "attributes": { + "sha256": "01b2e0ef893383a50dbeb13970fe7fa3be36ca3e83259e01649945b09d736985", + "urls": [ + "https://github.com/pinterest/ktlint/releases/download/1.3.0/ktlint" + ], + "executable": true + } + }, + "rules_android": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", + "strip_prefix": "rules_android-0.1.1", + "urls": [ + "https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_kotlin+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_nodejs+//nodejs:extensions.bzl%node": { + "general": { + "bzlTransitiveDigest": "hb4P9W+UouOR3GTf2pDXbhhQFSTElj+EgJVbrB5U+cQ=", + "usagesDigest": "8OoyQ05AfTDe1T/jKkylUFidWxQge7e3HN2eOIIA6xM=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "nodejs_linux_amd64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories", + "attributes": { + "platform": "linux_amd64", + "node_version": "16.19.0" + } + }, + "nodejs_linux_arm64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories", + "attributes": { + "platform": "linux_arm64", + "node_version": "16.19.0" + } + }, + "nodejs_linux_s390x": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories", + "attributes": { + "platform": "linux_s390x", + "node_version": "16.19.0" + } + }, + "nodejs_linux_ppc64le": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories", + "attributes": { + "platform": "linux_ppc64le", + "node_version": "16.19.0" + } + }, + "nodejs_darwin_amd64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories", + "attributes": { + "platform": "darwin_amd64", + "node_version": "16.19.0" + } + }, + "nodejs_darwin_arm64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories", + "attributes": { + "platform": "darwin_arm64", + "node_version": "16.19.0" + } + }, + "nodejs_windows_amd64": { + "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%node_repositories", + "attributes": { + "platform": "windows_amd64", + "node_version": "16.19.0" + } + }, + "nodejs": { + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", + "attributes": { + "user_node_repository_name": "nodejs" + } + }, + "nodejs_host": { + "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias", + "attributes": { + "user_node_repository_name": "nodejs" + } + }, + "nodejs_toolchains": { + "repoRuleId": "@@rules_nodejs+//nodejs/private:toolchains_repo.bzl%toolchains_repo", + "attributes": { + "user_node_repository_name": "nodejs" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_nodejs+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_nodejs+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_python+//python/uv:uv.bzl%uv": { + "general": { + "bzlTransitiveDigest": "Xpqjnjzy6zZ90Es9Wa888ZLHhn7IsNGbph/e6qoxzw8=", + "usagesDigest": "4JapxcpS0mL3524k0TZJffAtVyuRjDHZvN9kBRxxF1U=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "uv": { + "repoRuleId": "@@rules_python+//python/uv/private:uv_toolchains_repo.bzl%uv_toolchains_repo", + "attributes": { + "toolchain_type": "'@@rules_python+//python/uv:uv_toolchain_type'", + "toolchain_names": [ + "none" + ], + "toolchain_implementations": { + "none": "'@@rules_python+//python:none'" + }, + "toolchain_compatible_with": { + "none": [ + "@platforms//:incompatible" + ] + }, + "toolchain_target_settings": {} + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_python+", + "platforms", + "platforms" + ] + ] + } + }, + "@@rules_rust+//rust/private:internal_extensions.bzl%i": { + "general": { + "bzlTransitiveDigest": "DPfxvX70w8ifC0qweS/4YigbXRsujE7gnUTNIlLkMaI=", + "usagesDigest": "8daAc/SRar7Mu8+uVH3y5t7CY0RsiqVBIznBuEjXJ4w=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "rules_rust_tinyjson": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a", + "url": "https://static.crates.io/crates/tinyjson/tinyjson-2.5.1.crate", + "strip_prefix": "tinyjson-2.5.1", + "type": "tar.gz", + "build_file": "@@rules_rust+//util/process_wrapper:BUILD.tinyjson.bazel" + } + }, + "rrra__aho-corasick-1.0.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/aho-corasick/1.0.2/download" + ], + "strip_prefix": "aho-corasick-1.0.2", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.aho-corasick-1.0.2.bazel" + } + }, + "rrra__anstream-0.3.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstream/0.3.2/download" + ], + "strip_prefix": "anstream-0.3.2", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstream-0.3.2.bazel" + } + }, + "rrra__anstyle-1.0.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstyle/1.0.1/download" + ], + "strip_prefix": "anstyle-1.0.1", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-1.0.1.bazel" + } + }, + "rrra__anstyle-parse-0.2.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstyle-parse/0.2.1/download" + ], + "strip_prefix": "anstyle-parse-0.2.1", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-parse-0.2.1.bazel" + } + }, + "rrra__anstyle-query-1.0.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstyle-query/1.0.0/download" + ], + "strip_prefix": "anstyle-query-1.0.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-query-1.0.0.bazel" + } + }, + "rrra__anstyle-wincon-1.0.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anstyle-wincon/1.0.1/download" + ], + "strip_prefix": "anstyle-wincon-1.0.1", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anstyle-wincon-1.0.1.bazel" + } + }, + "rrra__anyhow-1.0.71": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/anyhow/1.0.71/download" + ], + "strip_prefix": "anyhow-1.0.71", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.anyhow-1.0.71.bazel" + } + }, + "rrra__bitflags-1.3.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/bitflags/1.3.2/download" + ], + "strip_prefix": "bitflags-1.3.2", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.bitflags-1.3.2.bazel" + } + }, + "rrra__cc-1.0.79": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/cc/1.0.79/download" + ], + "strip_prefix": "cc-1.0.79", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.cc-1.0.79.bazel" + } + }, + "rrra__clap-4.3.11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clap/4.3.11/download" + ], + "strip_prefix": "clap-4.3.11", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.clap-4.3.11.bazel" + } + }, + "rrra__clap_builder-4.3.11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clap_builder/4.3.11/download" + ], + "strip_prefix": "clap_builder-4.3.11", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.clap_builder-4.3.11.bazel" + } + }, + "rrra__clap_derive-4.3.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clap_derive/4.3.2/download" + ], + "strip_prefix": "clap_derive-4.3.2", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.clap_derive-4.3.2.bazel" + } + }, + "rrra__clap_lex-0.5.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/clap_lex/0.5.0/download" + ], + "strip_prefix": "clap_lex-0.5.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.clap_lex-0.5.0.bazel" + } + }, + "rrra__colorchoice-1.0.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/colorchoice/1.0.0/download" + ], + "strip_prefix": "colorchoice-1.0.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.colorchoice-1.0.0.bazel" + } + }, + "rrra__either-1.8.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/either/1.8.1/download" + ], + "strip_prefix": "either-1.8.1", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.either-1.8.1.bazel" + } + }, + "rrra__env_logger-0.10.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/env_logger/0.10.0/download" + ], + "strip_prefix": "env_logger-0.10.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.env_logger-0.10.0.bazel" + } + }, + "rrra__errno-0.3.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/errno/0.3.1/download" + ], + "strip_prefix": "errno-0.3.1", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.errno-0.3.1.bazel" + } + }, + "rrra__errno-dragonfly-0.1.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/errno-dragonfly/0.1.2/download" + ], + "strip_prefix": "errno-dragonfly-0.1.2", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.errno-dragonfly-0.1.2.bazel" + } + }, + "rrra__heck-0.4.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/heck/0.4.1/download" + ], + "strip_prefix": "heck-0.4.1", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.heck-0.4.1.bazel" + } + }, + "rrra__hermit-abi-0.3.2": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/hermit-abi/0.3.2/download" + ], + "strip_prefix": "hermit-abi-0.3.2", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.hermit-abi-0.3.2.bazel" + } + }, + "rrra__humantime-2.1.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/humantime/2.1.0/download" + ], + "strip_prefix": "humantime-2.1.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.humantime-2.1.0.bazel" + } + }, + "rrra__io-lifetimes-1.0.11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/io-lifetimes/1.0.11/download" + ], + "strip_prefix": "io-lifetimes-1.0.11", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.io-lifetimes-1.0.11.bazel" + } + }, + "rrra__is-terminal-0.4.7": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/is-terminal/0.4.7/download" + ], + "strip_prefix": "is-terminal-0.4.7", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.is-terminal-0.4.7.bazel" + } + }, + "rrra__itertools-0.11.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/itertools/0.11.0/download" + ], + "strip_prefix": "itertools-0.11.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.itertools-0.11.0.bazel" + } + }, + "rrra__itoa-1.0.8": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/itoa/1.0.8/download" + ], + "strip_prefix": "itoa-1.0.8", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.itoa-1.0.8.bazel" + } + }, + "rrra__libc-0.2.147": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/libc/0.2.147/download" + ], + "strip_prefix": "libc-0.2.147", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.libc-0.2.147.bazel" + } + }, + "rrra__linux-raw-sys-0.3.8": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/linux-raw-sys/0.3.8/download" + ], + "strip_prefix": "linux-raw-sys-0.3.8", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.linux-raw-sys-0.3.8.bazel" + } + }, + "rrra__log-0.4.19": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/log/0.4.19/download" + ], + "strip_prefix": "log-0.4.19", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.log-0.4.19.bazel" + } + }, + "rrra__memchr-2.5.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/memchr/2.5.0/download" + ], + "strip_prefix": "memchr-2.5.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.memchr-2.5.0.bazel" + } + }, + "rrra__once_cell-1.18.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/once_cell/1.18.0/download" + ], + "strip_prefix": "once_cell-1.18.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.once_cell-1.18.0.bazel" + } + }, + "rrra__proc-macro2-1.0.64": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/proc-macro2/1.0.64/download" + ], + "strip_prefix": "proc-macro2-1.0.64", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.proc-macro2-1.0.64.bazel" + } + }, + "rrra__quote-1.0.29": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/quote/1.0.29/download" + ], + "strip_prefix": "quote-1.0.29", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.quote-1.0.29.bazel" + } + }, + "rrra__regex-1.9.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/regex/1.9.1/download" + ], + "strip_prefix": "regex-1.9.1", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.regex-1.9.1.bazel" + } + }, + "rrra__regex-automata-0.3.3": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/regex-automata/0.3.3/download" + ], + "strip_prefix": "regex-automata-0.3.3", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.regex-automata-0.3.3.bazel" + } + }, + "rrra__regex-syntax-0.7.4": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/regex-syntax/0.7.4/download" + ], + "strip_prefix": "regex-syntax-0.7.4", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.regex-syntax-0.7.4.bazel" + } + }, + "rrra__rustix-0.37.23": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/rustix/0.37.23/download" + ], + "strip_prefix": "rustix-0.37.23", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.rustix-0.37.23.bazel" + } + }, + "rrra__ryu-1.0.14": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/ryu/1.0.14/download" + ], + "strip_prefix": "ryu-1.0.14", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.ryu-1.0.14.bazel" + } + }, + "rrra__serde-1.0.171": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/serde/1.0.171/download" + ], + "strip_prefix": "serde-1.0.171", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.serde-1.0.171.bazel" + } + }, + "rrra__serde_derive-1.0.171": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/serde_derive/1.0.171/download" + ], + "strip_prefix": "serde_derive-1.0.171", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.serde_derive-1.0.171.bazel" + } + }, + "rrra__serde_json-1.0.102": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/serde_json/1.0.102/download" + ], + "strip_prefix": "serde_json-1.0.102", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.serde_json-1.0.102.bazel" + } + }, + "rrra__strsim-0.10.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/strsim/0.10.0/download" + ], + "strip_prefix": "strsim-0.10.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.strsim-0.10.0.bazel" + } + }, + "rrra__syn-2.0.25": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/syn/2.0.25/download" + ], + "strip_prefix": "syn-2.0.25", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.syn-2.0.25.bazel" + } + }, + "rrra__termcolor-1.2.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/termcolor/1.2.0/download" + ], + "strip_prefix": "termcolor-1.2.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.termcolor-1.2.0.bazel" + } + }, + "rrra__unicode-ident-1.0.10": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/unicode-ident/1.0.10/download" + ], + "strip_prefix": "unicode-ident-1.0.10", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.unicode-ident-1.0.10.bazel" + } + }, + "rrra__utf8parse-0.2.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/utf8parse/0.2.1/download" + ], + "strip_prefix": "utf8parse-0.2.1", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.utf8parse-0.2.1.bazel" + } + }, + "rrra__winapi-0.3.9": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/winapi/0.3.9/download" + ], + "strip_prefix": "winapi-0.3.9", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-0.3.9.bazel" + } + }, + "rrra__winapi-i686-pc-windows-gnu-0.4.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/0.4.0/download" + ], + "strip_prefix": "winapi-i686-pc-windows-gnu-0.4.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-i686-pc-windows-gnu-0.4.0.bazel" + } + }, + "rrra__winapi-util-0.1.5": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/winapi-util/0.1.5/download" + ], + "strip_prefix": "winapi-util-0.1.5", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-util-0.1.5.bazel" + } + }, + "rrra__winapi-x86_64-pc-windows-gnu-0.4.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download" + ], + "strip_prefix": "winapi-x86_64-pc-windows-gnu-0.4.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.winapi-x86_64-pc-windows-gnu-0.4.0.bazel" + } + }, + "rrra__windows-sys-0.48.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows-sys/0.48.0/download" + ], + "strip_prefix": "windows-sys-0.48.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows-sys-0.48.0.bazel" + } + }, + "rrra__windows-targets-0.48.1": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows-targets/0.48.1/download" + ], + "strip_prefix": "windows-targets-0.48.1", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows-targets-0.48.1.bazel" + } + }, + "rrra__windows_aarch64_gnullvm-0.48.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows_aarch64_gnullvm/0.48.0/download" + ], + "strip_prefix": "windows_aarch64_gnullvm-0.48.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_aarch64_gnullvm-0.48.0.bazel" + } + }, + "rrra__windows_aarch64_msvc-0.48.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows_aarch64_msvc/0.48.0/download" + ], + "strip_prefix": "windows_aarch64_msvc-0.48.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_aarch64_msvc-0.48.0.bazel" + } + }, + "rrra__windows_i686_gnu-0.48.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows_i686_gnu/0.48.0/download" + ], + "strip_prefix": "windows_i686_gnu-0.48.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_i686_gnu-0.48.0.bazel" + } + }, + "rrra__windows_i686_msvc-0.48.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows_i686_msvc/0.48.0/download" + ], + "strip_prefix": "windows_i686_msvc-0.48.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_i686_msvc-0.48.0.bazel" + } + }, + "rrra__windows_x86_64_gnu-0.48.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows_x86_64_gnu/0.48.0/download" + ], + "strip_prefix": "windows_x86_64_gnu-0.48.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_x86_64_gnu-0.48.0.bazel" + } + }, + "rrra__windows_x86_64_gnullvm-0.48.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows_x86_64_gnullvm/0.48.0/download" + ], + "strip_prefix": "windows_x86_64_gnullvm-0.48.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_x86_64_gnullvm-0.48.0.bazel" + } + }, + "rrra__windows_x86_64_msvc-0.48.0": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a", + "type": "tar.gz", + "urls": [ + "https://static.crates.io/crates/windows_x86_64_msvc/0.48.0/download" + ], + "strip_prefix": "windows_x86_64_msvc-0.48.0", + "build_file": "@@rules_rust+//tools/rust_analyzer/3rdparty/crates:BUILD.windows_x86_64_msvc-0.48.0.bazel" + } + } + }, + "moduleExtensionMetadata": { + "explicitRootModuleDirectDeps": [ + "rules_rust_tinyjson", + "rrra__anyhow-1.0.71", + "rrra__clap-4.3.11", + "rrra__env_logger-0.10.0", + "rrra__itertools-0.11.0", + "rrra__log-0.4.19", + "rrra__serde-1.0.171", + "rrra__serde_json-1.0.102" + ], + "explicitRootModuleDirectDevDeps": [], + "useAllRepos": "NO", + "reproducible": false + }, + "recordedRepoMappingEntries": [ + [ + "rules_rust+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_rust+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_rust+", + "rrra__anyhow-1.0.71", + "rules_rust++i+rrra__anyhow-1.0.71" + ], + [ + "rules_rust+", + "rrra__clap-4.3.11", + "rules_rust++i+rrra__clap-4.3.11" + ], + [ + "rules_rust+", + "rrra__env_logger-0.10.0", + "rules_rust++i+rrra__env_logger-0.10.0" + ], + [ + "rules_rust+", + "rrra__itertools-0.11.0", + "rules_rust++i+rrra__itertools-0.11.0" + ], + [ + "rules_rust+", + "rrra__log-0.4.19", + "rules_rust++i+rrra__log-0.4.19" + ], + [ + "rules_rust+", + "rrra__serde-1.0.171", + "rules_rust++i+rrra__serde-1.0.171" + ], + [ + "rules_rust+", + "rrra__serde_json-1.0.102", + "rules_rust++i+rrra__serde_json-1.0.102" + ] + ] + } + } + } +} diff --git a/src/BUILD b/src/BUILD deleted file mode 100644 index e69de29..0000000 diff --git a/src/mw_log/BUILD b/src/mw_log/BUILD new file mode 100644 index 0000000..62ea381 --- /dev/null +++ b/src/mw_log/BUILD @@ -0,0 +1,23 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +load("@rules_rust//cargo:defs.bzl", "cargo_build_script") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library") + +rust_library( + name = "mw_log", + srcs = glob(["src/**/*.rs"]), + crate_features = ["std"], + crate_name = "mw_log", + edition = "2021", + visibility = ["//visibility:public"], +) diff --git a/src/mw_log/Cargo.toml b/src/mw_log/Cargo.toml index a448a2d..28a3b89 100644 --- a/src/mw_log/Cargo.toml +++ b/src/mw_log/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "mw_log" -version = "0.0.1" +version = "0.0.1" authors = ["S-CORE"] readme = "README.md" description = """ @@ -11,9 +11,6 @@ A lightweight logging facade for Rust rust-version = "1.61.0" edition = "2021" -[package.metadata.docs.rs] -features = ["std", "kv_std"] - [features] max_level_off = [] max_level_error = [] @@ -30,10 +27,10 @@ release_max_level_debug = [] release_max_level_trace = [] std = [] - kv = [] -kv_std = ["std", "kv", "value-bag/error"] - -[dependencies] -value-bag = { version = "1.7", optional = true, default-features = false, features = ["inline-i128"] } +# Not available but listed here to suppress warnings. +kv_sval = [] +kv_std = [] +kv_serde = [] +serde = [] diff --git a/src/mw_log_subscriber/BUILD b/src/mw_log_subscriber/BUILD new file mode 100644 index 0000000..0e814b6 --- /dev/null +++ b/src/mw_log_subscriber/BUILD @@ -0,0 +1,50 @@ +# ******************************************************************************* +# Copyright (c) 2025 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +load("@rules_rust//cargo:defs.bzl", "cargo_build_script") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library") + +cc_library( + name = "libmw_log_ffi", + srcs = ["src/mw_log_ffi.cpp"], + visibility = ["//visibility:public"], + deps = [ + "@score-baselibs//score/mw/log", + ], +) + +rust_library( + name = "mw_log_subscriber", + srcs = glob(["src/**/*.rs"]), + crate_name = "mw_log_subscriber", + edition = "2021", + visibility = ["//visibility:public"], + deps = [ + ":libmw_log_ffi", + "//src/mw_log", + ], +) + +rust_binary( + name = "example", + srcs = ["examples/main.rs"], + data = [ + "examples/config/logging.json", + ], + edition = "2021", + visibility = ["//visibility:public"], + deps = [ + ":mw_log_subscriber", + "//src/mw_log", + ], +) diff --git a/src/mw_log_subscriber/Cargo.toml b/src/mw_log_subscriber/Cargo.toml new file mode 100644 index 0000000..a3643dd --- /dev/null +++ b/src/mw_log_subscriber/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "mw_log_subscriber" +version.workspace = true +edition.workspace = true + +[dependencies] +mw_log.workspace = true + +[build-dependencies] +cc.workspace = true + +# TODO: make baselib work with Cargo so this module can be used in cargo diff --git a/src/mw_log_subscriber/examples/config/logging.json b/src/mw_log_subscriber/examples/config/logging.json new file mode 100644 index 0000000..cf4f333 --- /dev/null +++ b/src/mw_log_subscriber/examples/config/logging.json @@ -0,0 +1,7 @@ +{ + "appId": "JSON", + "appDesc": "JSON example programs", + "logMode" : "kConsole", + "logLevel": "kVerbose", + "logLevelThresholdConsole": "kInfo" +} diff --git a/src/mw_log_subscriber/examples/main.rs b/src/mw_log_subscriber/examples/main.rs new file mode 100644 index 0000000..bb74b84 --- /dev/null +++ b/src/mw_log_subscriber/examples/main.rs @@ -0,0 +1,74 @@ +// +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::path::PathBuf; + +use mw_log::{debug, error, info, trace, warn}; +use mw_log_subscriber::MwLoggerBuilder; + +fn main() { + //Setup for example using config file + let path = PathBuf::from(std::env::current_dir().unwrap()) + .join(file!()) + .parent() + .unwrap() + .join("config") + .join("logging.json"); + + std::env::set_var("MW_LOG_CONFIG_FILE", path.as_os_str()); + + // Just initialize and set as default logger + MwLoggerBuilder::new().set_as_default_logger::(); + + trace!("This is a trace log"); + debug!("This is a debug log"); + error!("This is an error log"); + info!("This is an info log"); + warn!("This is a warn log"); + + error!( + "This is an log that will be trimmed: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc + ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd + eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee + fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg + END MARKER NOT VISIBLE" + ); + + error!( + "This is an log that will be trimmed {} {} {} {} {} {} {}. END MARKER NOT VISIBLE", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg" + ); + + // Using logger instance with context + let logger = MwLoggerBuilder::new() + .context("ALFA") + .build::(); + + trace!( + logger : logger, + "This is a trace log" + ); + debug!(logger : logger, "This is a debug log"); + error!(logger : logger, "This is an error log"); + info!(logger : logger, "This is an info log"); + warn!(logger : logger, "This is a warn log"); +} diff --git a/src/mw_log_subscriber/src/lib.rs b/src/mw_log_subscriber/src/lib.rs new file mode 100644 index 0000000..61a8684 --- /dev/null +++ b/src/mw_log_subscriber/src/lib.rs @@ -0,0 +1,214 @@ +// +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// +// +// SPDX-License-Identifier: Apache-2.0 +// + +mod mw_log_ffi; + +use crate::mw_log_ffi::*; + +use core::ffi::c_char; +use core::fmt::{self, Write}; +use mw_log::{Level, Log, Metadata, Record}; +use std::ffi::CString; +use std::mem::MaybeUninit; + +const MSG_SIZE: usize = 512; + +/// Builder for the MwLogger +pub struct MwLoggerBuilder { + context: Option, +} + +impl MwLoggerBuilder { + pub fn new() -> Self { + Self { context: None } + } + + /// Builds the MwLogger with the specified context and configuration and returns it. + pub fn build( + self, + ) -> MwLogger { + let context_cstr = self.context.unwrap_or(CString::new("DFLT").unwrap()); + let c_logger_ptr = unsafe { mw_log_create_logger(context_cstr.as_ptr() as *const _) }; + MwLogger { + ptr: c_logger_ptr, + log_fn: log::, + } + } + + /// Builds and sets the MwLogger as the default logger with the specified configuration. + pub fn set_as_default_logger< + const SHOW_MODULE: bool, + const SHOW_FILE: bool, + const SHOW_LINE: bool, + >( + self, + ) { + let logger = self.build::(); + mw_log::set_max_level(mw_log_logger_level(logger.ptr)); + mw_log::set_boxed_logger(Box::new(logger)) + .expect("Failed to initialize MwLogger as default logger - logger may already be set"); + } + + /// Sets the context for currently build logger. + pub fn context(mut self, context: &str) -> Self { + self.context = Some(CString::new(context).expect( + "Failed to create CString: + input contains null bytes", + )); + self + } +} + +struct BufWriter { + buf: [MaybeUninit; BUF_SIZE], + pos: usize, +} + +impl BufWriter { + fn new() -> Self { + Self { + buf: [MaybeUninit::uninit(); BUF_SIZE], + pos: 0, + } + } + + /// Returns a slice to filled part of buffer. This is not null-terminated. + fn as_c_str(&self) -> &[c_char] { + unsafe { core::slice::from_raw_parts(self.buf.as_ptr().cast::(), self.len()) } + } + + fn len(&self) -> usize { + self.pos + } + + fn revert_pos(&mut self, cnt: usize) { + self.pos = self.pos.saturating_sub(cnt); + } + + // Finds the right index (around requested `index`) to trim utf8 string to a valid char boundary + fn floor_char_boundary(view: &str, index: usize) -> usize { + if index >= view.len() { + view.len() + } else { + let lower_bound = index.saturating_sub(3); + let new_index = view.as_bytes()[lower_bound..=index] + .iter() + .rposition(|b| Self::is_utf8_char_boundary(*b)); + + // SAFETY: we know that the character boundary will be within four bytes + unsafe { lower_bound + new_index.unwrap_unchecked() } + } + } + + const fn is_utf8_char_boundary(i: u8) -> bool { + // This is bit magic equivalent to: b < 128 || b >= 192 + (i as i8) >= -0x40 + } +} + +impl Write for BufWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + let bytes = s.as_bytes(); + let to_write = bytes.len().min(self.buf.len() - self.pos - 1); // Cutting write, instead error when we don't fit + let bounded_to_write = Self::floor_char_boundary(s, to_write); + + if bounded_to_write == 0 { + return Err(fmt::Error); + } + + let dest = self.buf[self.pos..self.pos + bounded_to_write] + .as_mut_ptr() + .cast::(); + + unsafe { core::ptr::copy_nonoverlapping(bytes.as_ptr(), dest, bounded_to_write) }; + + self.pos += bounded_to_write; + Ok(()) + } +} + +pub struct MwLogger { + ptr: *const Logger, + log_fn: fn(&mut BufWriter, &Record), +} + +// SAFETY: The underlying C++ logger is assumed to be safe to change thread +unsafe impl Send for MwLogger {} + +// SAFETY: The underlying C++ logger is assumed to be thread-safe. +unsafe impl Sync for MwLogger {} + +impl MwLogger { + fn write_log(&self, level: Level, msg: &BufWriter) { + let slice = msg.as_c_str(); + + unsafe { + match level { + Level::Error => mw_log_error_logger(self.ptr, slice.as_ptr(), slice.len() as u32), + Level::Warn => mw_log_warn_logger(self.ptr, slice.as_ptr(), slice.len() as u32), + Level::Info => mw_log_info_logger(self.ptr, slice.as_ptr(), slice.len() as u32), + Level::Debug => mw_log_debug_logger(self.ptr, slice.as_ptr(), slice.len() as u32), + Level::Trace => mw_log_verbose_logger(self.ptr, slice.as_ptr(), slice.len() as u32), + } + } + } +} + +impl Log for MwLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + mw_log_is_log_level_enabled(self.ptr, metadata.level()) + } + fn log(&self, record: &Record) { + if !self.enabled(record.metadata()) { + return; + } + + let mut msg_writer = BufWriter::::new(); + (self.log_fn)(&mut msg_writer, record); + + self.write_log(record.level(), &msg_writer); + } + + fn flush(&self) { + // No-op for this logger, as it does not buffer logs + } +} + +fn log( + msg_writer: &mut BufWriter, + record: &Record, +) { + if SHOW_FILE || SHOW_LINE || SHOW_MODULE { + let _ = write!(msg_writer, "["); + if SHOW_MODULE { + if let Some(module) = record.module_path() { + let _ = write!(msg_writer, "{}:", module); + } + } + if SHOW_FILE { + if let Some(file) = record.file() { + let _ = write!(msg_writer, "{}:", file); + } + } + if SHOW_LINE { + if let Some(line) = record.line() { + let _ = write!(msg_writer, "{}:", line); + } + } + + msg_writer.revert_pos(1); + let _ = write!(msg_writer, "] "); + } + + let _ = msg_writer.write_fmt(*record.args()); +} diff --git a/src/mw_log_subscriber/src/mw_log_ffi.cpp b/src/mw_log_subscriber/src/mw_log_ffi.cpp new file mode 100644 index 0000000..bc794aa --- /dev/null +++ b/src/mw_log_subscriber/src/mw_log_ffi.cpp @@ -0,0 +1,113 @@ +/******************************************************************************** + * Copyright (c) 2025 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/mw/log/logging.h" +#include "score/mw/log/configuration/configuration.h" +#include "score/mw/log/logger.h" +#include "score/mw/log/log_level.h" + +namespace score +{ + namespace mw + { + namespace log + { + + extern "C" + { + + Logger *mw_log_create_logger(const char *context) + { + return &CreateLogger(context); + } + + bool mw_log_is_log_level_enabled_internal(const Logger *logger, uint8_t level) + { + return logger->IsLogEnabled(GetLogLevelFromU8(level)); + } + + void mw_log_fatal_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogFatal() << LogString{message, size}; + } + + void mw_log_error_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogError() << LogString{message, size}; + } + + void mw_log_warn_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogWarn() << LogString{message, size}; + } + + void mw_log_info_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogInfo() << LogString{message, size}; + } + + void mw_log_debug_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogDebug() << LogString{message, size}; + } + + void mw_log_verbose_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogVerbose() << LogString{message, size}; + } + + uint8_t mw_log_logger_level_internal(const Logger *logger) + { + // TODO: This is adapter code, as there seems to be no way to get log level for Logger + if (logger->IsLogEnabled(LogLevel::kInfo)) + { + // Between Verbose, Debug, Info + if (logger->IsLogEnabled(LogLevel::kDebug)) + { + if (logger->IsLogEnabled(LogLevel::kVerbose)) + { + return static_cast(LogLevel::kVerbose); + } + + return static_cast(LogLevel::kDebug); + } + + return static_cast(LogLevel::kInfo); + } + else + { + // Lower half: Warn, Error, Fatal + if (logger->IsLogEnabled(LogLevel::kError)) + { + if (logger->IsLogEnabled(LogLevel::kWarn)) + { + return static_cast(LogLevel::kWarn); + } + + return static_cast(LogLevel::kError); + } + + if (logger->IsLogEnabled(LogLevel::kFatal)) + { + return static_cast(LogLevel::kFatal); + } + } + + // fallback + return static_cast(LogLevel::kOff); + } + } + + } // namespace log + } // namespace mw +} // namespace score diff --git a/src/mw_log_subscriber/src/mw_log_ffi.rs b/src/mw_log_subscriber/src/mw_log_ffi.rs new file mode 100644 index 0000000..8640c43 --- /dev/null +++ b/src/mw_log_subscriber/src/mw_log_ffi.rs @@ -0,0 +1,66 @@ +// +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// +// +// SPDX-License-Identifier: Apache-2.0 +// + +use core::ffi::c_char; + +use mw_log::{Level, LevelFilter}; + +// Opaque type representing the C++ logger ptr +#[repr(C)] +pub(crate) struct Logger { + _private: [u8; 0], // Opaque +} + +pub(crate) fn mw_log_logger_level(logger: *const Logger) -> LevelFilter { + let level = unsafe { mw_log_logger_level_internal(logger) }; + log_level_from_ffi(level) +} + +pub(crate) fn mw_log_is_log_level_enabled(logger: *const Logger, level: Level) -> bool { + let level_byte = match level { + Level::Error => 0x02, + Level::Warn => 0x03, + Level::Info => 0x04, + Level::Debug => 0x05, + Level::Trace => 0x06, + }; + unsafe { mw_log_is_log_level_enabled_internal(logger, level_byte) } +} + +/// Get the max log level from C++ as a LevelFilter directly +fn log_level_from_ffi(level: u8) -> LevelFilter { + match level { + 0x00 => LevelFilter::Off, + 0x01 => LevelFilter::Error, // Currently Fatal treated as Error + 0x02 => LevelFilter::Error, + 0x03 => LevelFilter::Warn, + 0x04 => LevelFilter::Info, + 0x05 => LevelFilter::Debug, + 0x06 => LevelFilter::Trace, // Verbose is Trace + _ => LevelFilter::Info, // fallback + } +} + +extern "C" { + + pub(crate) fn mw_log_create_logger(context: *const c_char) -> *const Logger; + pub(crate) fn mw_log_error_logger(logger: *const Logger, message: *const c_char, len: u32); + pub(crate) fn mw_log_warn_logger(logger: *const Logger, message: *const c_char, len: u32); + pub(crate) fn mw_log_info_logger(logger: *const Logger, message: *const c_char, len: u32); + pub(crate) fn mw_log_debug_logger(logger: *const Logger, message: *const c_char, len: u32); + pub(crate) fn mw_log_verbose_logger(logger: *const Logger, message: *const c_char, len: u32); + + fn mw_log_is_log_level_enabled_internal(logger: *const Logger, level: u8) -> bool; + fn mw_log_logger_level_internal(logger: *const Logger) -> u8; + +} From 1a82956aab14e7b9d20d9332688d5f51c341951e Mon Sep 17 00:00:00 2001 From: Pawel Rutka Date: Mon, 15 Sep 2025 09:13:02 +0200 Subject: [PATCH 4/4] logging: Apply fixes requested by review Signed-off-by: Pawel Rutka --- src/mw_log_subscriber/examples/main.rs | 2 +- src/mw_log_subscriber/src/lib.rs | 17 ++- src/mw_log_subscriber/src/mw_log_ffi.cpp | 138 +++++++++++------------ src/mw_log_subscriber/src/mw_log_ffi.rs | 2 +- 4 files changed, 79 insertions(+), 80 deletions(-) diff --git a/src/mw_log_subscriber/examples/main.rs b/src/mw_log_subscriber/examples/main.rs index bb74b84..42264a0 100644 --- a/src/mw_log_subscriber/examples/main.rs +++ b/src/mw_log_subscriber/examples/main.rs @@ -60,7 +60,7 @@ fn main() { // Using logger instance with context let logger = MwLoggerBuilder::new() - .context("ALFA") + .with_context("ALFA") .build::(); trace!( diff --git a/src/mw_log_subscriber/src/lib.rs b/src/mw_log_subscriber/src/lib.rs index 61a8684..22c19e1 100644 --- a/src/mw_log_subscriber/src/lib.rs +++ b/src/mw_log_subscriber/src/lib.rs @@ -60,7 +60,7 @@ impl MwLoggerBuilder { } /// Sets the context for currently build logger. - pub fn context(mut self, context: &str) -> Self { + pub fn with_context(mut self, context: &str) -> Self { self.context = Some(CString::new(context).expect( "Failed to create CString: input contains null bytes", @@ -69,6 +69,10 @@ impl MwLoggerBuilder { } } +/// A simple buffer writer that implements `core::fmt::Write` +/// and writes into a fixed-size(`BUF_SIZE) buffer. +/// Used in `Log` implementation to format log messages +/// before passing them to the underlying C++ logger. struct BufWriter { buf: [MaybeUninit; BUF_SIZE], pos: usize, @@ -83,14 +87,17 @@ impl BufWriter { } /// Returns a slice to filled part of buffer. This is not null-terminated. - fn as_c_str(&self) -> &[c_char] { + fn as_slice(&self) -> &[c_char] { + // SAFETY: We only expose already initialized part of the buffer unsafe { core::slice::from_raw_parts(self.buf.as_ptr().cast::(), self.len()) } } + /// Returns the current length of the filled part of the buffer. fn len(&self) -> usize { self.pos } + /// Reverts the current position (consumes) by `cnt` bytes, saturating at 0. fn revert_pos(&mut self, cnt: usize) { self.pos = self.pos.saturating_sub(cnt); } @@ -142,15 +149,15 @@ pub struct MwLogger { log_fn: fn(&mut BufWriter, &Record), } -// SAFETY: The underlying C++ logger is assumed to be safe to change thread +// SAFETY: The underlying C++ logger is known to be safe to change thread unsafe impl Send for MwLogger {} -// SAFETY: The underlying C++ logger is assumed to be thread-safe. +// SAFETY: The underlying C++ logger is known to be thread-safe. unsafe impl Sync for MwLogger {} impl MwLogger { fn write_log(&self, level: Level, msg: &BufWriter) { - let slice = msg.as_c_str(); + let slice = msg.as_slice(); unsafe { match level { diff --git a/src/mw_log_subscriber/src/mw_log_ffi.cpp b/src/mw_log_subscriber/src/mw_log_ffi.cpp index bc794aa..22ec0ed 100644 --- a/src/mw_log_subscriber/src/mw_log_ffi.cpp +++ b/src/mw_log_subscriber/src/mw_log_ffi.cpp @@ -16,98 +16,90 @@ #include "score/mw/log/logger.h" #include "score/mw/log/log_level.h" -namespace score +namespace score::mw::log { - namespace mw + extern "C" { - namespace log + + Logger *mw_log_create_logger(const char *context) { + return &CreateLogger(context); + } - extern "C" - { + bool mw_log_is_log_level_enabled_internal(const Logger *logger, uint8_t level) + { + return logger->IsLogEnabled(GetLogLevelFromU8(level)); + } - Logger *mw_log_create_logger(const char *context) - { - return &CreateLogger(context); - } + void mw_log_fatal_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogFatal() << LogString{message, size}; + } - bool mw_log_is_log_level_enabled_internal(const Logger *logger, uint8_t level) - { - return logger->IsLogEnabled(GetLogLevelFromU8(level)); - } + void mw_log_error_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogError() << LogString{message, size}; + } - void mw_log_fatal_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogFatal() << LogString{message, size}; - } + void mw_log_warn_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogWarn() << LogString{message, size}; + } - void mw_log_error_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogError() << LogString{message, size}; - } + void mw_log_info_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogInfo() << LogString{message, size}; + } - void mw_log_warn_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogWarn() << LogString{message, size}; - } + void mw_log_debug_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogDebug() << LogString{message, size}; + } - void mw_log_info_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogInfo() << LogString{message, size}; - } + void mw_log_verbose_logger(const Logger *logger, const char *message, uint32_t size) + { + logger->LogVerbose() << LogString{message, size}; + } - void mw_log_debug_logger(const Logger *logger, const char *message, uint32_t size) + uint8_t mw_log_logger_level_internal(const Logger *logger) + { + // TODO: This is adapter code, as there seems to be no way to get log level for Logger + if (logger->IsLogEnabled(LogLevel::kInfo)) + { + // Between Verbose, Debug, Info + if (logger->IsLogEnabled(LogLevel::kDebug)) { - logger->LogDebug() << LogString{message, size}; - } + if (logger->IsLogEnabled(LogLevel::kVerbose)) + { + return static_cast(LogLevel::kVerbose); + } - void mw_log_verbose_logger(const Logger *logger, const char *message, uint32_t size) - { - logger->LogVerbose() << LogString{message, size}; + return static_cast(LogLevel::kDebug); } - uint8_t mw_log_logger_level_internal(const Logger *logger) + return static_cast(LogLevel::kInfo); + } + else + { + // Lower half: Warn, Error, Fatal + if (logger->IsLogEnabled(LogLevel::kError)) { - // TODO: This is adapter code, as there seems to be no way to get log level for Logger - if (logger->IsLogEnabled(LogLevel::kInfo)) - { - // Between Verbose, Debug, Info - if (logger->IsLogEnabled(LogLevel::kDebug)) - { - if (logger->IsLogEnabled(LogLevel::kVerbose)) - { - return static_cast(LogLevel::kVerbose); - } - - return static_cast(LogLevel::kDebug); - } - - return static_cast(LogLevel::kInfo); - } - else + if (logger->IsLogEnabled(LogLevel::kWarn)) { - // Lower half: Warn, Error, Fatal - if (logger->IsLogEnabled(LogLevel::kError)) - { - if (logger->IsLogEnabled(LogLevel::kWarn)) - { - return static_cast(LogLevel::kWarn); - } - - return static_cast(LogLevel::kError); - } - - if (logger->IsLogEnabled(LogLevel::kFatal)) - { - return static_cast(LogLevel::kFatal); - } + return static_cast(LogLevel::kWarn); } - // fallback - return static_cast(LogLevel::kOff); + return static_cast(LogLevel::kError); + } + + if (logger->IsLogEnabled(LogLevel::kFatal)) + { + return static_cast(LogLevel::kFatal); } } - } // namespace log - } // namespace mw -} // namespace score + // fallback + return static_cast(LogLevel::kOff); + } + } +} // namespace score::mw::log diff --git a/src/mw_log_subscriber/src/mw_log_ffi.rs b/src/mw_log_subscriber/src/mw_log_ffi.rs index 8640c43..7c2bd12 100644 --- a/src/mw_log_subscriber/src/mw_log_ffi.rs +++ b/src/mw_log_subscriber/src/mw_log_ffi.rs @@ -53,7 +53,7 @@ fn log_level_from_ffi(level: u8) -> LevelFilter { extern "C" { - pub(crate) fn mw_log_create_logger(context: *const c_char) -> *const Logger; + pub(crate) fn mw_log_create_logger(context: *const c_char) -> *mut Logger; pub(crate) fn mw_log_error_logger(logger: *const Logger, message: *const c_char, len: u32); pub(crate) fn mw_log_warn_logger(logger: *const Logger, message: *const c_char, len: u32); pub(crate) fn mw_log_info_logger(logger: *const Logger, message: *const c_char, len: u32);