mirror of
https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator.git
synced 2025-07-16 08:29:25 +09:00
Compare commits
49 commits
ac72b3638e
...
3fc6bd14fe
Author | SHA1 | Date | |
---|---|---|---|
3fc6bd14fe | |||
72371e52bf | |||
a652b40228 | |||
c4407639a0 | |||
b0ceb39278 | |||
d41bc7c1b0 | |||
76c19d194d | |||
a6c56ef9d6 | |||
|
472723016c | ||
|
ae01a30cb0 | ||
|
d85642dcec | ||
|
7aba746503 | ||
|
b5a189f9a1 | ||
|
3e72513b75 | ||
|
76310c64a3 | ||
|
6ff047563c | ||
|
fd106e3355 | ||
cb67f9648b | |||
d2f9cd603f | |||
cba4b3af05 | |||
9e7a83242b | |||
5f7db47fcf | |||
fe6ce5a0dd | |||
71c25dd379 | |||
cc0fe59b28 | |||
9a882fdab1 | |||
28b2b198a8 | |||
4131ce1880 | |||
7c1a0856a0 | |||
9728af3551 | |||
30279dc2b3 | |||
39a793462a | |||
dbb124ec76 | |||
c3effa280a | |||
fdb87a0898 | |||
f96aab4d9d | |||
49a9698a32 | |||
80ce358aee | |||
859d9c0fa3 | |||
1ed3843508 | |||
48215049e8 | |||
86d26c57cb | |||
a87e81ad5b | |||
e147bc513d | |||
d104e62bf3 | |||
eec855c3a1 | |||
8e0761e39e | |||
6b5b5e0234 | |||
dfd28782dd |
18 changed files with 2430 additions and 20 deletions
41
.gitlab-ci.yml
Normal file
41
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,41 @@
|
|||
# This file is a template, and might need editing before it works on your project.
|
||||
# This is a sample GitLab CI/CD configuration file that should run without any modifications.
|
||||
# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
|
||||
# it uses echo commands to simulate the pipeline execution.
|
||||
#
|
||||
# A pipeline is composed of independent jobs that run scripts, grouped into stages.
|
||||
# Stages run in sequential order, but jobs within stages run in parallel.
|
||||
#
|
||||
# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
|
||||
#
|
||||
# You can copy and paste this template into a new `.gitlab-ci.yml` file.
|
||||
# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
|
||||
#
|
||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml
|
||||
|
||||
stages: # List of stages for jobs, and their order of execution
|
||||
- build
|
||||
- test
|
||||
|
||||
.setup-rust:
|
||||
image: rust:latest
|
||||
before_script:
|
||||
- rustup component add clippy rustfmt
|
||||
|
||||
rust-latest:
|
||||
stage: build
|
||||
extends: .setup-rust
|
||||
script:
|
||||
- cargo build --verbose
|
||||
- cargo test --verbose
|
||||
- cargo doc
|
||||
|
||||
lint-test-job: # This job also runs in the test stage.
|
||||
stage: test # It can run at the same time as unit-test-job (in parallel).
|
||||
extends: .setup-rust
|
||||
script:
|
||||
- cargo fmt -- --check
|
||||
- cargo clippy
|
14
CHANGELOG.md
Normal file
14
CHANGELOG.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.1.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Implemented basic code generation covering current CSR XML.
|
||||
|
||||
[0.1.0]: https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator/~/tags/v0.1.0
|
775
Cargo.lock
generated
775
Cargo.lock
generated
|
@ -11,6 +11,21 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
|
@ -67,21 +82,296 @@ version = "1.0.95"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "endcap-sl-software-ri-generator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"heck",
|
||||
"hex",
|
||||
"itertools",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"roxmltree",
|
||||
"sha2",
|
||||
"syn",
|
||||
"thiserror",
|
||||
"typenum",
|
||||
"vergen-gitcl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -107,18 +397,106 @@ dependencies = [
|
|||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.25"
|
||||
|
@ -131,12 +509,61 @@ version = "2.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
|
@ -155,6 +582,26 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
|
@ -190,6 +637,91 @@ version = "0.20.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.96"
|
||||
|
@ -201,6 +733,20 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.33.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"memchr",
|
||||
"ntapi",
|
||||
"rayon",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
|
@ -221,6 +767,45 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
|
@ -233,6 +818,196 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "9.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0d2f179f8075b805a43a2a21728a46f0cc2921b3c58695b28fa8817e103cd9a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_metadata",
|
||||
"derive_builder",
|
||||
"regex",
|
||||
"rustc_version",
|
||||
"rustversion",
|
||||
"sysinfo",
|
||||
"time",
|
||||
"vergen-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vergen-gitcl"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2f89d70a58a4506a6079cedf575c64cf51649ccbb4e02a63dac539b264b7711"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_builder",
|
||||
"rustversion",
|
||||
"time",
|
||||
"vergen",
|
||||
"vergen-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vergen-lib"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_builder",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
||||
dependencies = [
|
||||
"windows-core 0.57.0",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
|
18
Cargo.toml
18
Cargo.toml
|
@ -1,7 +1,11 @@
|
|||
[package]
|
||||
name = "endcap-sl-software-ri-generator"
|
||||
version = "0.1.0"
|
||||
authors = ["Wataru Otsubo <wataru.otsubo@cern.ch>"]
|
||||
edition = "2021"
|
||||
description = "A generator of register interface for mpsoc software from register map in xml format"
|
||||
repository = "https://gitlab.cern.ch/wotsubo/endcap-sl-software-ri-generator"
|
||||
build = "build.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "endcap-sl-software-ri-generator"
|
||||
|
@ -13,7 +17,21 @@ path = "src/lib.rs"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.95"
|
||||
chrono = "0.4.39"
|
||||
clap = { version = "4.5.28", features = ["derive"] }
|
||||
env_logger = "0.11.6"
|
||||
heck = "0.5"
|
||||
hex = "0.4.3"
|
||||
itertools = "0.14"
|
||||
log = "0.4"
|
||||
prettyplease = "0.2"
|
||||
proc-macro2 = "1.0.93"
|
||||
quote = "1.0"
|
||||
roxmltree = "0.20"
|
||||
sha2 = "0.10"
|
||||
syn = "2.0.96"
|
||||
thiserror = "2.0"
|
||||
typenum = "1.17.0"
|
||||
|
||||
[build-dependencies]
|
||||
vergen-gitcl = { version = "1.0.0", features = ["build", "cargo", "rustc", "si"] }
|
||||
|
|
79
README-ja.md
Normal file
79
README-ja.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
# Endcap SL Software RI Generator
|
||||
|
||||
これはXMLのレジスタマップからMPSoCソフトで使う用のコードを生成するソフトです。
|
||||
|
||||
XMLおよびそのスキーマは[L0 Muon Endcap/Endcap Sl CSR XML](https://gitlab.cern.ch/l0muon-endcap/endcap-sl-csr-xml)にあります。
|
||||
生成されたコードは[MPSoC Software](https://gitlab.cern.ch/wotsubo/mpsoc-software)で使うことが想定されています。
|
||||
そこに`RegisterSpec`などのレジスタ向けのトレイトが定義されてます。
|
||||
|
||||
## 使い方
|
||||
|
||||
このプロジェクトでは、バイナリとライブラリの両方のクレートを提供しています。
|
||||
バイナリクレートはシェルから使うことができ、ライブラリクレートは他のプログラムから使うためのものです。
|
||||
|
||||
### ビルド環境の構築
|
||||
|
||||
このコードは[Rust](https://www.rust-lang.org/ja/)で書かれているので、[rustup](https://www.rust-lang.org/ja/tools/install)をインストールする必要があります。
|
||||
rustupのインストールには公式サイトにある`curl`スクリプトを使うか、あるいはシステムのパッケージマネージャを使うことができます。
|
||||
`cargo`を実行してみることでインストールのチェックができます。
|
||||
|
||||
`cargo`はlxplusにインストールされているみたいなので、それも使えるかもしれません(ただし、バージョンは最新ではありません)。
|
||||
|
||||
### バイナリクレートの使い方
|
||||
|
||||
```bash
|
||||
cargo build --bins --release
|
||||
```
|
||||
を実行します。
|
||||
`--release`はオプションです。
|
||||
|
||||
`target/release`に入ってるバイナリファイルを実行するか、`cargo run -- <XML> <OUT>`で実行できます。
|
||||
|
||||
詳しくは`--help`を見てください。
|
||||
|
||||
### ライブラリクレートの使い方
|
||||
|
||||
```bash
|
||||
cargo doc --open
|
||||
```
|
||||
|
||||
で閲覧できるドキュメントを見てください。
|
||||
|
||||
## 開発
|
||||
|
||||
```bash
|
||||
cargo doc --document-private-items --open
|
||||
```
|
||||
|
||||
でみれるプライベートアイテムを含んだドキュメントを見てください。
|
||||
|
||||
テストなどは以下のコマンドで実行できます
|
||||
```bash
|
||||
$ cargo test # テスト
|
||||
$ cargo fmt # フォーマット
|
||||
$ cargo clippy # リント (`rustup component add clippy`でインストールできます)
|
||||
```
|
||||
|
||||
### rust-analyzer
|
||||
|
||||
開発には[rust-analyzer](https://rust-analyzer.github.io)を使用することを __強く推奨__ します。
|
||||
推論された型の表示や補完に関して必要不可欠な上、フォーマットやclippyを実行することができます。
|
||||
|
||||
### CIについて
|
||||
|
||||
ビルドとフォーマットとclippyのテストが走ります。
|
||||
フォーマットやclippyのチェックをしてpushしてください。
|
||||
|
||||
### Rustについて
|
||||
|
||||
以下のウェブサイトでRustを良く学べます。
|
||||
|
||||
- [The Book](https://doc.rust-jp.rs/book-ja/)
|
||||
|
||||
もしくはターミナルで
|
||||
```bash
|
||||
rustup doc
|
||||
```
|
||||
を実行してみてください。
|
||||
様々な有益なドキュメントへのリンクが載っています(自分はThe Bookとstandard libraryのドキュメントとRust By Exampleが役に立ちました)。
|
||||
|
82
README.md
Normal file
82
README.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
# Endcap SL Software RI Generator
|
||||
|
||||
[日本語版](README-ja.md)
|
||||
|
||||
Generates register interface for mpsoc software from register map in xml format.
|
||||
|
||||
The XML and its schema definition are in [L0 Muon Endcap/Endcap Sl CSR XML](https://gitlab.cern.ch/l0muon-endcap/endcap-sl-csr-xml).
|
||||
The generated code is intended to be used in [MPSoC Software](https://gitlab.cern.ch/wotsubo/mpsoc-software), which provides definition of register traits (`RegisterSpec`) used in the generated code.
|
||||
|
||||
## Usage
|
||||
|
||||
This project provides both binary and library crates.
|
||||
Binary crates can be used from shell, while library crates can be used in other programs.
|
||||
|
||||
### Setting up building environment
|
||||
|
||||
This code is written in [Rust](https://www.rust-lang.org), so you need to install [rustup](https://www.rust-lang.org/tools/install).
|
||||
You can use either officially provided `curl` script, or system package manager to install.
|
||||
You can check the installation by executing `cargo` in the terminal.
|
||||
|
||||
Note that `cargo` is available on lxplus, so you might be able to use that (it is not the latest version though).
|
||||
|
||||
### Binary crate usage
|
||||
|
||||
Execute
|
||||
|
||||
```bash
|
||||
cargo build --bins --release
|
||||
```
|
||||
|
||||
to build.
|
||||
`--release` is optional(this is a compiler optimization config).
|
||||
|
||||
Execute the binary generated in `target/release/`, or run with `cargo run -- <XML> <OUT>`.
|
||||
|
||||
See the `--help` for more information.
|
||||
|
||||
### Library crate usage
|
||||
|
||||
See the doc, which is available with
|
||||
|
||||
```bash
|
||||
cargo doc --open
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
See the doc with private items with
|
||||
|
||||
```bash
|
||||
cargo doc --document-private-items --open
|
||||
```
|
||||
|
||||
You can execute test, format and lint with:
|
||||
|
||||
```bash
|
||||
$ cargo test # test
|
||||
$ cargo fmt # format
|
||||
$ cargo clippy # lint (run `rustup component add clippy` to install)
|
||||
```
|
||||
|
||||
### rust-analyzer
|
||||
|
||||
It is __heavily recommended__ to use [rust-analyzer](https://rust-analyzer.github.io) in development.
|
||||
It is not only essential for displaying inferred types and completion, but also it can execute formatter and clippy.
|
||||
|
||||
### About CI
|
||||
|
||||
In the CI, test for build, format and clippy run.
|
||||
Please format your code and check clippy before pushing.
|
||||
|
||||
### About Rust
|
||||
|
||||
You can learn Rust with the following webpage.
|
||||
|
||||
- [The Book](https://doc.rust-lang.org/book/)
|
||||
|
||||
Or in terminal, execute
|
||||
```bash
|
||||
rustup doc
|
||||
```
|
||||
It has a lot of links to helpful documents (personally, I appreciate "The Book", std library doc and "Rust By Example").
|
18
build.rs
Normal file
18
build.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Get build metadata
|
||||
let build = BuildBuilder::default().build_timestamp(true).build()?;
|
||||
let gitcl = GitclBuilder::default()
|
||||
.commit_date(true)
|
||||
.describe(false, true, None)
|
||||
.commit_timestamp(true)
|
||||
.sha(false)
|
||||
.build()?;
|
||||
Emitter::default()
|
||||
.add_instructions(&build)?
|
||||
.add_instructions(&gitcl)?
|
||||
.emit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,8 +1,13 @@
|
|||
//! Convert DOM to register interface, complementing optional parameters.
|
||||
//! Convert DOM to register interface defined in [`crate::types`], complementing optional parameters.
|
||||
//!
|
||||
//! root: [`Module::from_xml_dom`]
|
||||
//!
|
||||
//! error: [`DomConversionError`]
|
||||
|
||||
use std::{num, str};
|
||||
|
||||
use roxmltree::{Node, TextPos};
|
||||
use sha2::{Digest, Sha256};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::parser::{ParseCustomBool, ParseEnumError, ParsePrefixedU32, ParsePrefixedU32Error};
|
||||
|
@ -11,6 +16,7 @@ use crate::types::{
|
|||
Value,
|
||||
};
|
||||
|
||||
/// Possible errors in conversion, with positional information.
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
pub enum DomConversionError {
|
||||
#[error("attribute {attr} not found in element: {start} - {end}", start = pos.0, end = pos.1)]
|
||||
|
@ -49,8 +55,11 @@ pub enum DomConversionError {
|
|||
param: &'static str,
|
||||
pos: (TextPos, TextPos),
|
||||
},
|
||||
#[error("other dom conversion error: {0}")]
|
||||
OtherError(String),
|
||||
#[error("other dom conversion error: {comment}: {start} - {end}", start = pos.0, end = pos.1)]
|
||||
OtherError {
|
||||
comment: &'static str,
|
||||
pos: (TextPos, TextPos),
|
||||
},
|
||||
}
|
||||
|
||||
impl DomConversionError {
|
||||
|
@ -110,6 +119,15 @@ impl DomConversionError {
|
|||
found,
|
||||
}
|
||||
}
|
||||
|
||||
fn other_error(comment: &'static str, node: Node) -> Self {
|
||||
let range = node.range();
|
||||
let doc = node.document();
|
||||
Self::OtherError {
|
||||
comment,
|
||||
pos: (doc.text_pos_at(range.start), doc.text_pos_at(range.end)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod util {
|
||||
|
@ -121,7 +139,11 @@ mod util {
|
|||
|
||||
pub(crate) fn get_name(node: Node) -> Result<String, DomConversionError> {
|
||||
match node.attribute("name") {
|
||||
Some(name) => Ok(name.to_string()),
|
||||
Some(name) if !name.is_empty() => Ok(name.to_string()),
|
||||
Some(_name) => Err(DomConversionError::other_error(
|
||||
"name cannot be empty",
|
||||
node,
|
||||
)),
|
||||
None => Err(DomConversionError::attr_not_found("name", node)),
|
||||
}
|
||||
}
|
||||
|
@ -189,6 +211,7 @@ impl Module {
|
|||
desc: node.attribute("desc").map(str::to_string),
|
||||
elements_bitstring: child_bitstrings,
|
||||
elements_other: child_other,
|
||||
xmlhash: Sha256::digest(node.document().input_text()).into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -281,8 +304,9 @@ impl Block {
|
|||
.next()
|
||||
{
|
||||
Some(s) => Ok(s.to_string()),
|
||||
None => Err(DomConversionError::OtherError(
|
||||
"decoder format is not yet fixed".to_string(),
|
||||
None => Err(DomConversionError::other_error(
|
||||
"decoder format is not yet fixed",
|
||||
node,
|
||||
)),
|
||||
}?,
|
||||
};
|
||||
|
@ -323,6 +347,18 @@ impl Register {
|
|||
.map_err(|e| DomConversionError::parse_prefixed_u32_error(e, "addr", node))?,
|
||||
None => 0,
|
||||
};
|
||||
let r#type = util::get_type(node)
|
||||
.transpose()?
|
||||
.map_or_else(
|
||||
|| {
|
||||
node.ancestors()
|
||||
.filter_map(util::get_type)
|
||||
.next()
|
||||
.transpose()
|
||||
},
|
||||
|x| Ok(Some(x)),
|
||||
)?
|
||||
.ok_or_else(|| DomConversionError::parameter_completion_error("type", node))?;
|
||||
let mask = node
|
||||
.attribute("mask")
|
||||
.map(|addr| {
|
||||
|
@ -352,15 +388,41 @@ impl Register {
|
|||
.transpose()?;
|
||||
let desc = node.attribute("desc").map(str::to_string);
|
||||
|
||||
let children = node
|
||||
let children: Vec<_> = node
|
||||
.children()
|
||||
.filter(|node| node.is_element() && node.tag_name().name().eq("field"))
|
||||
.map(Field::from_xml_dom)
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
// Validation
|
||||
if mask.is_some() && !children.is_empty() {
|
||||
return Err(DomConversionError::other_error(
|
||||
"both mask and field are used in the same register",
|
||||
node,
|
||||
));
|
||||
}
|
||||
if default.is_some() && !children.is_empty() {
|
||||
return Err(DomConversionError::other_error(
|
||||
"both default and field are used in the same register",
|
||||
node,
|
||||
));
|
||||
}
|
||||
if let (Some(mask), Some(default)) = (mask, default) {
|
||||
if default & !(mask) != 0 {
|
||||
log::warn!(
|
||||
"default value {} doesn't fit mask {}: {} - {}",
|
||||
default,
|
||||
mask,
|
||||
node.document().text_pos_at(node.range().start),
|
||||
node.document().text_pos_at(node.range().end)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Register {
|
||||
name,
|
||||
addr,
|
||||
r#type,
|
||||
mask,
|
||||
modf,
|
||||
multiple,
|
||||
|
@ -372,13 +434,13 @@ impl Register {
|
|||
}
|
||||
|
||||
impl Memory {
|
||||
pub(crate) fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
||||
pub(crate) fn from_xml_dom(_node: Node) -> Result<Self, DomConversionError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Fifo {
|
||||
pub(crate) fn from_xml_dom(node: Node) -> Result<Self, DomConversionError> {
|
||||
pub(crate) fn from_xml_dom(_node: Node) -> Result<Self, DomConversionError> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
@ -424,6 +486,7 @@ impl Field {
|
|||
.map(Value::from_xml_dom)
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
// Validation
|
||||
if let Some(default) = default {
|
||||
if default & !(mask) != 0 {
|
||||
log::warn!(
|
||||
|
|
468
src/generator.rs
Normal file
468
src/generator.rs
Normal file
|
@ -0,0 +1,468 @@
|
|||
//! Generate register interface rust code from types in [`crate::types`].
|
||||
//!
|
||||
//! root: [`Module::generate_code`]
|
||||
//!
|
||||
//! # For developers
|
||||
//! Pass `--document-private-items` to see non-public items.
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{self, PathBuf},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
meta::{
|
||||
GENERATOR_BUILD_TIMESTAMP, GENERATOR_GIT_COMMIT_TIMESTAMP, GENERATOR_GIT_DESCRIBE,
|
||||
GENERATOR_GIT_SHA,
|
||||
},
|
||||
type_traits::GetName,
|
||||
types::{Block, Module, ModuleBlockElements, Register},
|
||||
};
|
||||
use chrono::Local;
|
||||
use heck::{ToSnakeCase, ToUpperCamelCase};
|
||||
use itertools::Itertools;
|
||||
use quote::quote;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CodeGenError {
|
||||
#[error("tokenization(syn) error: {0}")]
|
||||
SynError(#[from] syn::Error),
|
||||
#[error("failed to create file: {0}")]
|
||||
FilePathError(String),
|
||||
#[error("parent is required for {module}")]
|
||||
ParentMissing { module: &'static str },
|
||||
#[error("Unsupported structure: {}", name)]
|
||||
UnsupportedStructure { name: &'static str },
|
||||
}
|
||||
|
||||
mod util {
|
||||
use crate::types::DataType;
|
||||
|
||||
use super::CodeGenError;
|
||||
|
||||
pub(super) fn parse_to_ident(s: &str) -> Result<proc_macro2::Ident, CodeGenError> {
|
||||
Ok(syn::parse_str(s)?)
|
||||
}
|
||||
|
||||
pub(super) fn parse_to_literal(s: &str) -> Result<proc_macro2::Literal, CodeGenError> {
|
||||
Ok(syn::parse_str(s)?)
|
||||
}
|
||||
|
||||
// currently only U32 is used, so `dead_code` for Debug, PartialEq
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) enum RustUxTypes {
|
||||
Bool,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
}
|
||||
|
||||
impl RustUxTypes {
|
||||
/// Derive appropriate rust types for `mask`ed value.
|
||||
pub(super) fn from_mask(mask: u32) -> RustUxTypes {
|
||||
match 32 - mask.leading_zeros() - mask.trailing_zeros() {
|
||||
0 => panic!("mask cannot be 0"),
|
||||
1 => RustUxTypes::Bool,
|
||||
x if 1 < x && x <= 8 => RustUxTypes::U8,
|
||||
x if 8 < x && x <= 16 => RustUxTypes::U16,
|
||||
x if 16 < x && x <= 32 => RustUxTypes::U32,
|
||||
_ => panic!("supposed not to be reachable"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn to_rust_type_token(&self) -> proc_macro2::Ident {
|
||||
match self {
|
||||
RustUxTypes::Bool => parse_to_ident("bool").unwrap(),
|
||||
RustUxTypes::U8 => parse_to_ident("u8").unwrap(),
|
||||
RustUxTypes::U16 => parse_to_ident("u16").unwrap(),
|
||||
RustUxTypes::U32 => parse_to_ident("u32").unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&DataType> for RustUxTypes {
|
||||
fn from(value: &DataType) -> Self {
|
||||
match value {
|
||||
DataType::D32 => Self::U32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::RustUxTypes;
|
||||
|
||||
#[test]
|
||||
fn rustuxtypes_from_mask() {
|
||||
assert_eq!(RustUxTypes::from_mask(0x1), RustUxTypes::Bool);
|
||||
assert_eq!(RustUxTypes::from_mask(0x20), RustUxTypes::Bool);
|
||||
assert_eq!(RustUxTypes::from_mask(0x4000), RustUxTypes::Bool);
|
||||
assert_eq!(RustUxTypes::from_mask(0x80_0000), RustUxTypes::Bool);
|
||||
assert_eq!(RustUxTypes::from_mask(0x100_0000), RustUxTypes::Bool);
|
||||
|
||||
assert_eq!(RustUxTypes::from_mask(0x0300_0000), RustUxTypes::U8);
|
||||
assert_eq!(RustUxTypes::from_mask(0x0000_01e0), RustUxTypes::U8);
|
||||
|
||||
assert_eq!(RustUxTypes::from_mask(0x0000_01f0), RustUxTypes::U8);
|
||||
assert_eq!(RustUxTypes::from_mask(0x000f_f000), RustUxTypes::U8);
|
||||
|
||||
assert_eq!(RustUxTypes::from_mask(0x0fff_0000), RustUxTypes::U16);
|
||||
assert_eq!(RustUxTypes::from_mask(0x0f0f_0000), RustUxTypes::U16);
|
||||
assert_eq!(RustUxTypes::from_mask(0x010f_8000), RustUxTypes::U16);
|
||||
|
||||
assert_eq!(RustUxTypes::from_mask(0xffff_f000), RustUxTypes::U32);
|
||||
assert_eq!(RustUxTypes::from_mask(0x1fff_ff00), RustUxTypes::U32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rustuxtypes_to_token() {
|
||||
assert_eq!(
|
||||
RustUxTypes::U8.to_rust_type_token(),
|
||||
proc_macro2::Ident::new("u8", proc_macro2::Span::call_site())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn generate_code(self) -> Result<HashMap<path::PathBuf, syn::File>, CodeGenError> {
|
||||
let build_metadata = format!(
|
||||
"
|
||||
# Build metadata
|
||||
|
||||
- timestamp: {}
|
||||
|
||||
## CSR XML
|
||||
|
||||
- sha256: {}
|
||||
- git describe: TODO (after building step is fixed)
|
||||
- git commit timestamp: TODO
|
||||
- git SHA: TODO
|
||||
|
||||
## Generator
|
||||
|
||||
- build timestamp: {}
|
||||
- git describe: {}
|
||||
- git commit timestamp: {}
|
||||
- git SHA: {}
|
||||
",
|
||||
Local::now().to_rfc3339_opts(chrono::SecondsFormat::Nanos, false),
|
||||
hex::encode(self.xmlhash),
|
||||
GENERATOR_BUILD_TIMESTAMP,
|
||||
GENERATOR_GIT_DESCRIBE,
|
||||
GENERATOR_GIT_COMMIT_TIMESTAMP,
|
||||
GENERATOR_GIT_SHA,
|
||||
);
|
||||
let files = self.generate_register_interface(None, None, HashMap::new())?;
|
||||
Ok(files
|
||||
.into_iter()
|
||||
.map(
|
||||
|(path, tokens)| -> Result<(PathBuf, syn::File), syn::Error> {
|
||||
let tokens = if path
|
||||
.file_name()
|
||||
.is_some_and(|file| file == "register_interface.rs")
|
||||
{
|
||||
quote! {
|
||||
#![doc = #build_metadata]
|
||||
|
||||
#tokens
|
||||
}
|
||||
} else {
|
||||
tokens
|
||||
};
|
||||
let file: syn::File = syn::parse2(tokens)?;
|
||||
Ok((path, file))
|
||||
},
|
||||
)
|
||||
.process_results(|kv| HashMap::from_iter(kv))?)
|
||||
}
|
||||
}
|
||||
|
||||
trait CodeGen {
|
||||
/// `parent_name` in UpperCamelCase.
|
||||
fn generate_register_interface(
|
||||
self,
|
||||
parent_name: Option<proc_macro2::Ident>,
|
||||
parent_path: Option<path::PathBuf>,
|
||||
files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
|
||||
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError>;
|
||||
}
|
||||
|
||||
impl CodeGen for Module {
|
||||
fn generate_register_interface(
|
||||
self,
|
||||
_: Option<proc_macro2::Ident>,
|
||||
_: Option<path::PathBuf>,
|
||||
mut files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
|
||||
) -> std::result::Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError> {
|
||||
if !self.elements_bitstring.is_empty() {
|
||||
todo!("bitstring generation is not yet implemented")
|
||||
}
|
||||
|
||||
let child_mods = self
|
||||
.elements_other
|
||||
.iter()
|
||||
.map(|e| {
|
||||
util::parse_to_ident(&e.get_name().to_snake_case()).map(|child_name| {
|
||||
quote! {
|
||||
pub mod #child_name;
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let out = quote! {
|
||||
#(#child_mods)*
|
||||
};
|
||||
files.insert(PathBuf::from("./register_interface.rs"), out);
|
||||
|
||||
let ident_register_interface = util::parse_to_ident("RegisterInterface").unwrap();
|
||||
let register_interface_mod = PathBuf::from("register_interface");
|
||||
let files = self
|
||||
.elements_other
|
||||
.into_iter()
|
||||
.try_fold(files, |files, e| {
|
||||
e.generate_register_interface(
|
||||
Some(ident_register_interface.clone()),
|
||||
Some(register_interface_mod.clone()),
|
||||
files,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(files)
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeGen for ModuleBlockElements {
|
||||
fn generate_register_interface(
|
||||
self,
|
||||
parent_name: Option<proc_macro2::Ident>,
|
||||
parent_path: Option<path::PathBuf>,
|
||||
files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
|
||||
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError> {
|
||||
match self {
|
||||
ModuleBlockElements::Block(block) => {
|
||||
block.generate_register_interface(parent_name, parent_path, files)
|
||||
}
|
||||
ModuleBlockElements::Register(register) => {
|
||||
register.generate_register_interface(parent_name, parent_path, files)
|
||||
}
|
||||
ModuleBlockElements::Memory(_memory) => todo!(),
|
||||
ModuleBlockElements::Fifo(_fifo) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeGen for Block {
|
||||
fn generate_register_interface(
|
||||
self,
|
||||
parent_name: Option<proc_macro2::Ident>,
|
||||
parent_path: Option<path::PathBuf>,
|
||||
mut files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
|
||||
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError> {
|
||||
if self.multiple.is_some() {
|
||||
// Plan: expand automatically, or same as register?
|
||||
return Err(CodeGenError::UnsupportedStructure {
|
||||
name: "multiple in block",
|
||||
});
|
||||
}
|
||||
let parent_name = parent_name.ok_or(CodeGenError::ParentMissing { module: "Block" })?;
|
||||
let parent_path = parent_path.ok_or(CodeGenError::ParentMissing { module: "Block" })?;
|
||||
|
||||
let snake_case_name = util::parse_to_ident(&self.name.to_snake_case())?;
|
||||
let upper_camel_name = util::parse_to_ident(&self.name.to_upper_camel_case())?;
|
||||
let addr = util::parse_to_literal(&format!("0x{:x}", self.addr))?;
|
||||
let desc = self.desc.unwrap_or("".to_string());
|
||||
|
||||
let accessors_methods = self.elements.iter().map(|e| {
|
||||
let child_name = e.get_name();
|
||||
let snake_case_name = util::parse_to_ident(&child_name.to_snake_case())?;
|
||||
match e {
|
||||
ModuleBlockElements::Block(_) => {
|
||||
let child_upper_camel_name = util::parse_to_ident(&child_name.to_upper_camel_case())?;
|
||||
Ok(quote! {
|
||||
pub fn #snake_case_name(&self) -> #snake_case_name::#child_upper_camel_name {
|
||||
#snake_case_name::#child_upper_camel_name::new(self.mem_ptr)
|
||||
}
|
||||
})
|
||||
},
|
||||
ModuleBlockElements::Register(register) => {
|
||||
let child_upper_camel_name = util::parse_to_ident(&format!("Reg{}", child_name.to_upper_camel_case()))?;
|
||||
match ®ister.multiple {
|
||||
None => {
|
||||
Ok(quote! {
|
||||
pub fn #snake_case_name(&self) -> #snake_case_name::#child_upper_camel_name {
|
||||
#snake_case_name::#child_upper_camel_name::new(self.mem_ptr)
|
||||
}
|
||||
})
|
||||
},
|
||||
Some(multiple_param) => {
|
||||
let num_multiple = multiple_param.multiple as usize;
|
||||
let elements = (0..num_multiple).map(|i| {
|
||||
let offset = (multiple_param.offset as usize) * i;
|
||||
quote! {
|
||||
#snake_case_name::#child_upper_camel_name::new(unsafe { self.mem_ptr.add(#offset) } )
|
||||
}
|
||||
});
|
||||
Ok(quote! {
|
||||
pub fn #snake_case_name(&self) -> [#snake_case_name::#child_upper_camel_name; #num_multiple] {
|
||||
[ #(#elements),* ]
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
ModuleBlockElements::Memory(_memory) => todo!(),
|
||||
ModuleBlockElements::Fifo(_fifo) => todo!(),
|
||||
}
|
||||
}).collect::<Result<Vec<proc_macro2::TokenStream>, CodeGenError>>()?;
|
||||
|
||||
let parent_struct = if parent_name == util::parse_to_ident("RegisterInterface").unwrap() {
|
||||
quote! {#parent_name}
|
||||
} else {
|
||||
quote! {#parent_name<'a>}
|
||||
};
|
||||
|
||||
let child_mods = self
|
||||
.elements
|
||||
.iter()
|
||||
.map(|e| {
|
||||
util::parse_to_ident(&e.get_name().to_snake_case()).map(|child_name| {
|
||||
quote! {
|
||||
pub mod #child_name;
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let out = quote! {
|
||||
#![doc = #desc]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::#parent_name;
|
||||
|
||||
#(#child_mods)*
|
||||
|
||||
const OFFSET: usize = #addr;
|
||||
|
||||
pub struct #upper_camel_name<'a> {
|
||||
mem_ptr: *mut u32,
|
||||
_marker: PhantomData<&'a mut #parent_struct>,
|
||||
}
|
||||
|
||||
impl #upper_camel_name<'_> {
|
||||
pub(crate) fn new(parent_ptr: *mut u32) -> Self {
|
||||
#upper_camel_name {
|
||||
mem_ptr: unsafe { parent_ptr.add(OFFSET) },
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#(#accessors_methods)*
|
||||
}
|
||||
};
|
||||
let (out_path, next_parent_path) = mod_file_path(parent_path, &snake_case_name);
|
||||
log::info!("{:?}", out_path);
|
||||
if let Some(old_out) = files.insert(out_path.clone(), out.clone()) {
|
||||
log::error!("path {}", out_path.display());
|
||||
log::error!("old {}", old_out.to_string());
|
||||
log::error!("new {}", out.to_string());
|
||||
return Err(CodeGenError::FilePathError(snake_case_name.to_string()));
|
||||
};
|
||||
|
||||
let files = self.elements.into_iter().try_fold(files, |files, e| {
|
||||
e.generate_register_interface(
|
||||
Some(upper_camel_name.clone()),
|
||||
Some(next_parent_path.clone()),
|
||||
files,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(files)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get filepath to write the `mod_snake_case_name` and next parent_path.
|
||||
fn mod_file_path(
|
||||
parent_path: path::PathBuf,
|
||||
mod_snake_case_name: &proc_macro2::Ident,
|
||||
) -> (path::PathBuf, path::PathBuf) {
|
||||
(
|
||||
parent_path.join(format!("{}.rs", mod_snake_case_name)),
|
||||
parent_path.join(mod_snake_case_name.to_string()),
|
||||
)
|
||||
}
|
||||
|
||||
mod codegen_register;
|
||||
mod codegen_registerspec_impl;
|
||||
|
||||
/// Internally calls functions in [`codegen_register`]
|
||||
impl CodeGen for Register {
|
||||
fn generate_register_interface(
|
||||
self,
|
||||
parent_name: Option<proc_macro2::Ident>,
|
||||
parent_path: Option<path::PathBuf>,
|
||||
mut files: HashMap<path::PathBuf, proc_macro2::TokenStream>,
|
||||
) -> Result<HashMap<path::PathBuf, proc_macro2::TokenStream>, CodeGenError> {
|
||||
let parent_name = parent_name.ok_or(CodeGenError::ParentMissing { module: "Block" })?;
|
||||
let parent_path = parent_path.ok_or(CodeGenError::ParentMissing { module: "Block" })?;
|
||||
|
||||
let snake_case_name = util::parse_to_ident(&self.name.to_snake_case())?;
|
||||
let upper_camel_name = util::parse_to_ident(&self.name.to_upper_camel_case())?;
|
||||
let reg_name = util::parse_to_ident(&format!("Reg{upper_camel_name}"))?;
|
||||
let addr = util::parse_to_literal(&format!("0x{:x}", self.addr))?;
|
||||
|
||||
let (code_t_def, type_t, type_ux): (
|
||||
proc_macro2::TokenStream,
|
||||
proc_macro2::Ident,
|
||||
proc_macro2::Ident,
|
||||
) = codegen_register::reg_type_def(&self, &upper_camel_name)?;
|
||||
|
||||
let code_reg_def: proc_macro2::TokenStream =
|
||||
codegen_registerspec_impl::gen_registerspec_impl(
|
||||
reg_name.clone(),
|
||||
self.modf,
|
||||
type_t,
|
||||
type_ux,
|
||||
);
|
||||
|
||||
let desc = self.desc.unwrap_or("".to_string());
|
||||
|
||||
let out = quote! {
|
||||
#![doc = #desc]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::register_spec::{DataConversionError, Modifiable, Readable, RegisterSpec, Writable};
|
||||
|
||||
const OFFSET: usize = #addr;
|
||||
|
||||
pub struct #reg_name<'a> {
|
||||
mem_ptr: *mut u32,
|
||||
_marker: PhantomData<&'a mut super::#parent_name<'a>>,
|
||||
}
|
||||
|
||||
impl #reg_name<'_> {
|
||||
pub(crate) fn new(parent_ptr: *mut u32) -> Self {
|
||||
#reg_name {
|
||||
mem_ptr: unsafe { parent_ptr.add(OFFSET) },
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#code_reg_def
|
||||
|
||||
#code_t_def
|
||||
};
|
||||
|
||||
let (out_path, _next_parent_path) = mod_file_path(parent_path, &snake_case_name);
|
||||
log::info!("{:?}", out_path);
|
||||
if files.insert(out_path, out).is_some() {
|
||||
return Err(CodeGenError::FilePathError(snake_case_name.to_string()));
|
||||
}
|
||||
Ok(files)
|
||||
}
|
||||
}
|
661
src/generator/codegen_register.rs
Normal file
661
src/generator/codegen_register.rs
Normal file
|
@ -0,0 +1,661 @@
|
|||
//! Generator for [`Register`].
|
||||
//! The entry point is [`reg_type_def`].
|
||||
|
||||
use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
|
||||
use itertools::Itertools;
|
||||
use proc_macro2::{Ident, Literal, TokenStream};
|
||||
use quote::quote;
|
||||
|
||||
use crate::types::{DataType, Field, MultipleParams, Register, Value};
|
||||
|
||||
use super::{
|
||||
util::{self, RustUxTypes},
|
||||
CodeGenError,
|
||||
};
|
||||
|
||||
/// Generate underlying `T` and its implementation for `RegisterSpec`.
|
||||
///
|
||||
/// # Returns
|
||||
/// tuple of
|
||||
/// - code of `T` definition and its impls
|
||||
/// - [`Ident`] of `T`
|
||||
/// - [`Ident`] of `Ux`
|
||||
///
|
||||
/// # Cases
|
||||
/// - `T` == `Ux` => [`reg_type_def_simple`]
|
||||
/// - `T` is masked `Ux` => [`reg_type_def_masked`]
|
||||
/// - `T` has 1+ fields => [`reg_type_def_with_field`]
|
||||
pub(super) fn reg_type_def(
|
||||
reg: &Register,
|
||||
upper_camel_name: &Ident,
|
||||
) -> Result<(TokenStream, Ident, Ident), CodeGenError> {
|
||||
Ok(match reg.elements.is_empty() {
|
||||
true => match reg.mask {
|
||||
Some(mask) => reg_type_def_masked(®.r#type, mask, upper_camel_name),
|
||||
None => reg_type_def_simple(®.r#type),
|
||||
},
|
||||
false => reg_type_def_with_field(®.r#type, ®.elements, upper_camel_name)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Where `T` == `Ux`.
|
||||
///
|
||||
/// No `T` def nor impl are required.
|
||||
///
|
||||
/// # Returns
|
||||
/// same as [`reg_type_def`]
|
||||
fn reg_type_def_simple(basetype: &DataType) -> (TokenStream, Ident, Ident) {
|
||||
let type_t_ux = {
|
||||
let x: RustUxTypes = basetype.into();
|
||||
x.to_rust_type_token()
|
||||
};
|
||||
|
||||
let out = quote! {};
|
||||
(out, type_t_ux.clone(), type_t_ux)
|
||||
}
|
||||
|
||||
/// Where `T` is masked `Ux`.
|
||||
///
|
||||
/// `T` is a one-length "tuple struct" `T(pub Ux)`.
|
||||
/// `impl TryFrom<Ux> for T` and `impl From<T> for Ux` are included,
|
||||
/// but without `impl T`.
|
||||
///
|
||||
/// # Returns
|
||||
/// same as [`reg_type_def`]
|
||||
fn reg_type_def_masked(
|
||||
basetype: &DataType,
|
||||
mask: u32,
|
||||
upper_camel_name: &Ident,
|
||||
) -> (TokenStream, Ident, Ident) {
|
||||
let type_ux = {
|
||||
let x: RustUxTypes = basetype.into();
|
||||
x.to_rust_type_token()
|
||||
};
|
||||
|
||||
let out = quote! {
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct #upper_camel_name(pub #type_ux);
|
||||
impl TryFrom<#type_ux> for #upper_camel_name {
|
||||
type Error = DataConversionError<#type_ux, Self>;
|
||||
|
||||
fn try_from(value: #type_ux) -> Result<Self, Self::Error> {
|
||||
Ok(Self(value & #mask))
|
||||
}
|
||||
}
|
||||
impl From<#upper_camel_name> for #type_ux {
|
||||
fn from(value: #upper_camel_name) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
(out, upper_camel_name.clone(), type_ux)
|
||||
}
|
||||
|
||||
/// Where `T` has fields.
|
||||
///
|
||||
/// `T` is a "struct struct" which has a single field `inner: Ux`.
|
||||
/// Also, a bunch of "mask" constants and getter/setter are defined for each fields.
|
||||
///
|
||||
/// # Returns
|
||||
/// same as [`reg_type_def`]
|
||||
///
|
||||
/// # Field generation
|
||||
/// This function calls [`generate_field`] for each [`Field`] entries to generate field definitions.
|
||||
/// See the doc of `generate_field` for more detail
|
||||
fn reg_type_def_with_field(
|
||||
basetype: &DataType,
|
||||
fields: &[Field],
|
||||
upper_camel_name: &Ident,
|
||||
) -> Result<(TokenStream, Ident, Ident), CodeGenError> {
|
||||
let type_ux = {
|
||||
let x: RustUxTypes = basetype.into();
|
||||
x.to_rust_type_token()
|
||||
};
|
||||
|
||||
let (code_masks, code_getters, code_setters): (
|
||||
Vec<TokenStream>,
|
||||
Vec<TokenStream>,
|
||||
Vec<TokenStream>,
|
||||
) = fields
|
||||
.iter()
|
||||
.map(|field| generate_field(field, basetype))
|
||||
.process_results(|iter| iter.multiunzip())?;
|
||||
|
||||
let out = quote! {
|
||||
#(#code_masks)*
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct #upper_camel_name {
|
||||
inner: #type_ux,
|
||||
}
|
||||
|
||||
impl #upper_camel_name {
|
||||
#(#code_getters)*
|
||||
|
||||
#(#code_setters)*
|
||||
}
|
||||
impl TryFrom<#type_ux> for #upper_camel_name {
|
||||
type Error = DataConversionError<#type_ux, Self>;
|
||||
|
||||
fn try_from(value: #type_ux) -> Result<Self, Self::Error> {
|
||||
Ok(Self { inner: value })
|
||||
}
|
||||
}
|
||||
impl From<#upper_camel_name> for u32 {
|
||||
fn from(value: #upper_camel_name) -> Self {
|
||||
value.inner
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok((out, upper_camel_name.clone(), type_ux))
|
||||
}
|
||||
|
||||
enum FieldType<'a> {
|
||||
RustType(RustUxTypes),
|
||||
CustomValue(&'a [Value]),
|
||||
}
|
||||
|
||||
/// Generate code for each field, which consists of these three.
|
||||
/// 1. mask definition
|
||||
/// 2. getter method
|
||||
/// 3. setter method
|
||||
///
|
||||
/// # Cases
|
||||
/// This function is separated into two cases.
|
||||
/// See each function docs for more detail.
|
||||
///
|
||||
/// 1. field is not "multiple" => [`generate_single_field`]
|
||||
/// 2. field is "multiple" => [`generate_multiple_field`]
|
||||
fn generate_field(
|
||||
field: &Field,
|
||||
basetype: &DataType,
|
||||
) -> Result<(TokenStream, TokenStream, TokenStream), CodeGenError> {
|
||||
let mask_name = util::parse_to_ident(&format!("{}_MASK", field.name.to_shouty_snake_case()))?;
|
||||
let base_type = util::RustUxTypes::from(basetype).to_rust_type_token();
|
||||
|
||||
let snake_case_name = util::parse_to_ident(&field.name.to_snake_case())?;
|
||||
let field_type = match field.elements.is_empty() {
|
||||
true => FieldType::RustType(util::RustUxTypes::from_mask(field.mask)),
|
||||
false => FieldType::CustomValue(&field.elements),
|
||||
};
|
||||
|
||||
let (code_mask, code_getter, code_setter) = match &field.multiple {
|
||||
Some(multiple_params) => generate_multiple_field(
|
||||
mask_name,
|
||||
base_type,
|
||||
field.mask,
|
||||
field_type,
|
||||
snake_case_name,
|
||||
multiple_params,
|
||||
&field.desc.clone().unwrap_or("".to_string()),
|
||||
),
|
||||
None => generate_single_field(
|
||||
mask_name,
|
||||
base_type,
|
||||
field.mask,
|
||||
field_type,
|
||||
snake_case_name,
|
||||
&field.desc.clone().unwrap_or("".to_string()),
|
||||
),
|
||||
};
|
||||
|
||||
Ok((code_mask, code_getter, code_setter))
|
||||
}
|
||||
|
||||
/// Generate "single" field definition (mask, getter, setter).
|
||||
///
|
||||
/// # Cases
|
||||
/// This function is separated into three cases based on field types [`FieldType`].
|
||||
///
|
||||
/// - field is `bool` => [`generate_single_bool_field`]
|
||||
/// - field is `u8`/`u16`/`u32` => [`generate_single_ux_field`]
|
||||
/// - field is custom [`Value`] => [`generate_custom_values_const_enumdef`] & [`generate_single_custom_values_field`]
|
||||
///
|
||||
/// For the details of types of register/field, see [`DataType`].
|
||||
/// Note that in both cases, mask definitions are the same.
|
||||
fn generate_single_field(
|
||||
mask_name: Ident,
|
||||
base_type: Ident,
|
||||
mask_val: u32,
|
||||
field_type: FieldType,
|
||||
snake_case_name: Ident,
|
||||
desc: &str,
|
||||
) -> (TokenStream, TokenStream, TokenStream) {
|
||||
let mask_val = util::parse_to_literal(&format!("0x{:x}", mask_val)).unwrap();
|
||||
let code_mask = match field_type {
|
||||
FieldType::RustType(_) => {
|
||||
quote! {
|
||||
const #mask_name: #base_type = #mask_val;
|
||||
}
|
||||
}
|
||||
FieldType::CustomValue(values) => {
|
||||
let additional =
|
||||
generate_custom_values_const_enumdef(&base_type, &snake_case_name, values);
|
||||
quote! {
|
||||
const #mask_name: #base_type = #mask_val;
|
||||
#additional
|
||||
}
|
||||
}
|
||||
};
|
||||
let (code_getter, code_setter) = match field_type {
|
||||
FieldType::RustType(field_type) => match field_type {
|
||||
RustUxTypes::Bool => generate_single_bool_field(mask_name, snake_case_name, desc),
|
||||
RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => {
|
||||
generate_single_ux_field(mask_name, base_type, snake_case_name, field_type, desc)
|
||||
}
|
||||
},
|
||||
FieldType::CustomValue(values) => {
|
||||
generate_single_custom_values_field(mask_name, snake_case_name, values, desc)
|
||||
}
|
||||
};
|
||||
|
||||
(code_mask, code_getter, code_setter)
|
||||
}
|
||||
|
||||
/// Generate bool "single" field definition (getter, setter).
|
||||
fn generate_single_bool_field(
|
||||
mask_name: Ident,
|
||||
snake_case_name: Ident,
|
||||
desc: &str,
|
||||
) -> (TokenStream, TokenStream) {
|
||||
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
||||
let getter_doc = format!("Getter. {}", desc);
|
||||
let code_getter = quote! {
|
||||
#[doc = #getter_doc]
|
||||
pub fn #snake_case_name(&self) -> bool {
|
||||
(self.inner & #mask_name) == #mask_name
|
||||
}
|
||||
};
|
||||
let setter_doc = format!("Setter. {}", desc);
|
||||
let code_setter = quote! {
|
||||
#[doc = #setter_doc]
|
||||
pub fn #setter_name(self, val: bool) -> Self {
|
||||
let mut inner = self.inner;
|
||||
if val {
|
||||
inner |= #mask_name
|
||||
} else {
|
||||
inner &= !#mask_name
|
||||
};
|
||||
Self { inner }
|
||||
}
|
||||
};
|
||||
(code_getter, code_setter)
|
||||
}
|
||||
|
||||
/// Generate u8/u16/u32 "single" field definition (getter, setter).
|
||||
fn generate_single_ux_field(
|
||||
mask_name: Ident,
|
||||
base_type: Ident,
|
||||
snake_case_name: Ident,
|
||||
field_type: RustUxTypes,
|
||||
desc: &str,
|
||||
) -> (TokenStream, TokenStream) {
|
||||
let field_type = field_type.to_rust_type_token();
|
||||
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
||||
|
||||
let getter_doc = format!("Getter. {}", desc);
|
||||
let code_getter = quote! {
|
||||
#[doc = #getter_doc]
|
||||
pub fn #snake_case_name(&self) -> #field_type {
|
||||
const RIGHT_SHIFT: #base_type = #mask_name.trailing_zeros();
|
||||
((self.inner & #mask_name) >> RIGHT_SHIFT)
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
};
|
||||
let setter_doc = format!("Setter. {}", desc);
|
||||
let code_setter = quote! {
|
||||
#[doc = #setter_doc]
|
||||
pub fn #setter_name(&self, val: #field_type) -> Self {
|
||||
let update: #base_type = (val as #base_type) & #mask_name;
|
||||
let mut inner = self.inner;
|
||||
inner &= !#mask_name;
|
||||
inner |= update;
|
||||
Self { inner }
|
||||
}
|
||||
};
|
||||
(code_getter, code_setter)
|
||||
}
|
||||
|
||||
fn custom_value_const_name(field_name: &Ident, value_name: &str) -> Ident {
|
||||
util::parse_to_ident(&format!(
|
||||
"{}_{}",
|
||||
field_name.to_string().to_shouty_snake_case(),
|
||||
value_name.to_shouty_snake_case()
|
||||
))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Generate const var and value enum definition.
|
||||
///
|
||||
/// Both for single & multiple.
|
||||
fn generate_custom_values_const_enumdef(
|
||||
base_type: &Ident,
|
||||
field_name: &Ident,
|
||||
values: &[Value],
|
||||
) -> TokenStream {
|
||||
let consts = values.iter().map(|value| {
|
||||
let const_name = custom_value_const_name(field_name, &value.name);
|
||||
let val = value.data;
|
||||
quote! {
|
||||
const #const_name: #base_type = #val;
|
||||
}
|
||||
});
|
||||
let variants = values.iter().map(|value| {
|
||||
let desc = value.desc.clone().unwrap_or("".to_string());
|
||||
let variant_name = util::parse_to_ident(&value.name.to_upper_camel_case()).unwrap();
|
||||
quote! {
|
||||
#[doc = #desc]
|
||||
#variant_name
|
||||
}
|
||||
});
|
||||
let value_enum_name =
|
||||
util::parse_to_ident(&field_name.to_string().to_upper_camel_case()).unwrap();
|
||||
|
||||
quote! {
|
||||
#(#consts)*
|
||||
pub enum #value_enum_name {
|
||||
#(#variants),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate custom [`Value`] "single" field definition (getter, setter).
|
||||
fn generate_single_custom_values_field(
|
||||
mask_name: Ident,
|
||||
snake_case_name: Ident,
|
||||
values: &[Value],
|
||||
desc: &str,
|
||||
) -> (TokenStream, TokenStream) {
|
||||
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
||||
let value_enum_name =
|
||||
util::parse_to_ident(&snake_case_name.to_string().to_upper_camel_case()).unwrap();
|
||||
let (getter_match_arms, setter_match_arms): (Vec<_>, Vec<_>) = values
|
||||
.iter()
|
||||
.map(|value| {
|
||||
let const_name = custom_value_const_name(&snake_case_name, &value.name);
|
||||
let variant_name = util::parse_to_ident(&value.name.to_upper_camel_case()).unwrap();
|
||||
(
|
||||
quote! {
|
||||
#const_name => #value_enum_name::#variant_name
|
||||
},
|
||||
quote! {
|
||||
#value_enum_name::#variant_name => #const_name
|
||||
},
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
let getter_doc = format!("Getter. {}", desc);
|
||||
let code_getter = quote! {
|
||||
#[doc = #getter_doc]
|
||||
pub fn #snake_case_name(&self) -> #value_enum_name {
|
||||
match (self.inner & #mask_name)
|
||||
>> #mask_name.trailing_zeros()
|
||||
{
|
||||
#(#getter_match_arms),*,
|
||||
_ => panic!("must not reachable"),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let setter_doc = format!("Setter. {}", desc);
|
||||
let code_setter = quote! {
|
||||
#[doc = #setter_doc]
|
||||
pub fn #setter_name(self, val: #value_enum_name) -> Self {
|
||||
let val = match val {
|
||||
#(#setter_match_arms),*
|
||||
};
|
||||
let mut inner = self.inner;
|
||||
inner &= !#mask_name;
|
||||
inner |= val;
|
||||
|
||||
Self { inner }
|
||||
}
|
||||
};
|
||||
(code_getter, code_setter)
|
||||
}
|
||||
|
||||
/// Generate "multiple" field definition (mask, getter, setter).
|
||||
///
|
||||
/// # Cases
|
||||
/// As "single" field cases, this function is separated into two cases;
|
||||
///
|
||||
/// - fields are `bool` => [`generate_multiple_bool_field`]
|
||||
/// - fields are `u8`/`u16`/`u32` => [`generate_multiple_ux_field`]
|
||||
/// - fields are custom [`Value`] => [`generate_custom_values_const_enumdef`] & [`generate_multiple_custom_values_field`]
|
||||
fn generate_multiple_field(
|
||||
mask_name: Ident,
|
||||
base_type: Ident,
|
||||
single_mask_val: u32,
|
||||
single_field_type: FieldType,
|
||||
snake_case_name: Ident,
|
||||
multiple_params: &MultipleParams,
|
||||
desc: &str,
|
||||
) -> (TokenStream, TokenStream, TokenStream) {
|
||||
let num_multiple = multiple_params.multiple;
|
||||
let id_num_multiple = util::parse_to_literal(&num_multiple.to_string()).unwrap();
|
||||
let masks: Vec<_> = (0..multiple_params.multiple)
|
||||
.map(|x| x * multiple_params.offset)
|
||||
.map(|offset| single_mask_val << offset)
|
||||
.map(|mask| util::parse_to_literal(&format!("0x{mask:x}")).unwrap())
|
||||
.collect();
|
||||
debug_assert_eq!(masks.len(), num_multiple.try_into().unwrap());
|
||||
let value_const_enumdefs = match single_field_type {
|
||||
FieldType::RustType(_) => quote! {},
|
||||
FieldType::CustomValue(values) => {
|
||||
generate_custom_values_const_enumdef(&base_type, &snake_case_name, values)
|
||||
}
|
||||
};
|
||||
let code_mask = quote! {
|
||||
const #mask_name: [#base_type; #id_num_multiple] = [#(#masks),*];
|
||||
#value_const_enumdefs
|
||||
};
|
||||
|
||||
let (code_getter, code_setter) = match single_field_type {
|
||||
FieldType::RustType(single_field_type) => match single_field_type {
|
||||
RustUxTypes::Bool => generate_multiple_bool_field(
|
||||
mask_name,
|
||||
base_type,
|
||||
snake_case_name,
|
||||
masks.clone(),
|
||||
desc,
|
||||
),
|
||||
RustUxTypes::U8 | RustUxTypes::U16 | RustUxTypes::U32 => generate_multiple_ux_field(
|
||||
mask_name,
|
||||
base_type,
|
||||
snake_case_name,
|
||||
single_field_type,
|
||||
masks,
|
||||
desc,
|
||||
),
|
||||
},
|
||||
FieldType::CustomValue(values) => generate_multiple_custom_values_field(
|
||||
mask_name,
|
||||
base_type,
|
||||
snake_case_name,
|
||||
masks,
|
||||
values,
|
||||
desc,
|
||||
),
|
||||
};
|
||||
|
||||
(code_mask, code_getter, code_setter)
|
||||
}
|
||||
|
||||
/// Generate bool "multiple" field definition (getter, setter).
|
||||
fn generate_multiple_bool_field(
|
||||
mask_name: Ident,
|
||||
base_type: Ident,
|
||||
snake_case_name: Ident,
|
||||
masks: Vec<Literal>,
|
||||
desc: &str,
|
||||
) -> (TokenStream, TokenStream) {
|
||||
let num_multiple = masks.len();
|
||||
let elem_getter = masks.iter().enumerate().map(|(i, _mask)| {
|
||||
quote! {
|
||||
(self.inner & #mask_name[#i]) == #mask_name[#i]
|
||||
}
|
||||
});
|
||||
let getter_doc = format!("Getter. {}", desc);
|
||||
let code_getter = quote! {
|
||||
#[doc = #getter_doc]
|
||||
pub fn #snake_case_name(&self) -> [bool; #num_multiple] {
|
||||
[
|
||||
#(#elem_getter),*
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
||||
let setter_doc = format!("Setter. {}", desc);
|
||||
let code_setter = quote! {
|
||||
#[doc = #setter_doc]
|
||||
pub fn #setter_name(self, val: [bool; #num_multiple]) -> Self {
|
||||
let mask: #base_type = #mask_name.iter().sum();
|
||||
let update: #base_type = #mask_name
|
||||
.iter()
|
||||
.zip(val)
|
||||
.filter_map(|(mask, val)| val.then_some(mask))
|
||||
.sum();
|
||||
let mut inner = self.inner;
|
||||
inner &= !mask;
|
||||
inner |= update;
|
||||
Self { inner }
|
||||
}
|
||||
};
|
||||
|
||||
(code_getter, code_setter)
|
||||
}
|
||||
|
||||
/// Generate u8/u16/u32 "multiple" field definition (getter, setter).
|
||||
fn generate_multiple_ux_field(
|
||||
mask_name: Ident,
|
||||
base_type: Ident,
|
||||
snake_case_name: Ident,
|
||||
single_field_type: RustUxTypes,
|
||||
masks: Vec<Literal>,
|
||||
desc: &str,
|
||||
) -> (TokenStream, TokenStream) {
|
||||
let field_type = single_field_type.to_rust_type_token();
|
||||
let num_multiple = masks.len();
|
||||
let elem_getter = masks.iter().enumerate().map(|(i, _mask)| {
|
||||
quote! {
|
||||
((self.inner & #mask_name[#i])
|
||||
>> (#mask_name[#i].trailing_zeros()))
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
});
|
||||
let getter_doc = format!("Getter. {}", desc);
|
||||
let code_getter = quote! {
|
||||
#[doc = #getter_doc]
|
||||
pub fn #snake_case_name(&self) -> [#field_type; #num_multiple] {
|
||||
[
|
||||
#(#elem_getter),*
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
||||
let setter_doc = format!("Setter. {}", desc);
|
||||
let code_setter = quote! {
|
||||
#[doc = #setter_doc]
|
||||
pub fn #setter_name(&self, val: [#field_type; #num_multiple]) -> Self {
|
||||
let mask: #base_type = #mask_name.iter().sum();
|
||||
let update: #base_type = #mask_name
|
||||
.iter()
|
||||
.zip(val)
|
||||
.map(|(mask, val)| (#base_type::from(val)) << (mask.trailing_zeros()))
|
||||
.sum();
|
||||
let mut inner = self.inner;
|
||||
inner &= !mask;
|
||||
inner |= update;
|
||||
Self { inner }
|
||||
}
|
||||
};
|
||||
|
||||
(code_getter, code_setter)
|
||||
}
|
||||
|
||||
/// Generate custom [`Value`] "single" field definition (getter, setter).
|
||||
fn generate_multiple_custom_values_field(
|
||||
mask_name: Ident,
|
||||
base_type: Ident,
|
||||
snake_case_name: Ident,
|
||||
masks: Vec<Literal>,
|
||||
values: &[Value],
|
||||
desc: &str,
|
||||
) -> (TokenStream, TokenStream) {
|
||||
let value_enum_name =
|
||||
util::parse_to_ident(&snake_case_name.to_string().to_upper_camel_case()).unwrap();
|
||||
let num_multiple = masks.len();
|
||||
let (getter_match_arms, setter_match_arms): (Vec<_>, Vec<_>) = values
|
||||
.iter()
|
||||
.map(|value| {
|
||||
let const_name = custom_value_const_name(&snake_case_name, &value.name);
|
||||
let variant_name = util::parse_to_ident(&value.name.to_upper_camel_case()).unwrap();
|
||||
(
|
||||
quote! {
|
||||
#const_name => #value_enum_name::#variant_name
|
||||
},
|
||||
quote! {
|
||||
#value_enum_name::#variant_name => #const_name
|
||||
},
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
let elem_getter = masks.iter().enumerate().map(|(i, _mask)| {
|
||||
quote! {
|
||||
match ((self.inner & #mask_name[#i]))
|
||||
>> (#mask_name[#i].trailing_zeros())
|
||||
{
|
||||
#(#getter_match_arms),*,
|
||||
_ => panic!("must not reachable"),
|
||||
}
|
||||
}
|
||||
});
|
||||
let getter_doc = format!("Getter. {}", desc);
|
||||
let code_getter = quote! {
|
||||
#[doc = #getter_doc]
|
||||
pub fn #snake_case_name(&self) -> [#value_enum_name; #num_multiple] {
|
||||
[
|
||||
#(#elem_getter),*
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let setter_name = util::parse_to_ident(&format!("set_{}", snake_case_name)).unwrap();
|
||||
let elem_setter = masks.iter().enumerate().map(|(i, _mask)| {
|
||||
quote! {
|
||||
match val[#i] {
|
||||
#(#setter_match_arms),*
|
||||
}
|
||||
}
|
||||
});
|
||||
let setter_doc = format!("Setter. {}", desc);
|
||||
let code_setter = quote! {
|
||||
#[doc = #setter_doc]
|
||||
pub fn #setter_name(&self, val: [#value_enum_name; #num_multiple]) -> Self {
|
||||
let val: [#base_type; #num_multiple] = [
|
||||
#(#elem_setter),*
|
||||
];
|
||||
let mask: #base_type = #mask_name.iter().sum();
|
||||
let update: #base_type = #mask_name
|
||||
.iter()
|
||||
.zip(val)
|
||||
.map(|(mask, val)| (#base_type::from(val)) << (mask.trailing_zeros()))
|
||||
.sum();
|
||||
let mut inner = self.inner;
|
||||
inner &= !mask;
|
||||
inner |= update;
|
||||
Self { inner }
|
||||
}
|
||||
};
|
||||
|
||||
(code_getter, code_setter)
|
||||
}
|
36
src/generator/codegen_registerspec_impl.rs
Normal file
36
src/generator/codegen_registerspec_impl.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::quote;
|
||||
|
||||
use crate::types::RwSpecifier;
|
||||
|
||||
pub(super) fn gen_registerspec_impl(
|
||||
reg_name: Ident,
|
||||
modf: RwSpecifier,
|
||||
type_t: Ident,
|
||||
type_ux: Ident,
|
||||
) -> TokenStream {
|
||||
let impl_rw = match modf {
|
||||
RwSpecifier::R => quote! {
|
||||
impl Readable for #reg_name<'_> {}
|
||||
},
|
||||
RwSpecifier::W => quote! {
|
||||
impl Writable for #reg_name<'_> {}
|
||||
},
|
||||
RwSpecifier::RW => quote! {
|
||||
impl Readable for #reg_name<'_> {}
|
||||
impl Writable for #reg_name<'_> {}
|
||||
impl Modifiable for #reg_name<'_> {}
|
||||
},
|
||||
};
|
||||
quote! {
|
||||
impl RegisterSpec for #reg_name<'_> {
|
||||
type Ux = #type_ux;
|
||||
type T = #type_t;
|
||||
|
||||
fn as_ptr(&self) -> *mut Self::Ux {
|
||||
self.mem_ptr
|
||||
}
|
||||
}
|
||||
#impl_rw
|
||||
}
|
||||
}
|
27
src/io.rs
Normal file
27
src/io.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
//! File IO for generated codes.
|
||||
|
||||
use std::{collections::HashMap, fs, io, path};
|
||||
|
||||
/// Write formatted codes generated with [`Module::generate_code`](crate::types::Module::generate_code).
|
||||
pub fn write_to_files(
|
||||
files: HashMap<path::PathBuf, syn::File>,
|
||||
out_path: &path::Path,
|
||||
) -> io::Result<()> {
|
||||
if !out_path.is_dir() {
|
||||
return Err(io::Error::from(io::ErrorKind::NotADirectory));
|
||||
}
|
||||
if fs::read_dir(out_path)?.next().is_some() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::AlreadyExists,
|
||||
format!("out path `{}` is not empty", out_path.display()),
|
||||
));
|
||||
}
|
||||
for (file_path, code) in files {
|
||||
fs::DirBuilder::new()
|
||||
.recursive(true)
|
||||
.create(out_path.join(&file_path).parent().unwrap())?;
|
||||
|
||||
fs::write(out_path.join(&file_path), prettyplease::unparse(&code))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
54
src/lib.rs
54
src/lib.rs
|
@ -1,5 +1,53 @@
|
|||
//! Root document [`types::Module::from_xml_dom`]
|
||||
#![doc = include_str!("../README.md")]
|
||||
//!
|
||||
//! ----
|
||||
//!
|
||||
//! _documents in the library_
|
||||
//!
|
||||
//! Generate register interface software from register map in XML.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! Here's a typical usage:
|
||||
//! ```no_run
|
||||
//! use endcap_sl_software_ri_generator::types;
|
||||
//!
|
||||
//! let xmlfile = std::fs::read_to_string("./csr.xml")?;
|
||||
//! let doc = roxmltree::Document::parse_with_options(
|
||||
//! &xmlfile,
|
||||
//! roxmltree::ParsingOptions {
|
||||
//! allow_dtd: true,
|
||||
//! nodes_limit: u32::MAX,
|
||||
//! },
|
||||
//! )?;
|
||||
//!
|
||||
//! let register_map = types::Module::from_xml_dom(doc.root_element())?;
|
||||
//!
|
||||
//! let files = register_map.generate_code()?;
|
||||
//! endcap_sl_software_ri_generator::write_to_files(files, &std::path::PathBuf::from("out"))?;
|
||||
//!
|
||||
//! # Ok::<(), anyhow::Error>(())
|
||||
//! ```
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! 1. Convert [`roxmltree::Document`] to register map represented with types defined in
|
||||
//! [`types`], filling missing parameters. See [`converter`].
|
||||
//! 2. Generate [`proc_macro2::TokenStream`] from register map produced in the previous step. See
|
||||
//! [`generator`].
|
||||
//!
|
||||
//! # modules
|
||||
//! - [`types`]: type definitions of internal register map representation
|
||||
//! - [`converter`]: DOM to internal representation
|
||||
//! - [`generator`]: internal representation to rust code
|
||||
//! - [`io`]: formatting and printing
|
||||
|
||||
pub mod types;
|
||||
pub mod parser;
|
||||
pub mod converter;
|
||||
pub mod generator;
|
||||
pub mod io;
|
||||
pub mod meta;
|
||||
mod parser;
|
||||
mod type_traits;
|
||||
pub mod types;
|
||||
|
||||
pub use io::write_to_files;
|
||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -1,13 +1,27 @@
|
|||
use std::fs;
|
||||
use std::{fs, path};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use endcap_sl_software_ri_generator::types;
|
||||
use itertools::Itertools;
|
||||
|
||||
/// Generate register interface from register map xml.
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about)]
|
||||
struct Args {
|
||||
/// Input XML register map.
|
||||
xml: path::PathBuf,
|
||||
/// Output directory.
|
||||
out: path::PathBuf,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
log::debug!("logger enabled");
|
||||
let args = Args::parse();
|
||||
log::debug!("args: {:?}", args);
|
||||
|
||||
println!("Hello, world!");
|
||||
let xmlfile = fs::read_to_string("./csr.xml")?;
|
||||
let xmlfile = fs::read_to_string(args.xml)?;
|
||||
let doc = roxmltree::Document::parse_with_options(
|
||||
&xmlfile,
|
||||
roxmltree::ParsingOptions {
|
||||
|
@ -15,11 +29,27 @@ fn main() -> Result<()> {
|
|||
nodes_limit: u32::MAX,
|
||||
},
|
||||
)?;
|
||||
// println!("Parsed: {:#?}", doc);
|
||||
// println!("Root: {:?}", doc.root_element());
|
||||
log::debug!("Parsed: {:#?}", doc);
|
||||
|
||||
let register_map = types::Module::from_xml_dom(doc.root_element())?;
|
||||
println!("read: {:#?}", register_map);
|
||||
log::info!("read: {:?}", register_map);
|
||||
log::debug!("read: {:#?}", register_map);
|
||||
|
||||
let files = register_map.generate_code()?;
|
||||
if log::log_enabled!(log::Level::Debug) {
|
||||
for (path, code) in &files {
|
||||
log::debug!("path: {:?}", path);
|
||||
log::debug!("{}", prettyplease::unparse(code));
|
||||
log::trace!("{}", prettyplease::unparse(code));
|
||||
}
|
||||
}
|
||||
if log::log_enabled!(log::Level::Info) {
|
||||
for filepath in files.keys().sorted() {
|
||||
log::info!("{}", filepath.display());
|
||||
}
|
||||
}
|
||||
|
||||
endcap_sl_software_ri_generator::write_to_files(files, &args.out)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
13
src/meta.rs
Normal file
13
src/meta.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
//! Get build metadata at compile time.
|
||||
//!
|
||||
//! See build.rs for more detail.
|
||||
|
||||
pub(crate) const GENERATOR_BUILD_TIMESTAMP: &str =
|
||||
env!("VERGEN_BUILD_TIMESTAMP", "Failed to get build timestamp");
|
||||
pub(crate) const GENERATOR_GIT_DESCRIBE: &str =
|
||||
env!("VERGEN_GIT_DESCRIBE", "Failed to get git describe");
|
||||
pub(crate) const GENERATOR_GIT_COMMIT_TIMESTAMP: &str = env!(
|
||||
"VERGEN_GIT_COMMIT_TIMESTAMP",
|
||||
"Failed to get git commit timestamp"
|
||||
);
|
||||
pub(crate) const GENERATOR_GIT_SHA: &str = env!("VERGEN_GIT_SHA", "Failed to get git sha");
|
|
@ -1,4 +1,6 @@
|
|||
//! Module for converting XML custom token string to rust types.
|
||||
//!
|
||||
//! Unlike, [`crate::converter`], this only has string "parser".
|
||||
|
||||
use std::any;
|
||||
|
||||
|
|
30
src/type_traits.rs
Normal file
30
src/type_traits.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
//! Util traits to get info from types in [`crate::types`].
|
||||
|
||||
use crate::types::{Block, ModuleBlockElements, Register};
|
||||
|
||||
pub(crate) trait GetName {
|
||||
fn get_name(&self) -> String;
|
||||
}
|
||||
|
||||
impl GetName for ModuleBlockElements {
|
||||
fn get_name(&self) -> String {
|
||||
match self {
|
||||
ModuleBlockElements::Block(block) => block.get_name(),
|
||||
ModuleBlockElements::Register(register) => register.get_name(),
|
||||
ModuleBlockElements::Memory(_memory) => todo!(),
|
||||
ModuleBlockElements::Fifo(_fifo) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetName for Block {
|
||||
fn get_name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl GetName for Register {
|
||||
fn get_name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
//! Register type definition.
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Module {
|
||||
pub name: String,
|
||||
|
@ -8,6 +10,7 @@ pub struct Module {
|
|||
pub desc: Option<String>,
|
||||
pub elements_bitstring: Vec<BitString>,
|
||||
pub elements_other: Vec<ModuleBlockElements>,
|
||||
pub xmlhash: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -51,6 +54,8 @@ pub struct Register {
|
|||
pub name: String,
|
||||
/// Fill this with proper calc.
|
||||
pub addr: u32,
|
||||
/// Fill this with proper calc.
|
||||
pub r#type: DataType,
|
||||
pub mask: Option<u32>,
|
||||
/// Fill this with proper calc.
|
||||
pub modf: RwSpecifier,
|
||||
|
@ -109,7 +114,7 @@ pub struct Field {
|
|||
pub struct Value {
|
||||
pub name: String,
|
||||
pub data: u32,
|
||||
pub desc: Option<String>
|
||||
pub desc: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -122,7 +127,7 @@ pub enum AmodValues {
|
|||
USER2,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum DataType {
|
||||
D32,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue