mirror of
https://github.com/cccb/servicepoint.git
synced 2025-01-18 10:00:14 +01:00
Compare commits
11 commits
93657c9f85
...
b6fa1b161e
Author | SHA1 | Date | |
---|---|---|---|
b6fa1b161e | |||
7e1fb6cc99 | |||
04cda144ed | |||
2a6005fff9 | |||
2f2160f246 | |||
4f83aa3d5c | |||
dea176d0d9 | |||
8320ee2d80 | |||
efaa52faa1 | |||
38316169e9 | |||
24e0eaaf07 |
3
.github/workflows/rust.yml
vendored
3
.github/workflows/rust.yml
vendored
|
@ -17,6 +17,9 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: install lzma
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y liblzma-dev
|
||||||
|
|
||||||
- name: build default features
|
- name: build default features
|
||||||
run: cargo build --all --verbose
|
run: cargo build --all --verbose
|
||||||
- name: build default features -- examples
|
- name: build default features -- examples
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@ out
|
||||||
.direnv
|
.direnv
|
||||||
.envrc
|
.envrc
|
||||||
result
|
result
|
||||||
|
mutants.*
|
274
Cargo.lock
generated
274
Cargo.lock
generated
|
@ -44,7 +44,7 @@ version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -54,14 +54,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.93"
|
version = "1.0.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "askama"
|
name = "askama"
|
||||||
|
@ -108,7 +108,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -186,9 +186,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.6.0"
|
version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitvec"
|
name = "bitvec"
|
||||||
|
@ -219,15 +219,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.8.0"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da"
|
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bzip2"
|
name = "bzip2"
|
||||||
version = "0.4.4"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
|
checksum = "bafdbf26611df8c14810e268ddceda071c297570a5fb360ceddf617fe417ef58"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bzip2-sys",
|
"bzip2-sys",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -255,9 +255,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cargo-platform"
|
name = "cargo-platform"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc"
|
checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -273,7 +273,7 @@ dependencies = [
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -282,24 +282,24 @@ version = "0.27.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb"
|
checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 4.5.20",
|
"clap 4.5.26",
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"log",
|
"log",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"toml 0.8.19",
|
"toml 0.8.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.0"
|
version = "1.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
|
checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -331,23 +331,23 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.20"
|
version = "4.5.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive 4.5.18",
|
"clap_derive 4.5.24",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.20"
|
version = "4.5.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex 0.7.2",
|
"clap_lex 0.7.4",
|
||||||
"strsim 0.11.1",
|
"strsim 0.11.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -366,14 +366,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.18"
|
version = "4.5.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -387,9 +387,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.2"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
|
@ -399,9 +399,9 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.15"
|
version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6"
|
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -449,12 +449,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.9"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -465,20 +465,20 @@ checksum = "311a6d2f1f9d60bff73d2c78a0af97ed27f79672f15c238192a5bbb64db56d00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.2.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.34"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
|
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
|
@ -528,9 +528,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.1"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "goblin"
|
name = "goblin"
|
||||||
|
@ -551,9 +551,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.1"
|
version = "0.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
|
@ -578,9 +578,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
|
@ -605,12 +605,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.6.0"
|
version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.15.1",
|
"hashbrown 0.15.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -621,9 +621,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
|
@ -644,15 +644,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.162"
|
version = "0.2.169"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.4.14"
|
version = "0.4.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
|
@ -690,9 +690,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.0"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
|
checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler2",
|
"adler2",
|
||||||
]
|
]
|
||||||
|
@ -778,18 +778,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.89"
|
version = "1.0.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.37"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
@ -842,15 +842,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.40"
|
version = "0.38.43"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
|
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.7.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -876,43 +876,43 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.23"
|
version = "1.0.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.215"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
|
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.215"
|
version = "1.0.217"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
|
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.132"
|
version = "1.0.135"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -931,24 +931,24 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servicepoint"
|
name = "servicepoint"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitvec",
|
"bitvec",
|
||||||
"bzip2",
|
"bzip2",
|
||||||
"clap 4.5.20",
|
"clap 4.5.26",
|
||||||
"flate2",
|
"flate2",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rand",
|
"rand",
|
||||||
"rust-lzma",
|
"rust-lzma",
|
||||||
"thiserror",
|
"thiserror 2.0.11",
|
||||||
"tungstenite",
|
"tungstenite",
|
||||||
"zstd",
|
"zstd",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servicepoint_binding_c"
|
name = "servicepoint_binding_c"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cbindgen",
|
"cbindgen",
|
||||||
"servicepoint",
|
"servicepoint",
|
||||||
|
@ -956,10 +956,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servicepoint_binding_uniffi"
|
name = "servicepoint_binding_uniffi"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"servicepoint",
|
"servicepoint",
|
||||||
"thiserror",
|
"thiserror 2.0.11",
|
||||||
"uniffi",
|
"uniffi",
|
||||||
"uniffi-bindgen-cs",
|
"uniffi-bindgen-cs",
|
||||||
"uniffi-bindgen-go",
|
"uniffi-bindgen-go",
|
||||||
|
@ -1025,9 +1025,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.87"
|
version = "2.0.96"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1042,15 +1042,16 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.14.0"
|
version = "3.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
|
"getrandom",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1079,7 +1080,16 @@ version = "1.0.69"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 2.0.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1090,7 +1100,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1129,7 +1150,7 @@ version = "0.22.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.6.0",
|
"indexmap 2.7.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
|
@ -1138,9 +1159,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.24.0"
|
version = "0.26.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -1150,7 +1171,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"rand",
|
"rand",
|
||||||
"sha1",
|
"sha1",
|
||||||
"thiserror",
|
"thiserror 2.0.11",
|
||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1162,15 +1183,15 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.8.0"
|
version = "2.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
|
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.13"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-linebreak"
|
name = "unicode-linebreak"
|
||||||
|
@ -1218,8 +1239,8 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi-bindgen-go"
|
name = "uniffi-bindgen-go"
|
||||||
version = "0.2.1+v0.25.0"
|
version = "0.2.2+v0.25.0"
|
||||||
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240"
|
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"askama 0.12.1",
|
"askama 0.12.1",
|
||||||
|
@ -1234,15 +1255,15 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"toml 0.5.11",
|
"toml 0.5.11",
|
||||||
"uniffi_bindgen 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"uniffi_bindgen 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
"uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
"uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_bindgen"
|
name = "uniffi_bindgen"
|
||||||
version = "0.25.0"
|
version = "0.25.0"
|
||||||
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240"
|
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"askama 0.12.1",
|
"askama 0.12.1",
|
||||||
|
@ -1257,9 +1278,9 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
"toml 0.5.11",
|
"toml 0.5.11",
|
||||||
"uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
"uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
"uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"uniffi_udl 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1322,10 +1343,10 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_checksum_derive"
|
name = "uniffi_checksum_derive"
|
||||||
version = "0.25.0"
|
version = "0.25.0"
|
||||||
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240"
|
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1334,7 +1355,7 @@ version = "0.25.0"
|
||||||
source = "git+https://github.com/NordSecurity/uniffi-bindgen-cs?rev=f68639fbc720b50ebe561ba75c66c84dc456bdce#f68639fbc720b50ebe561ba75c66c84dc456bdce"
|
source = "git+https://github.com/NordSecurity/uniffi-bindgen-cs?rev=f68639fbc720b50ebe561ba75c66c84dc456bdce#f68639fbc720b50ebe561ba75c66c84dc456bdce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1344,7 +1365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c"
|
checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1376,7 +1397,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
"toml 0.5.11",
|
"toml 0.5.11",
|
||||||
"uniffi_build",
|
"uniffi_build",
|
||||||
"uniffi_meta 0.25.3",
|
"uniffi_meta 0.25.3",
|
||||||
|
@ -1385,12 +1406,12 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_meta"
|
name = "uniffi_meta"
|
||||||
version = "0.25.0"
|
version = "0.25.0"
|
||||||
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240"
|
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
"siphasher",
|
"siphasher",
|
||||||
"uniffi_checksum_derive 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"uniffi_checksum_derive 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1419,7 +1440,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_testing"
|
name = "uniffi_testing"
|
||||||
version = "0.25.0"
|
version = "0.25.0"
|
||||||
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240"
|
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"camino",
|
"camino",
|
||||||
|
@ -1456,12 +1477,12 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uniffi_udl"
|
name = "uniffi_udl"
|
||||||
version = "0.25.0"
|
version = "0.25.0"
|
||||||
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240"
|
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"uniffi_meta 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
"uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"uniffi_testing 0.25.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
"weedle2 4.0.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240)",
|
"weedle2 4.0.0 (git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1529,7 +1550,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "weedle2"
|
name = "weedle2"
|
||||||
version = "4.0.0"
|
version = "4.0.0"
|
||||||
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=a77dc0462dc18d53846c758155ab4e0a42e5b240#a77dc0462dc18d53846c758155ab4e0a42e5b240"
|
source = "git+https://github.com/NordSecurity/uniffi-bindgen-go.git?rev=ba23bab72f1a9bcc39ce81924d3d9265598e017c#ba23bab72f1a9bcc39ce81924d3d9265598e017c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nom",
|
"nom",
|
||||||
]
|
]
|
||||||
|
@ -1564,7 +1585,7 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1573,15 +1594,6 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.52.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
|
@ -1657,9 +1669,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.20"
|
version = "0.6.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -1691,7 +1703,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -8,10 +8,10 @@ members = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
missing-docs = "warn"
|
missing-docs = "warn"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
thiserror = "1.0.69"
|
thiserror = "2.0"
|
||||||
|
|
|
@ -16,12 +16,12 @@ crate-type = ["rlib"]
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
bitvec = "1.0"
|
bitvec = "1.0"
|
||||||
flate2 = { version = "1.0", optional = true }
|
flate2 = { version = "1.0", optional = true }
|
||||||
bzip2 = { version = "0.4", optional = true }
|
bzip2 = { version = "0.5", optional = true }
|
||||||
zstd = { version = "0.13", optional = true }
|
zstd = { version = "0.13", optional = true }
|
||||||
rust-lzma = { version = "0.6.0", optional = true }
|
rust-lzma = { version = "0.6", optional = true }
|
||||||
rand = { version = "0.8", optional = true }
|
rand = { version = "0.8", optional = true }
|
||||||
tungstenite = { version = "0.24.0", optional = true }
|
tungstenite = { version = "0.26", optional = true }
|
||||||
once_cell = { version = "1.20.2", optional = true }
|
once_cell = { version = "1.20", optional = true }
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -56,4 +56,4 @@ clap = { version = "4.5", features = ["derive"] }
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
|
|
@ -17,7 +17,7 @@ cargo add servicepoint
|
||||||
or
|
or
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
servicepoint = "0.12.0"
|
servicepoint = "0.13.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
@ -39,8 +39,11 @@ Execute `cargo run --example` for a list of available examples and `cargo run --
|
||||||
|
|
||||||
## Note on stability
|
## Note on stability
|
||||||
|
|
||||||
This library is still in early development.
|
This library can be used for creative project or just to play around with the display.
|
||||||
You can absolutely use it, and it works, but expect minor breaking changes with every version bump.
|
A decent coverage by unit tests prevents major problems and I also test this with my own projects, which mostly use up-to-date versions.
|
||||||
|
|
||||||
|
That being said, the API is still being worked on.
|
||||||
|
Expect minor breaking changes with every version bump.
|
||||||
Please specify the full version including patch in your Cargo.toml until 1.0 is released.
|
Please specify the full version including patch in your Cargo.toml until 1.0 is released.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
//! An example for how to send text to the display.
|
//! An example for how to send text to the display.
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use servicepoint::*;
|
||||||
use servicepoint::{CharGrid, Command, Connection, Cp437Grid, Origin};
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
|
@ -42,10 +41,8 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let text = cli.text.join("\n");
|
let text = cli.text.join("\n");
|
||||||
let grid = CharGrid::from(text);
|
let grid = CharGrid::wrap_str(TILE_WIDTH, &text);
|
||||||
let grid = Cp437Grid::from(grid);
|
|
||||||
|
|
||||||
connection
|
connection
|
||||||
.send(Command::Cp437Data(Origin::ZERO, grid))
|
.send(Command::Utf8Data(Origin::ZERO, grid))
|
||||||
.expect("sending text failed");
|
.expect("sending text failed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
//! Show a brightness level test pattern on screen
|
//! Show a brightness level test pattern on screen
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use servicepoint::*;
|
use servicepoint::*;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
//! A simple game of life implementation to show how to render graphics to the display.
|
//! A simple game of life implementation to show how to render graphics to the display.
|
||||||
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use rand::{distributions, Rng};
|
use rand::{distributions, Rng};
|
||||||
|
|
||||||
use servicepoint::*;
|
use servicepoint::*;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
//! A simple example for how to send pixel data to the display.
|
//! A simple example for how to send pixel data to the display.
|
||||||
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use servicepoint::*;
|
use servicepoint::*;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
//! A simple example for how to set brightnesses for tiles on the screen.
|
//! A simple example for how to set brightnesses for tiles on the screen.
|
||||||
//! Continuously changes the tiles in a random window to random brightnesses.
|
//! Continuously changes the tiles in a random window to random brightnesses.
|
||||||
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use servicepoint::Command::{BitmapLinearWin, Brightness, CharBrightness};
|
|
||||||
use servicepoint::*;
|
use servicepoint::*;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
|
@ -31,7 +28,7 @@ fn main() {
|
||||||
let mut filled_grid = Bitmap::max_sized();
|
let mut filled_grid = Bitmap::max_sized();
|
||||||
filled_grid.fill(true);
|
filled_grid.fill(true);
|
||||||
|
|
||||||
let command = BitmapLinearWin(
|
let command = Command::BitmapLinearWin(
|
||||||
Origin::ZERO,
|
Origin::ZERO,
|
||||||
filled_grid,
|
filled_grid,
|
||||||
CompressionCode::Lzma,
|
CompressionCode::Lzma,
|
||||||
|
@ -41,7 +38,7 @@ fn main() {
|
||||||
|
|
||||||
// set all pixels to the same random brightness
|
// set all pixels to the same random brightness
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
connection.send(Brightness(rng.gen())).unwrap();
|
connection.send(Command::Brightness(rng.gen())).unwrap();
|
||||||
|
|
||||||
// continuously update random windows to new random brightness
|
// continuously update random windows to new random brightness
|
||||||
loop {
|
loop {
|
||||||
|
@ -61,7 +58,9 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.send(CharBrightness(origin, luma)).unwrap();
|
connection
|
||||||
|
.send(Command::CharBrightness(origin, luma))
|
||||||
|
.unwrap();
|
||||||
std::thread::sleep(wait_duration);
|
std::thread::sleep(wait_duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
//! An example on how to modify the image on screen without knowing the current content.
|
//! An example on how to modify the image on screen without knowing the current content.
|
||||||
|
use clap::Parser;
|
||||||
|
use servicepoint::*;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
|
|
||||||
use servicepoint::*;
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[arg(short, long, default_value = "localhost:2342")]
|
#[arg(short, long, default_value = "localhost:2342")]
|
||||||
|
@ -34,7 +32,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
connection
|
connection
|
||||||
.send(Command::BitmapLinearWin(Origin::ZERO, enabled_pixels.clone(), CompressionCode::Lzma))
|
.send(Command::BitmapLinearWin(
|
||||||
|
Origin::ZERO,
|
||||||
|
enabled_pixels.clone(),
|
||||||
|
CompressionCode::Lzma,
|
||||||
|
))
|
||||||
.expect("could not send command to display");
|
.expect("could not send command to display");
|
||||||
thread::sleep(sleep_duration);
|
thread::sleep(sleep_duration);
|
||||||
}
|
}
|
||||||
|
|
10
crates/servicepoint/src/bit_vec.rs
Normal file
10
crates/servicepoint/src/bit_vec.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/// A byte-packed vector of booleans.
|
||||||
|
///
|
||||||
|
/// The implementation is provided by [bitvec].
|
||||||
|
/// This is an alias for the specific type of [bitvec::BitVec] used in this crate.
|
||||||
|
pub type BitVec = bitvec::BitVec<u8, bitvec::Msb0>;
|
||||||
|
|
||||||
|
pub mod bitvec {
|
||||||
|
//! Re-export of the used library [mod@bitvec].
|
||||||
|
pub use bitvec::prelude::*;
|
||||||
|
}
|
|
@ -1,10 +1,23 @@
|
||||||
use bitvec::order::Msb0;
|
use crate::data_ref::DataRef;
|
||||||
use bitvec::prelude::BitSlice;
|
use crate::BitVec;
|
||||||
use bitvec::slice::IterMut;
|
use crate::*;
|
||||||
|
use ::bitvec::order::Msb0;
|
||||||
|
use ::bitvec::prelude::BitSlice;
|
||||||
|
use ::bitvec::slice::IterMut;
|
||||||
|
|
||||||
use crate::{BitVec, DataRef, Grid, PIXEL_HEIGHT, PIXEL_WIDTH};
|
/// A fixed-size 2D grid of booleans.
|
||||||
|
///
|
||||||
/// A grid of pixels stored in packed bytes.
|
/// The values are stored in packed bytes (8 values per byte) in the same order as used by the display for storing pixels.
|
||||||
|
/// This means that no conversion is necessary for sending the data to the display.
|
||||||
|
/// The downside is that the width has to be a multiple of 8.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use servicepoint::Bitmap;
|
||||||
|
/// let mut bitmap = Bitmap::new(8, 2);
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Bitmap {
|
pub struct Bitmap {
|
||||||
width: usize,
|
width: usize,
|
||||||
|
@ -26,7 +39,11 @@ impl Bitmap {
|
||||||
///
|
///
|
||||||
/// - when the width is not dividable by 8
|
/// - when the width is not dividable by 8
|
||||||
pub fn new(width: usize, height: usize) -> Self {
|
pub fn new(width: usize, height: usize) -> Self {
|
||||||
assert_eq!(width % 8, 0);
|
assert_eq!(
|
||||||
|
width % 8,
|
||||||
|
0,
|
||||||
|
"width must be a multiple of 8, but is {width}"
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
@ -176,11 +193,38 @@ impl From<Bitmap> for Vec<u8> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Bitmap> for BitVec {
|
impl From<Bitmap> for BitVec {
|
||||||
|
/// Turns a [Bitmap] into the underlying [BitVec].
|
||||||
fn from(value: Bitmap) -> Self {
|
fn from(value: Bitmap) -> Self {
|
||||||
value.bit_vec
|
value.bit_vec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&ValueGrid<bool>> for Bitmap {
|
||||||
|
/// Converts a grid of [bool]s into a [Bitmap].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when the width of `value` is not dividable by 8
|
||||||
|
fn from(value: &ValueGrid<bool>) -> Self {
|
||||||
|
let mut result = Self::new(value.width(), value.height());
|
||||||
|
for (mut to, from) in result.iter_mut().zip(value.iter()) {
|
||||||
|
*to = *from;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Bitmap> for ValueGrid<bool> {
|
||||||
|
/// Converts a [Bitmap] into a grid of [bool]s.
|
||||||
|
fn from(value: &Bitmap) -> Self {
|
||||||
|
let mut result = Self::new(value.width(), value.height());
|
||||||
|
for (to, from) in result.iter_mut().zip(value.iter()) {
|
||||||
|
*to = *from;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct IterRows<'t> {
|
pub struct IterRows<'t> {
|
||||||
bitmap: &'t Bitmap,
|
bitmap: &'t Bitmap,
|
||||||
row: usize,
|
row: usize,
|
||||||
|
@ -203,7 +247,7 @@ impl<'t> Iterator for IterRows<'t> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{Bitmap, DataRef, Grid};
|
use crate::{BitVec, Bitmap, DataRef, Grid, ValueGrid};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fill() {
|
fn fill() {
|
||||||
|
@ -295,4 +339,24 @@ mod tests {
|
||||||
data[1] = 0x0F;
|
data[1] = 0x0F;
|
||||||
assert!(grid.get(7, 1));
|
assert!(grid.get(7, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_bitvec() {
|
||||||
|
let mut grid = Bitmap::new(8, 2);
|
||||||
|
grid.set(0, 0, true);
|
||||||
|
let bitvec: BitVec = grid.into();
|
||||||
|
assert_eq!(bitvec.as_raw_slice(), [0x80, 0x00]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_bool_grid() {
|
||||||
|
let original = ValueGrid::load(
|
||||||
|
8,
|
||||||
|
1,
|
||||||
|
&[true, false, true, false, true, false, true, false],
|
||||||
|
);
|
||||||
|
let converted = Bitmap::from(&original);
|
||||||
|
let reconverted = ValueGrid::from(&converted);
|
||||||
|
assert_eq!(original, reconverted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use crate::{Grid, PrimitiveGrid};
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
#[cfg(feature = "rand")]
|
||||||
use rand::{
|
use rand::{
|
||||||
distributions::{Distribution, Standard},
|
distributions::{Distribution, Standard},
|
||||||
|
@ -22,28 +20,6 @@ use rand::{
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||||
pub struct Brightness(u8);
|
pub struct Brightness(u8);
|
||||||
|
|
||||||
/// A grid containing brightness values.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use servicepoint::{Brightness, BrightnessGrid, Command, Connection, Grid, Origin};
|
|
||||||
/// let mut grid = BrightnessGrid::new(2,2);
|
|
||||||
/// grid.set(0, 0, Brightness::MIN);
|
|
||||||
/// grid.set(1, 1, Brightness::MIN);
|
|
||||||
///
|
|
||||||
/// # let connection = Connection::open("127.0.0.1:2342").unwrap();
|
|
||||||
/// connection.send(Command::CharBrightness(Origin::new(3, 7), grid)).unwrap()
|
|
||||||
/// ```
|
|
||||||
pub type BrightnessGrid = PrimitiveGrid<Brightness>;
|
|
||||||
|
|
||||||
impl BrightnessGrid {
|
|
||||||
/// Like [Self::load], but ignoring any out-of-range brightness values
|
|
||||||
pub fn saturating_load(width: usize, height: usize, data: &[u8]) -> Self {
|
|
||||||
PrimitiveGrid::load(width, height, data).map(Brightness::saturating_from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Brightness> for u8 {
|
impl From<Brightness> for u8 {
|
||||||
fn from(brightness: Brightness) -> Self {
|
fn from(brightness: Brightness) -> Self {
|
||||||
Self::from(&brightness)
|
Self::from(&brightness)
|
||||||
|
@ -92,41 +68,6 @@ impl Default for Brightness {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BrightnessGrid> for Vec<u8> {
|
|
||||||
fn from(value: PrimitiveGrid<Brightness>) -> Self {
|
|
||||||
value
|
|
||||||
.iter()
|
|
||||||
.map(|brightness| (*brightness).into())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&BrightnessGrid> for PrimitiveGrid<u8> {
|
|
||||||
fn from(value: &PrimitiveGrid<Brightness>) -> Self {
|
|
||||||
let u8s = value
|
|
||||||
.iter()
|
|
||||||
.map(|brightness| (*brightness).into())
|
|
||||||
.collect::<Vec<u8>>();
|
|
||||||
PrimitiveGrid::load(value.width(), value.height(), &u8s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<PrimitiveGrid<u8>> for BrightnessGrid {
|
|
||||||
type Error = u8;
|
|
||||||
|
|
||||||
fn try_from(value: PrimitiveGrid<u8>) -> Result<Self, Self::Error> {
|
|
||||||
let brightnesses = value
|
|
||||||
.iter()
|
|
||||||
.map(|b| Brightness::try_from(*b))
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
Ok(BrightnessGrid::load(
|
|
||||||
value.width(),
|
|
||||||
value.height(),
|
|
||||||
&brightnesses,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rand")]
|
#[cfg(feature = "rand")]
|
||||||
impl Distribution<Brightness> for Standard {
|
impl Distribution<Brightness> for Standard {
|
||||||
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Brightness {
|
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Brightness {
|
||||||
|
@ -137,7 +78,6 @@ impl Distribution<Brightness> for Standard {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::DataRef;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn brightness_from_u8() {
|
fn brightness_from_u8() {
|
||||||
|
@ -154,24 +94,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_u8_grid() {
|
|
||||||
let mut grid = BrightnessGrid::new(2, 2);
|
|
||||||
grid.set(1, 0, Brightness::MIN);
|
|
||||||
grid.set(0, 1, Brightness::MAX);
|
|
||||||
let actual = PrimitiveGrid::from(&grid);
|
|
||||||
assert_eq!(actual.data_ref(), &[11, 0, 11, 11]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn saturating_convert() {
|
fn saturating_convert() {
|
||||||
assert_eq!(Brightness::MAX, Brightness::saturating_from(100));
|
assert_eq!(Brightness::MAX, Brightness::saturating_from(100));
|
||||||
assert_eq!(Brightness(5), Brightness::saturating_from(5));
|
assert_eq!(Brightness(5), Brightness::saturating_from(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn saturating_load() {
|
|
||||||
assert_eq!(BrightnessGrid::load(2,2, &[Brightness::MAX, Brightness::MAX, Brightness::MIN, Brightness::MAX]),
|
|
||||||
BrightnessGrid::saturating_load(2,2, &[255u8, 23, 0, 42]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
93
crates/servicepoint/src/brightness_grid.rs
Normal file
93
crates/servicepoint/src/brightness_grid.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
use crate::brightness::Brightness;
|
||||||
|
use crate::grid::Grid;
|
||||||
|
use crate::value_grid::ValueGrid;
|
||||||
|
use crate::ByteGrid;
|
||||||
|
|
||||||
|
/// A grid containing brightness values.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use servicepoint::{Brightness, BrightnessGrid, Command, Connection, Grid, Origin};
|
||||||
|
/// let mut grid = BrightnessGrid::new(2,2);
|
||||||
|
/// grid.set(0, 0, Brightness::MIN);
|
||||||
|
/// grid.set(1, 1, Brightness::MIN);
|
||||||
|
///
|
||||||
|
/// # let connection = Connection::open("127.0.0.1:2342").unwrap();
|
||||||
|
/// connection.send(Command::CharBrightness(Origin::new(3, 7), grid)).unwrap()
|
||||||
|
/// ```
|
||||||
|
pub type BrightnessGrid = ValueGrid<Brightness>;
|
||||||
|
|
||||||
|
impl BrightnessGrid {
|
||||||
|
/// Like [Self::load], but ignoring any out-of-range brightness values
|
||||||
|
pub fn saturating_load(width: usize, height: usize, data: &[u8]) -> Self {
|
||||||
|
ValueGrid::load(width, height, data).map(Brightness::saturating_from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BrightnessGrid> for Vec<u8> {
|
||||||
|
fn from(value: ValueGrid<Brightness>) -> Self {
|
||||||
|
value
|
||||||
|
.iter()
|
||||||
|
.map(|brightness| (*brightness).into())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&BrightnessGrid> for ByteGrid {
|
||||||
|
fn from(value: &ValueGrid<Brightness>) -> Self {
|
||||||
|
let u8s = value
|
||||||
|
.iter()
|
||||||
|
.map(|brightness| (*brightness).into())
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
ValueGrid::load(value.width(), value.height(), &u8s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ByteGrid> for BrightnessGrid {
|
||||||
|
type Error = u8;
|
||||||
|
|
||||||
|
fn try_from(value: ByteGrid) -> Result<Self, Self::Error> {
|
||||||
|
let brightnesses = value
|
||||||
|
.iter()
|
||||||
|
.map(|b| Brightness::try_from(*b))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
Ok(BrightnessGrid::load(
|
||||||
|
value.width(),
|
||||||
|
value.height(),
|
||||||
|
&brightnesses,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::value_grid::ValueGrid;
|
||||||
|
use crate::{Brightness, BrightnessGrid, DataRef, Grid};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_u8_grid() {
|
||||||
|
let mut grid = BrightnessGrid::new(2, 2);
|
||||||
|
grid.set(1, 0, Brightness::MIN);
|
||||||
|
grid.set(0, 1, Brightness::MAX);
|
||||||
|
let actual = ValueGrid::from(&grid);
|
||||||
|
assert_eq!(actual.data_ref(), &[11, 0, 11, 11]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn saturating_load() {
|
||||||
|
assert_eq!(
|
||||||
|
BrightnessGrid::load(
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
&[
|
||||||
|
Brightness::MAX,
|
||||||
|
Brightness::MAX,
|
||||||
|
Brightness::MIN,
|
||||||
|
Brightness::MAX
|
||||||
|
]
|
||||||
|
),
|
||||||
|
BrightnessGrid::saturating_load(2, 2, &[255u8, 23, 0, 42])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
4
crates/servicepoint/src/byte_grid.rs
Normal file
4
crates/servicepoint/src/byte_grid.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
use crate::ValueGrid;
|
||||||
|
|
||||||
|
/// A 2d grid of bytes - see [ValueGrid].
|
||||||
|
pub type ByteGrid = ValueGrid<u8>;
|
|
@ -1,13 +1,71 @@
|
||||||
use crate::primitive_grid::SeriesError;
|
use crate::{Grid, SetValueSeriesError, TryLoadValueGridError, ValueGrid};
|
||||||
use crate::{Grid, PrimitiveGrid};
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
/// A grid containing UTF-8 characters.
|
/// A grid containing UTF-8 characters.
|
||||||
pub type CharGrid = PrimitiveGrid<char>;
|
///
|
||||||
|
/// To send a CharGrid to the display, use [Command::Utf8Data](crate::Command::Utf8Data).
|
||||||
|
///
|
||||||
|
/// Also see [ValueGrid] for the non-specialized operations and examples.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use servicepoint::{CharGrid, Command, Connection, Origin};
|
||||||
|
/// let grid = CharGrid::from("You can\nload multiline\nstrings directly");
|
||||||
|
/// assert_eq!(grid.get_row_str(1), Some("load multiline\0\0".to_string()));
|
||||||
|
///
|
||||||
|
/// # let connection = Connection::Fake;
|
||||||
|
/// let command = Command::Utf8Data(Origin::ZERO, grid);
|
||||||
|
/// ```
|
||||||
|
pub type CharGrid = ValueGrid<char>;
|
||||||
|
|
||||||
impl CharGrid {
|
impl CharGrid {
|
||||||
|
/// Loads a [CharGrid] with the specified width from the provided text, wrapping to as many rows as needed.
|
||||||
|
///
|
||||||
|
/// The passed rows are extended with '\0' if needed.
|
||||||
|
///
|
||||||
|
/// returns: [CharGrid] that contains a copy of the provided data.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::CharGrid;
|
||||||
|
/// let grid = CharGrid::wrap_str(2, "abc\ndef");
|
||||||
|
/// ```
|
||||||
|
pub fn wrap_str(width: usize, text: &str) -> Self {
|
||||||
|
let lines = text
|
||||||
|
.split('\n')
|
||||||
|
.flat_map(move |x| {
|
||||||
|
x.chars()
|
||||||
|
.collect::<Vec<char>>()
|
||||||
|
.chunks(width)
|
||||||
|
.map(|c| {
|
||||||
|
let mut s = String::from_iter(c);
|
||||||
|
s.push_str(&"\0".repeat(width - s.chars().count()));
|
||||||
|
s
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
let height = lines.len();
|
||||||
|
let mut result = Self::new(width, height);
|
||||||
|
for (row, text_line) in lines.iter().enumerate() {
|
||||||
|
result.set_row_str(row, text_line).unwrap()
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
/// Copies a column from the grid as a String.
|
/// Copies a column from the grid as a String.
|
||||||
///
|
///
|
||||||
/// Returns [None] if x is out of bounds.
|
/// Returns [None] if x is out of bounds.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::CharGrid;
|
||||||
|
/// let grid = CharGrid::from("ab\ncd");
|
||||||
|
/// let col = grid.get_col_str(0).unwrap(); // "ac"
|
||||||
|
/// ```
|
||||||
pub fn get_col_str(&self, x: usize) -> Option<String> {
|
pub fn get_col_str(&self, x: usize) -> Option<String> {
|
||||||
Some(String::from_iter(self.get_col(x)?))
|
Some(String::from_iter(self.get_col(x)?))
|
||||||
}
|
}
|
||||||
|
@ -15,42 +73,91 @@ impl CharGrid {
|
||||||
/// Copies a row from the grid as a String.
|
/// Copies a row from the grid as a String.
|
||||||
///
|
///
|
||||||
/// Returns [None] if y is out of bounds.
|
/// Returns [None] if y is out of bounds.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::CharGrid;
|
||||||
|
/// let grid = CharGrid::from("ab\ncd");
|
||||||
|
/// let row = grid.get_row_str(0).unwrap(); // "ab"
|
||||||
|
/// ```
|
||||||
pub fn get_row_str(&self, y: usize) -> Option<String> {
|
pub fn get_row_str(&self, y: usize) -> Option<String> {
|
||||||
Some(String::from_iter(self.get_row(y)?))
|
Some(String::from_iter(self.get_row(y)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overwrites a row in the grid with a str.
|
/// Overwrites a row in the grid with a str.
|
||||||
///
|
///
|
||||||
/// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size.
|
/// Returns [SetValueSeriesError] if y is out of bounds or `row` is not of the correct size.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::CharGrid;
|
||||||
|
/// let mut grid = CharGrid::from("ab\ncd");
|
||||||
|
/// grid.set_row_str(0, "ef").unwrap();
|
||||||
|
/// ```
|
||||||
pub fn set_row_str(
|
pub fn set_row_str(
|
||||||
&mut self,
|
&mut self,
|
||||||
y: usize,
|
y: usize,
|
||||||
value: &str,
|
value: &str,
|
||||||
) -> Result<(), SeriesError> {
|
) -> Result<(), SetValueSeriesError> {
|
||||||
self.set_row(y, value.chars().collect::<Vec<_>>().as_ref())
|
self.set_row(y, value.chars().collect::<Vec<_>>().as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overwrites a column in the grid with a str.
|
/// Overwrites a column in the grid with a str.
|
||||||
///
|
///
|
||||||
/// Returns [SeriesError] if y is out of bounds or `row` is not of the correct size.
|
/// Returns [SetValueSeriesError] if y is out of bounds or `row` is not of the correct size.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::CharGrid;
|
||||||
|
/// let mut grid = CharGrid::from("ab\ncd");
|
||||||
|
/// grid.set_col_str(0, "ef").unwrap();
|
||||||
|
/// ```
|
||||||
pub fn set_col_str(
|
pub fn set_col_str(
|
||||||
&mut self,
|
&mut self,
|
||||||
x: usize,
|
x: usize,
|
||||||
value: &str,
|
value: &str,
|
||||||
) -> Result<(), SeriesError> {
|
) -> Result<(), SetValueSeriesError> {
|
||||||
self.set_col(x, value.chars().collect::<Vec<_>>().as_ref())
|
self.set_col(x, value.chars().collect::<Vec<_>>().as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Loads a [CharGrid] with the specified dimensions from the provided UTF-8 bytes.
|
||||||
|
///
|
||||||
|
/// returns: [CharGrid] that contains the provided data, or [FromUtf8Error] if the data is invalid.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::CharGrid;
|
||||||
|
/// let grid = CharGrid::load_utf8(2, 2, [97u8, 98, 99, 100].to_vec());
|
||||||
|
/// ```
|
||||||
|
pub fn load_utf8(
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
) -> Result<CharGrid, LoadUtf8Error> {
|
||||||
|
let s: Vec<char> = String::from_utf8(bytes)?.chars().collect();
|
||||||
|
Ok(CharGrid::try_load(width, height, s)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum LoadUtf8Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
FromUtf8Error(#[from] FromUtf8Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
TryLoadError(#[from] TryLoadValueGridError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for CharGrid {
|
impl From<&str> for CharGrid {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
let value = value.replace("\r\n", "\n");
|
let value = value.replace("\r\n", "\n");
|
||||||
let mut lines = value
|
let mut lines = value.split('\n').collect::<Vec<_>>();
|
||||||
.split('\n')
|
let width = lines
|
||||||
.map(move |line| line.trim_end())
|
.iter()
|
||||||
.collect::<Vec<_>>();
|
.fold(0, move |a, x| std::cmp::max(a, x.chars().count()));
|
||||||
let width =
|
|
||||||
lines.iter().fold(0, move |a, x| std::cmp::max(a, x.len()));
|
|
||||||
|
|
||||||
while lines.last().is_some_and(move |line| line.is_empty()) {
|
while lines.last().is_some_and(move |line| line.is_empty()) {
|
||||||
_ = lines.pop();
|
_ = lines.pop();
|
||||||
|
@ -73,26 +180,63 @@ impl From<String> for CharGrid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<CharGrid> for String {
|
||||||
|
fn from(grid: CharGrid) -> Self {
|
||||||
|
String::from(&grid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&CharGrid> for String {
|
impl From<&CharGrid> for String {
|
||||||
|
/// Converts a [CharGrid] into a [String].
|
||||||
|
///
|
||||||
|
/// Rows are separated by '\n'.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use servicepoint::CharGrid;
|
||||||
|
/// let grid = CharGrid::from("ab\ncd");
|
||||||
|
/// let string = String::from(grid);
|
||||||
|
/// let grid = CharGrid::from(string);
|
||||||
|
/// ```
|
||||||
fn from(value: &CharGrid) -> Self {
|
fn from(value: &CharGrid) -> Self {
|
||||||
value
|
value
|
||||||
.iter_rows()
|
.iter_rows()
|
||||||
.map(move |chars| {
|
.map(String::from_iter)
|
||||||
chars
|
.collect::<Vec<String>>()
|
||||||
.collect::<String>()
|
|
||||||
.replace('\0', " ")
|
|
||||||
.trim_end()
|
|
||||||
.to_string()
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n")
|
.join("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&CharGrid> for Vec<u8> {
|
||||||
|
/// Converts a [CharGrid] into a [`Vec<u8>`].
|
||||||
|
///
|
||||||
|
/// Rows are not separated.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use servicepoint::{CharGrid, Grid};
|
||||||
|
/// let grid = CharGrid::from("ab\ncd");
|
||||||
|
/// let height = grid.height();
|
||||||
|
/// let width = grid.width();
|
||||||
|
/// let grid = CharGrid::load_utf8(width, height, grid.into());
|
||||||
|
/// ```
|
||||||
|
fn from(value: &CharGrid) -> Self {
|
||||||
|
String::from_iter(value.iter()).into_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CharGrid> for Vec<u8> {
|
||||||
|
/// See [`From<&CharGrid>::from`].
|
||||||
|
fn from(value: CharGrid) -> Self {
|
||||||
|
Self::from(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Grid;
|
|
||||||
#[test]
|
#[test]
|
||||||
fn col_str() {
|
fn col_str() {
|
||||||
let mut grid = CharGrid::new(2, 3);
|
let mut grid = CharGrid::new(2, 3);
|
||||||
|
@ -109,7 +253,7 @@ mod test {
|
||||||
assert_eq!(grid.get_row_str(1), Some(String::from("\0\0")));
|
assert_eq!(grid.get_row_str(1), Some(String::from("\0\0")));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
grid.set_row_str(1, "abc"),
|
grid.set_row_str(1, "abc"),
|
||||||
Err(SeriesError::InvalidLength {
|
Err(SetValueSeriesError::InvalidLength {
|
||||||
expected: 2,
|
expected: 2,
|
||||||
actual: 3
|
actual: 3
|
||||||
})
|
})
|
||||||
|
@ -120,10 +264,35 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn str_to_char_grid() {
|
fn str_to_char_grid() {
|
||||||
let original = "Hello\r\nWorld!\n...\n";
|
// conversion with .to_string() covers one more line
|
||||||
|
let original = "Hello\r\nWorld!\n...\n".to_string();
|
||||||
|
|
||||||
let grid = CharGrid::from(original);
|
let grid = CharGrid::from(original);
|
||||||
assert_eq!(3, grid.height());
|
assert_eq!(3, grid.height());
|
||||||
let actual = String::from(&grid);
|
assert_eq!("Hello\0\nWorld!\n...\0\0\0", String::from(grid));
|
||||||
assert_eq!("Hello\nWorld!\n...", actual);
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn round_trip_bytes() {
|
||||||
|
let grid = CharGrid::from("Hello\0\nWorld!\n...\0\0\0");
|
||||||
|
let bytes: Vec<u8> = grid.clone().into();
|
||||||
|
let copy =
|
||||||
|
CharGrid::load_utf8(grid.width(), grid.height(), bytes).unwrap();
|
||||||
|
assert_eq!(grid, copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn round_trip_string() {
|
||||||
|
let grid = CharGrid::from("Hello\0\nWorld!\n...\0\0\0");
|
||||||
|
let str: String = grid.clone().into();
|
||||||
|
let copy = CharGrid::from(str);
|
||||||
|
assert_eq!(grid, copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wrap_str() {
|
||||||
|
let grid = CharGrid::wrap_str(2, "abc\ndef");
|
||||||
|
assert_eq!(4, grid.height());
|
||||||
|
assert_eq!("ab\nc\0\nde\nf\0", String::from(grid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
use crate::{
|
use crate::command_code::CommandCode;
|
||||||
command_code::CommandCode,
|
use crate::compression::into_decompressed;
|
||||||
compression::into_decompressed,
|
use crate::value_grid::ValueGrid;
|
||||||
packet::{Header, Packet},
|
use crate::*;
|
||||||
Bitmap, Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin,
|
|
||||||
Pixels, PrimitiveGrid, BitVec, Tiles, TILE_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Type alias for documenting the meaning of the u16 in enum values
|
/// Type alias for documenting the meaning of the u16 in enum values
|
||||||
pub type Offset = usize;
|
pub type Offset = usize;
|
||||||
|
@ -41,7 +38,7 @@ pub type Offset = usize;
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use servicepoint::{Brightness, Command, Connection, packet::Packet};
|
/// use servicepoint::{Brightness, Command, Connection, Packet};
|
||||||
/// #
|
/// #
|
||||||
/// // create command
|
/// // create command
|
||||||
/// let command = Command::Brightness(Brightness::MAX);
|
/// let command = Command::Brightness(Brightness::MAX);
|
||||||
|
@ -74,14 +71,29 @@ pub enum Command {
|
||||||
|
|
||||||
/// Show text on the screen.
|
/// Show text on the screen.
|
||||||
///
|
///
|
||||||
/// The text is sent in the form of a 2D grid of [CP-437] encoded characters.
|
/// The text is sent in the form of a 2D grid of UTF-8 encoded characters (the default encoding in rust).
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use servicepoint::{Command, Connection, Origin};
|
/// # use servicepoint::{Command, Connection, Origin, CharGrid};
|
||||||
|
/// # let connection = Connection::Fake;
|
||||||
|
/// let grid = CharGrid::from("Hello,\nWorld!");
|
||||||
|
/// connection.send(Command::Utf8Data(Origin::ZERO, grid)).expect("send failed");
|
||||||
|
/// ```
|
||||||
|
Utf8Data(Origin<Tiles>, CharGrid),
|
||||||
|
|
||||||
|
/// Show text on the screen.
|
||||||
|
///
|
||||||
|
/// The text is sent in the form of a 2D grid of [CP-437] encoded characters.
|
||||||
|
///
|
||||||
|
/// <div class="warning">You probably want to use [Command::Utf8Data] instead</div>
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use servicepoint::{Command, Connection, Origin, CharGrid, Cp437Grid};
|
||||||
/// # let connection = Connection::Fake;
|
/// # let connection = Connection::Fake;
|
||||||
/// use servicepoint::{CharGrid, Cp437Grid};
|
|
||||||
/// let grid = CharGrid::from("Hello,\nWorld!");
|
/// let grid = CharGrid::from("Hello,\nWorld!");
|
||||||
/// let grid = Cp437Grid::from(&grid);
|
/// let grid = Cp437Grid::from(&grid);
|
||||||
/// connection.send(Command::Cp437Data(Origin::ZERO, grid)).expect("send failed");
|
/// connection.send(Command::Cp437Data(Origin::ZERO, grid)).expect("send failed");
|
||||||
|
@ -234,6 +246,8 @@ pub enum TryFromPacketError {
|
||||||
/// The given brightness value is out of bounds
|
/// The given brightness value is out of bounds
|
||||||
#[error("The given brightness value {0} is out of bounds.")]
|
#[error("The given brightness value {0} is out of bounds.")]
|
||||||
InvalidBrightness(u8),
|
InvalidBrightness(u8),
|
||||||
|
#[error(transparent)]
|
||||||
|
InvalidUtf8(#[from] std::string::FromUtf8Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Packet> for Command {
|
impl TryFrom<Packet> for Command {
|
||||||
|
@ -269,6 +283,7 @@ impl TryFrom<Packet> for Command {
|
||||||
CommandCode::CharBrightness => {
|
CommandCode::CharBrightness => {
|
||||||
Self::packet_into_char_brightness(&packet)
|
Self::packet_into_char_brightness(&packet)
|
||||||
}
|
}
|
||||||
|
CommandCode::Utf8Data => Self::packet_into_utf8(&packet),
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
CommandCode::BitmapLegacy => Ok(Command::BitmapLegacy),
|
CommandCode::BitmapLegacy => Ok(Command::BitmapLegacy),
|
||||||
CommandCode::BitmapLinear => {
|
CommandCode::BitmapLinear => {
|
||||||
|
@ -426,8 +441,7 @@ impl Command {
|
||||||
payload,
|
payload,
|
||||||
} = packet;
|
} = packet;
|
||||||
|
|
||||||
let grid =
|
let grid = ValueGrid::load(*width as usize, *height as usize, payload);
|
||||||
PrimitiveGrid::load(*width as usize, *height as usize, payload);
|
|
||||||
let grid = match BrightnessGrid::try_from(grid) {
|
let grid = match BrightnessGrid::try_from(grid) {
|
||||||
Ok(grid) => grid,
|
Ok(grid) => grid,
|
||||||
Err(val) => return Err(TryFromPacketError::InvalidBrightness(val)),
|
Err(val) => return Err(TryFromPacketError::InvalidBrightness(val)),
|
||||||
|
@ -489,18 +503,37 @@ impl Command {
|
||||||
Cp437Grid::load(*c as usize, *d as usize, payload),
|
Cp437Grid::load(*c as usize, *d as usize, payload),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn packet_into_utf8(
|
||||||
|
packet: &Packet,
|
||||||
|
) -> Result<Command, TryFromPacketError> {
|
||||||
|
let Packet {
|
||||||
|
header:
|
||||||
|
Header {
|
||||||
|
command_code: _,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
d,
|
||||||
|
},
|
||||||
|
payload,
|
||||||
|
} = packet;
|
||||||
|
let payload: Vec<_> =
|
||||||
|
String::from_utf8(payload.clone())?.chars().collect();
|
||||||
|
Ok(Command::Utf8Data(
|
||||||
|
Origin::new(*a as usize, *b as usize),
|
||||||
|
CharGrid::load(*c as usize, *d as usize, &payload),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::command::TryFromPacketError;
|
||||||
|
use crate::command_code::CommandCode;
|
||||||
use crate::{
|
use crate::{
|
||||||
bitvec::prelude::BitVec,
|
BitVec, Bitmap, Brightness, BrightnessGrid, CharGrid, Command,
|
||||||
command::TryFromPacketError,
|
CompressionCode, Cp437Grid, Header, Origin, Packet, Pixels,
|
||||||
command_code::CommandCode,
|
|
||||||
origin::Pixels,
|
|
||||||
packet::{Header, Packet},
|
|
||||||
Bitmap, Brightness, BrightnessGrid, Command, CompressionCode, Origin,
|
|
||||||
PrimitiveGrid,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn round_trip(original: Command) {
|
fn round_trip(original: Command) {
|
||||||
|
@ -556,16 +589,18 @@ mod tests {
|
||||||
fn round_trip_char_brightness() {
|
fn round_trip_char_brightness() {
|
||||||
round_trip(Command::CharBrightness(
|
round_trip(Command::CharBrightness(
|
||||||
Origin::new(5, 2),
|
Origin::new(5, 2),
|
||||||
PrimitiveGrid::new(7, 5),
|
BrightnessGrid::new(7, 5),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn round_trip_cp437_data() {
|
fn round_trip_cp437_data() {
|
||||||
round_trip(Command::Cp437Data(
|
round_trip(Command::Cp437Data(Origin::new(5, 2), Cp437Grid::new(7, 5)));
|
||||||
Origin::new(5, 2),
|
}
|
||||||
PrimitiveGrid::new(7, 5),
|
|
||||||
));
|
#[test]
|
||||||
|
fn round_trip_utf8_data() {
|
||||||
|
round_trip(Command::Utf8Data(Origin::new(5, 2), CharGrid::new(7, 5)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub(crate) enum CommandCode {
|
||||||
BitmapLinearWinBzip2 = 0x0018,
|
BitmapLinearWinBzip2 = 0x0018,
|
||||||
#[cfg(feature = "compression_lzma")]
|
#[cfg(feature = "compression_lzma")]
|
||||||
BitmapLinearWinLzma = 0x0019,
|
BitmapLinearWinLzma = 0x0019,
|
||||||
|
Utf8Data = 0x0020,
|
||||||
#[cfg(feature = "compression_zstd")]
|
#[cfg(feature = "compression_zstd")]
|
||||||
BitmapLinearWinZstd = 0x001A,
|
BitmapLinearWinZstd = 0x001A,
|
||||||
}
|
}
|
||||||
|
@ -93,6 +94,9 @@ impl TryFrom<u16> for CommandCode {
|
||||||
value if value == CommandCode::BitmapLinearWinBzip2 as u16 => {
|
value if value == CommandCode::BitmapLinearWinBzip2 as u16 => {
|
||||||
Ok(CommandCode::BitmapLinearWinBzip2)
|
Ok(CommandCode::BitmapLinearWinBzip2)
|
||||||
}
|
}
|
||||||
|
value if value == CommandCode::Utf8Data as u16 => {
|
||||||
|
Ok(CommandCode::Utf8Data)
|
||||||
|
}
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use flate2::{FlushCompress, FlushDecompress, Status};
|
||||||
#[cfg(feature = "compression_zstd")]
|
#[cfg(feature = "compression_zstd")]
|
||||||
use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder};
|
use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder};
|
||||||
|
|
||||||
use crate::{packet::Payload, CompressionCode};
|
use crate::{CompressionCode, Payload};
|
||||||
|
|
||||||
pub(crate) fn into_decompressed(
|
pub(crate) fn into_decompressed(
|
||||||
kind: CompressionCode,
|
kind: CompressionCode,
|
||||||
|
|
|
@ -107,9 +107,7 @@ impl Connection {
|
||||||
|
|
||||||
let request = ClientRequestBuilder::new(uri).into_client_request()?;
|
let request = ClientRequestBuilder::new(uri).into_client_request()?;
|
||||||
let (sock, _) = connect(request)?;
|
let (sock, _) = connect(request)?;
|
||||||
Ok(Self::WebSocket(std::sync::Mutex::new(
|
Ok(Self::WebSocket(std::sync::Mutex::new(sock)))
|
||||||
sock,
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send something packet-like to the display. Usually this is in the form of a Command.
|
/// Send something packet-like to the display. Usually this is in the form of a Command.
|
||||||
|
@ -144,7 +142,7 @@ impl Connection {
|
||||||
Connection::WebSocket(socket) => {
|
Connection::WebSocket(socket) => {
|
||||||
let mut socket = socket.lock().unwrap();
|
let mut socket = socket.lock().unwrap();
|
||||||
socket
|
socket
|
||||||
.send(tungstenite::Message::Binary(data))
|
.send(tungstenite::Message::Binary(data.into()))
|
||||||
.map_err(SendError::WebsocketError)
|
.map_err(SendError::WebsocketError)
|
||||||
}
|
}
|
||||||
Connection::Fake => {
|
Connection::Fake => {
|
||||||
|
@ -159,9 +157,7 @@ impl Drop for Connection {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
#[cfg(feature = "protocol_websocket")]
|
#[cfg(feature = "protocol_websocket")]
|
||||||
if let Connection::WebSocket(sock) = self {
|
if let Connection::WebSocket(sock) = self {
|
||||||
_ = sock
|
_ = sock.try_lock().map(move |mut sock| sock.close(None));
|
||||||
.try_lock()
|
|
||||||
.map(move |mut sock| sock.close(None));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +165,6 @@ impl Drop for Connection {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::packet::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn send_fake() {
|
fn send_fake() {
|
||||||
|
|
75
crates/servicepoint/src/constants.rs
Normal file
75
crates/servicepoint/src/constants.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// size of a single tile in one dimension
|
||||||
|
pub const TILE_SIZE: usize = 8;
|
||||||
|
|
||||||
|
/// Display tile count in the x-direction
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
|
||||||
|
/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
|
||||||
|
/// ```
|
||||||
|
pub const TILE_WIDTH: usize = 56;
|
||||||
|
|
||||||
|
/// Display tile count in the y-direction
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
|
||||||
|
/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
|
||||||
|
/// ```
|
||||||
|
pub const TILE_HEIGHT: usize = 20;
|
||||||
|
|
||||||
|
/// Display width in pixels
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
|
||||||
|
/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
|
||||||
|
/// ```
|
||||||
|
pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE;
|
||||||
|
|
||||||
|
/// Display height in pixels
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
|
||||||
|
/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
|
||||||
|
/// ```
|
||||||
|
pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE;
|
||||||
|
|
||||||
|
/// pixel count on whole screen
|
||||||
|
pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT;
|
||||||
|
|
||||||
|
/// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use std::time::Instant;
|
||||||
|
/// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, Bitmap};
|
||||||
|
/// # let connection = servicepoint::Connection::Fake;
|
||||||
|
/// # let pixels = Bitmap::max_sized();
|
||||||
|
/// loop {
|
||||||
|
/// let start = Instant::now();
|
||||||
|
///
|
||||||
|
/// // Change pixels here
|
||||||
|
///
|
||||||
|
/// connection.send(Command::BitmapLinearWin(
|
||||||
|
/// Origin::new(0,0),
|
||||||
|
/// pixels,
|
||||||
|
/// CompressionCode::Lzma
|
||||||
|
/// ))
|
||||||
|
/// .expect("send failed");
|
||||||
|
///
|
||||||
|
/// // warning: will crash if resulting duration is negative, e.g. when resuming from standby
|
||||||
|
/// std::thread::sleep(FRAME_PACING - start.elapsed());
|
||||||
|
/// # break; // prevent doctest from hanging
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub const FRAME_PACING: Duration = Duration::from_millis(30);
|
|
@ -1,153 +1,54 @@
|
||||||
//! Conversion between UTF-8 and CP-437.
|
|
||||||
//!
|
|
||||||
//! Most of the functionality is only available with feature "cp437" enabled.
|
|
||||||
|
|
||||||
use crate::{Grid, PrimitiveGrid};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// A grid containing codepage 437 characters.
|
/// Contains functions to convert between UTF-8 and Codepage 437.
|
||||||
///
|
///
|
||||||
/// The encoding is currently not enforced.
|
/// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
|
||||||
pub type Cp437Grid = PrimitiveGrid<u8>;
|
pub struct Cp437Converter;
|
||||||
|
|
||||||
/// The error occurring when loading an invalid character
|
/// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters
|
||||||
#[derive(Debug, PartialEq, thiserror::Error)]
|
///
|
||||||
#[error("The character {char:?} at position {index} is not a valid CP437 character")]
|
/// Mostly follows CP437, except 0x0A, which is kept for use as line ending.
|
||||||
pub struct InvalidCharError {
|
///
|
||||||
/// invalid character is at this position in input
|
/// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
|
||||||
index: usize,
|
///
|
||||||
/// the invalid character
|
/// Mostly copied from <https://github.com/kip93/cp437-tools>. License: GPL-3.0
|
||||||
char: char,
|
#[rustfmt::skip]
|
||||||
}
|
const CP437_TO_UTF8: [char; 256] = [
|
||||||
|
/* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '♪', '♫', '☼',
|
||||||
impl Cp437Grid {
|
/* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔', '▲', '▼',
|
||||||
/// Load an ASCII-only [&str] into a [Cp437Grid] of specified width.
|
/* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/',
|
||||||
///
|
/* 3X */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
|
||||||
/// # Panics
|
/* 4X */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||||||
///
|
/* 5X */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\',']', '^', '_',
|
||||||
/// - for width == 0
|
/* 6X */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||||
/// - on empty strings
|
/* 7X */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '⌂',
|
||||||
pub fn load_ascii(
|
/* 8X */ 'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä', 'Å',
|
||||||
value: &str,
|
/* 9X */ 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥', '₧', 'ƒ',
|
||||||
width: usize,
|
/* AX */ 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '⌐', '¬', '½', '¼', '¡', '«', '»',
|
||||||
wrap: bool,
|
/* BX */ '░', '▒', '▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗', '╝', '╜', '╛', '┐',
|
||||||
) -> Result<Self, InvalidCharError> {
|
/* CX */ '└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧',
|
||||||
assert!(width > 0);
|
/* DX */ '╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀',
|
||||||
assert!(!value.is_empty());
|
/* EX */ 'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩',
|
||||||
|
/* FX */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ',
|
||||||
let mut chars = {
|
];
|
||||||
let mut x = 0;
|
static UTF8_TO_CP437: once_cell::sync::Lazy<HashMap<char, u8>> =
|
||||||
let mut y = 0;
|
once_cell::sync::Lazy::new(|| {
|
||||||
|
let pairs = CP437_TO_UTF8
|
||||||
for (index, char) in value.chars().enumerate() {
|
.iter()
|
||||||
if !char.is_ascii() {
|
.enumerate()
|
||||||
return Err(InvalidCharError { index, char });
|
.map(move |(index, char)| (*char, index as u8));
|
||||||
}
|
HashMap::from_iter(pairs)
|
||||||
|
});
|
||||||
let is_lf = char == '\n';
|
|
||||||
if is_lf || (wrap && x == width) {
|
|
||||||
y += 1;
|
|
||||||
x = 0;
|
|
||||||
if is_lf {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
x += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cp437Grid::new(width, y + 1)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut x = 0;
|
|
||||||
let mut y = 0;
|
|
||||||
for char in value.chars().map(move |c| c as u8) {
|
|
||||||
let is_lf = char == b'\n';
|
|
||||||
if is_lf || (wrap && x == width) {
|
|
||||||
y += 1;
|
|
||||||
x = 0;
|
|
||||||
if is_lf {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if wrap || x < width {
|
|
||||||
chars.set(x, y, char);
|
|
||||||
}
|
|
||||||
x += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(chars)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)] // depends on features
|
|
||||||
pub use feature_cp437::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "cp437")]
|
|
||||||
mod feature_cp437 {
|
|
||||||
use super::*;
|
|
||||||
use crate::CharGrid;
|
|
||||||
|
|
||||||
/// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters
|
|
||||||
///
|
|
||||||
/// Mostly follows CP437, except 0x0A, which is kept for use as line ending.
|
|
||||||
///
|
|
||||||
/// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
|
|
||||||
///
|
|
||||||
/// Mostly copied from <https://github.com/kip93/cp437-tools>. License: GPL-3.0
|
|
||||||
#[rustfmt::skip]
|
|
||||||
pub const CP437_TO_UTF8: [char; 256] = [
|
|
||||||
/* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '♪', '♫', '☼',
|
|
||||||
/* 1X */ '►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔', '▲', '▼',
|
|
||||||
/* 2X */ ' ', '!', '"', '#', '$', '%', '&', '\'','(', ')', '*', '+', ',', '-', '.', '/',
|
|
||||||
/* 3X */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
|
|
||||||
/* 4X */ '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
||||||
/* 5X */ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\',']', '^', '_',
|
|
||||||
/* 6X */ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
||||||
/* 7X */ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '⌂',
|
|
||||||
/* 8X */ 'Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë', 'è', 'ï', 'î', 'ì', 'Ä', 'Å',
|
|
||||||
/* 9X */ 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù', 'ÿ', 'Ö', 'Ü', '¢', '£', '¥', '₧', 'ƒ',
|
|
||||||
/* AX */ 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ', 'ª', 'º', '¿', '⌐', '¬', '½', '¼', '¡', '«', '»',
|
|
||||||
/* BX */ '░', '▒', '▓', '│', '┤', '╡', '╢', '╖', '╕', '╣', '║', '╗', '╝', '╜', '╛', '┐',
|
|
||||||
/* CX */ '└', '┴', '┬', '├', '─', '┼', '╞', '╟', '╚', '╔', '╩', '╦', '╠', '═', '╬', '╧',
|
|
||||||
/* DX */ '╨', '╤', '╥', '╙', '╘', '╒', '╓', '╫', '╪', '┘', '┌', '█', '▄', '▌', '▐', '▀',
|
|
||||||
/* EX */ 'α', 'ß', 'Γ', 'π', 'Σ', 'σ', 'µ', 'τ', 'Φ', 'Θ', 'Ω', 'δ', '∞', 'φ', 'ε', '∩',
|
|
||||||
/* FX */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ',
|
|
||||||
];
|
|
||||||
|
|
||||||
static UTF8_TO_CP437: once_cell::sync::Lazy<HashMap<char, u8>> =
|
|
||||||
once_cell::sync::Lazy::new(|| {
|
|
||||||
let pairs = CP437_TO_UTF8
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(move |(index, char)| (*char, index as u8));
|
|
||||||
HashMap::from_iter(pairs)
|
|
||||||
});
|
|
||||||
|
|
||||||
|
impl Cp437Converter {
|
||||||
const MISSING_CHAR_CP437: u8 = 0x3F; // '?'
|
const MISSING_CHAR_CP437: u8 = 0x3F; // '?'
|
||||||
|
|
||||||
impl From<&Cp437Grid> for CharGrid {
|
|
||||||
fn from(value: &Cp437Grid) -> Self {
|
|
||||||
value.map(cp437_to_char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&CharGrid> for Cp437Grid {
|
|
||||||
fn from(value: &CharGrid) -> Self {
|
|
||||||
value.map(char_to_cp437)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CharGrid> for Cp437Grid {
|
|
||||||
fn from(value: CharGrid) -> Self {
|
|
||||||
Cp437Grid::from(&value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the provided bytes to UTF-8.
|
/// Convert the provided bytes to UTF-8.
|
||||||
pub fn cp437_to_str(cp437: &[u8]) -> String {
|
pub fn cp437_to_str(cp437: &[u8]) -> String {
|
||||||
cp437.iter().map(move |char| cp437_to_char(*char)).collect()
|
cp437
|
||||||
|
.iter()
|
||||||
|
.map(move |char| Self::cp437_to_char(*char))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a single CP-437 character to UTF-8.
|
/// Convert a single CP-437 character to UTF-8.
|
||||||
|
@ -159,66 +60,20 @@ mod feature_cp437 {
|
||||||
///
|
///
|
||||||
/// Characters that are not available are mapped to '?'.
|
/// Characters that are not available are mapped to '?'.
|
||||||
pub fn str_to_cp437(utf8: &str) -> Vec<u8> {
|
pub fn str_to_cp437(utf8: &str) -> Vec<u8> {
|
||||||
utf8.chars().map(char_to_cp437).collect()
|
utf8.chars().map(Self::char_to_cp437).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a single UTF-8 character to CP-437.
|
/// Convert a single UTF-8 character to CP-437.
|
||||||
pub fn char_to_cp437(utf8: char) -> u8 {
|
pub fn char_to_cp437(utf8: char) -> u8 {
|
||||||
*UTF8_TO_CP437.get(&utf8).unwrap_or(&MISSING_CHAR_CP437)
|
*UTF8_TO_CP437
|
||||||
|
.get(&utf8)
|
||||||
|
.unwrap_or(&Self::MISSING_CHAR_CP437)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn load_ascii_nowrap() {
|
|
||||||
let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd']
|
|
||||||
.map(move |c| c as u8);
|
|
||||||
let expected = Cp437Grid::load(5, 2, &chars);
|
|
||||||
|
|
||||||
let actual = Cp437Grid::load_ascii("Hello,\nWorld!", 5, false).unwrap();
|
|
||||||
// comma will be removed because line is too long and wrap is off
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn load_ascii_wrap() {
|
|
||||||
let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd']
|
|
||||||
.map(move |c| c as u8);
|
|
||||||
let expected = Cp437Grid::load(5, 2, &chars);
|
|
||||||
|
|
||||||
let actual = Cp437Grid::load_ascii("HelloWorld", 5, true).unwrap();
|
|
||||||
// line break will be added
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn load_ascii_invalid() {
|
|
||||||
assert_eq!(
|
|
||||||
Err(InvalidCharError {
|
|
||||||
char: '🥶',
|
|
||||||
index: 2
|
|
||||||
}),
|
|
||||||
Cp437Grid::load_ascii("?#🥶42", 3, false)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[cfg(feature = "cp437")]
|
|
||||||
mod tests_feature_cp437 {
|
mod tests_feature_cp437 {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::CharGrid;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn round_trip_cp437() {
|
|
||||||
let utf8 = CharGrid::load(2, 2, &['Ä', 'x', '\n', '$']);
|
|
||||||
let cp437 = Cp437Grid::from(&utf8);
|
|
||||||
let actual = CharGrid::from(&cp437);
|
|
||||||
assert_eq!(actual, utf8);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn convert_str() {
|
fn convert_str() {
|
||||||
|
@ -245,13 +100,16 @@ mod tests_feature_cp437 {
|
||||||
│dx ≡ Σ √x²ⁿ·δx
|
│dx ≡ Σ √x²ⁿ·δx
|
||||||
⌡"#;
|
⌡"#;
|
||||||
|
|
||||||
let cp437 = str_to_cp437(utf8);
|
let cp437 = Cp437Converter::str_to_cp437(utf8);
|
||||||
let actual = cp437_to_str(&*cp437);
|
let actual = Cp437Converter::cp437_to_str(&*cp437);
|
||||||
assert_eq!(utf8, actual)
|
assert_eq!(utf8, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn convert_invalid() {
|
fn convert_invalid() {
|
||||||
assert_eq!(cp437_to_char(char_to_cp437('😜')), '?');
|
assert_eq!(
|
||||||
|
Cp437Converter::cp437_to_char(Cp437Converter::char_to_cp437('😜')),
|
||||||
|
'?'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
163
crates/servicepoint/src/cp437_grid.rs
Normal file
163
crates/servicepoint/src/cp437_grid.rs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
/// A grid containing codepage 437 characters.
|
||||||
|
///
|
||||||
|
/// The encoding is currently not enforced.
|
||||||
|
pub type Cp437Grid = crate::value_grid::ValueGrid<u8>;
|
||||||
|
|
||||||
|
/// The error occurring when loading an invalid character
|
||||||
|
#[derive(Debug, PartialEq, thiserror::Error)]
|
||||||
|
#[error(
|
||||||
|
"The character {char:?} at position {index} is not a valid CP437 character"
|
||||||
|
)]
|
||||||
|
pub struct InvalidCharError {
|
||||||
|
/// invalid character is at this position in input
|
||||||
|
index: usize,
|
||||||
|
/// the invalid character
|
||||||
|
char: char,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cp437Grid {
|
||||||
|
/// Load an ASCII-only [&str] into a [Cp437Grid] of specified width.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - for width == 0
|
||||||
|
/// - on empty strings
|
||||||
|
pub fn load_ascii(
|
||||||
|
value: &str,
|
||||||
|
width: usize,
|
||||||
|
wrap: bool,
|
||||||
|
) -> Result<Self, InvalidCharError> {
|
||||||
|
assert!(width > 0);
|
||||||
|
assert!(!value.is_empty());
|
||||||
|
|
||||||
|
let mut chars = {
|
||||||
|
let mut x = 0;
|
||||||
|
let mut y = 0;
|
||||||
|
|
||||||
|
for (index, char) in value.chars().enumerate() {
|
||||||
|
if !char.is_ascii() {
|
||||||
|
return Err(InvalidCharError { index, char });
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_lf = char == '\n';
|
||||||
|
if is_lf || (wrap && x == width) {
|
||||||
|
y += 1;
|
||||||
|
x = 0;
|
||||||
|
if is_lf {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cp437Grid::new(width, y + 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut x = 0;
|
||||||
|
let mut y = 0;
|
||||||
|
for char in value.chars().map(move |c| c as u8) {
|
||||||
|
let is_lf = char == b'\n';
|
||||||
|
if is_lf || (wrap && x == width) {
|
||||||
|
y += 1;
|
||||||
|
x = 0;
|
||||||
|
if is_lf {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if wrap || x < width {
|
||||||
|
chars.set(x, y, char);
|
||||||
|
}
|
||||||
|
x += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(chars)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::Grid;
|
||||||
|
#[allow(unused)] // depends on features
|
||||||
|
pub use feature_cp437::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "cp437")]
|
||||||
|
mod feature_cp437 {
|
||||||
|
use super::*;
|
||||||
|
use crate::{CharGrid, Cp437Converter};
|
||||||
|
|
||||||
|
impl From<&Cp437Grid> for CharGrid {
|
||||||
|
fn from(value: &Cp437Grid) -> Self {
|
||||||
|
value.map(Cp437Converter::cp437_to_char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Cp437Grid> for CharGrid {
|
||||||
|
fn from(value: Cp437Grid) -> Self {
|
||||||
|
Self::from(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&CharGrid> for Cp437Grid {
|
||||||
|
fn from(value: &CharGrid) -> Self {
|
||||||
|
value.map(Cp437Converter::char_to_cp437)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CharGrid> for Cp437Grid {
|
||||||
|
fn from(value: CharGrid) -> Self {
|
||||||
|
Self::from(&value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_ascii_nowrap() {
|
||||||
|
let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd']
|
||||||
|
.map(move |c| c as u8);
|
||||||
|
let expected = Cp437Grid::load(5, 2, &chars);
|
||||||
|
|
||||||
|
let actual = Cp437Grid::load_ascii("Hello,\nWorld!", 5, false).unwrap();
|
||||||
|
// comma will be removed because line is too long and wrap is off
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_ascii_wrap() {
|
||||||
|
let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd']
|
||||||
|
.map(move |c| c as u8);
|
||||||
|
let expected = Cp437Grid::load(5, 2, &chars);
|
||||||
|
|
||||||
|
let actual = Cp437Grid::load_ascii("HelloWorld", 5, true).unwrap();
|
||||||
|
// line break will be added
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_ascii_invalid() {
|
||||||
|
assert_eq!(
|
||||||
|
Err(InvalidCharError {
|
||||||
|
char: '🥶',
|
||||||
|
index: 2
|
||||||
|
}),
|
||||||
|
Cp437Grid::load_ascii("?#🥶42", 3, false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[cfg(feature = "cp437")]
|
||||||
|
mod tests_feature_cp437 {
|
||||||
|
use super::*;
|
||||||
|
use crate::CharGrid;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn round_trip_cp437() {
|
||||||
|
let utf8 = CharGrid::load(2, 2, &['Ä', 'x', '\n', '$']);
|
||||||
|
let cp437 = Cp437Grid::from(utf8.clone());
|
||||||
|
let actual = CharGrid::from(cp437);
|
||||||
|
assert_eq!(actual, utf8);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,14 +2,17 @@
|
||||||
//!
|
//!
|
||||||
//! Your starting point is a [Connection] to the display.
|
//! Your starting point is a [Connection] to the display.
|
||||||
//! With a connection, you can send [Command]s.
|
//! With a connection, you can send [Command]s.
|
||||||
//! When received, the display will update the state of the pixels.
|
//! When received, the display will update the state of its pixels.
|
||||||
//!
|
//!
|
||||||
//! # Examples
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ### Clear display
|
||||||
//! use servicepoint::{Command, CompressionCode, Grid, Bitmap};
|
|
||||||
//!
|
//!
|
||||||
//! let connection = servicepoint::Connection::open("127.0.0.1:2342")
|
//! ```rust
|
||||||
|
//! use servicepoint::{Connection, Command};
|
||||||
|
//!
|
||||||
|
//! // establish a connection
|
||||||
|
//! let connection = Connection::open("127.0.0.1:2342")
|
||||||
//! .expect("connection failed");
|
//! .expect("connection failed");
|
||||||
//!
|
//!
|
||||||
//! // turn off all pixels on display
|
//! // turn off all pixels on display
|
||||||
|
@ -17,6 +20,8 @@
|
||||||
//! .expect("send failed");
|
//! .expect("send failed");
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! ### Set all pixels to on
|
||||||
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! # use servicepoint::{Command, CompressionCode, Grid, Bitmap};
|
//! # use servicepoint::{Command, CompressionCode, Grid, Bitmap};
|
||||||
//! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
|
//! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
|
||||||
|
@ -34,114 +39,63 @@
|
||||||
//! // send command to display
|
//! // send command to display
|
||||||
//! connection.send(command).expect("send failed");
|
//! connection.send(command).expect("send failed");
|
||||||
//! ```
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### Send text
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # use servicepoint::{Command, CompressionCode, Grid, Bitmap, CharGrid};
|
||||||
|
//! # let connection = servicepoint::Connection::open("127.0.0.1:2342").expect("connection failed");
|
||||||
|
//! // create a text grid
|
||||||
|
//! let mut grid = CharGrid::from("Hello\nCCCB?");
|
||||||
|
//! // modify the grid
|
||||||
|
//! grid.set(grid.width() - 1, 1, '!');
|
||||||
|
//! // create the command to send the data
|
||||||
|
//! let command = Command::Utf8Data(servicepoint::Origin::ZERO, grid);
|
||||||
|
//! // send command to display
|
||||||
|
//! connection.send(command).expect("send failed");
|
||||||
|
//! ```
|
||||||
|
|
||||||
use std::time::Duration;
|
pub use crate::bit_vec::{bitvec, BitVec};
|
||||||
|
|
||||||
pub use bitvec;
|
|
||||||
|
|
||||||
pub use crate::bitmap::Bitmap;
|
pub use crate::bitmap::Bitmap;
|
||||||
pub use crate::brightness::{Brightness, BrightnessGrid};
|
pub use crate::brightness::Brightness;
|
||||||
|
pub use crate::brightness_grid::BrightnessGrid;
|
||||||
|
pub use crate::byte_grid::ByteGrid;
|
||||||
pub use crate::char_grid::CharGrid;
|
pub use crate::char_grid::CharGrid;
|
||||||
pub use crate::command::{Command, Offset};
|
pub use crate::command::{Command, Offset};
|
||||||
pub use crate::compression_code::CompressionCode;
|
pub use crate::compression_code::CompressionCode;
|
||||||
pub use crate::connection::Connection;
|
pub use crate::connection::Connection;
|
||||||
pub use crate::cp437::Cp437Grid;
|
pub use crate::constants::*;
|
||||||
|
pub use crate::cp437::Cp437Converter;
|
||||||
|
pub use crate::cp437_grid::Cp437Grid;
|
||||||
pub use crate::data_ref::DataRef;
|
pub use crate::data_ref::DataRef;
|
||||||
pub use crate::grid::Grid;
|
pub use crate::grid::Grid;
|
||||||
pub use crate::origin::{Origin, Pixels, Tiles};
|
pub use crate::origin::{Origin, Pixels, Tiles};
|
||||||
pub use crate::primitive_grid::{PrimitiveGrid, SeriesError};
|
pub use crate::packet::{Header, Packet, Payload};
|
||||||
|
pub use crate::value_grid::{
|
||||||
/// An alias for the specific type of [bitvec::prelude::BitVec] used.
|
IterGridRows, SetValueSeriesError, TryLoadValueGridError, Value, ValueGrid,
|
||||||
pub type BitVec = bitvec::prelude::BitVec<u8, bitvec::prelude::Msb0>;
|
};
|
||||||
|
|
||||||
|
mod bit_vec;
|
||||||
mod bitmap;
|
mod bitmap;
|
||||||
mod brightness;
|
mod brightness;
|
||||||
|
mod brightness_grid;
|
||||||
|
mod byte_grid;
|
||||||
mod char_grid;
|
mod char_grid;
|
||||||
mod command;
|
mod command;
|
||||||
mod command_code;
|
mod command_code;
|
||||||
mod compression;
|
mod compression;
|
||||||
mod compression_code;
|
mod compression_code;
|
||||||
mod connection;
|
mod connection;
|
||||||
pub mod cp437;
|
mod constants;
|
||||||
|
mod cp437_grid;
|
||||||
mod data_ref;
|
mod data_ref;
|
||||||
mod grid;
|
mod grid;
|
||||||
mod origin;
|
mod origin;
|
||||||
pub mod packet;
|
mod packet;
|
||||||
mod primitive_grid;
|
mod value_grid;
|
||||||
|
|
||||||
/// size of a single tile in one dimension
|
#[cfg(feature = "cp437")]
|
||||||
pub const TILE_SIZE: usize = 8;
|
mod cp437;
|
||||||
|
|
||||||
/// Display tile count in the x-direction
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
|
|
||||||
/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
/// ```
|
|
||||||
pub const TILE_WIDTH: usize = 56;
|
|
||||||
|
|
||||||
/// Display tile count in the y-direction
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use servicepoint::{Cp437Grid, TILE_HEIGHT, TILE_WIDTH};
|
|
||||||
/// let grid = Cp437Grid::new(TILE_WIDTH, TILE_HEIGHT);
|
|
||||||
/// ```
|
|
||||||
pub const TILE_HEIGHT: usize = 20;
|
|
||||||
|
|
||||||
/// Display width in pixels
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
|
|
||||||
/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
|
|
||||||
/// ```
|
|
||||||
pub const PIXEL_WIDTH: usize = TILE_WIDTH * TILE_SIZE;
|
|
||||||
|
|
||||||
/// Display height in pixels
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH, Bitmap};
|
|
||||||
/// let grid = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
|
|
||||||
/// ```
|
|
||||||
pub const PIXEL_HEIGHT: usize = TILE_HEIGHT * TILE_SIZE;
|
|
||||||
|
|
||||||
/// pixel count on whole screen
|
|
||||||
pub const PIXEL_COUNT: usize = PIXEL_WIDTH * PIXEL_HEIGHT;
|
|
||||||
|
|
||||||
/// Actual hardware limit is around 28-29ms/frame. Rounded up for less dropped packets.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # use std::time::Instant;
|
|
||||||
/// # use servicepoint::{Command, CompressionCode, FRAME_PACING, Origin, Bitmap};
|
|
||||||
/// # let connection = servicepoint::Connection::Fake;
|
|
||||||
/// # let pixels = Bitmap::max_sized();
|
|
||||||
/// loop {
|
|
||||||
/// let start = Instant::now();
|
|
||||||
///
|
|
||||||
/// // Change pixels here
|
|
||||||
///
|
|
||||||
/// connection.send(Command::BitmapLinearWin(
|
|
||||||
/// Origin::new(0,0),
|
|
||||||
/// pixels,
|
|
||||||
/// CompressionCode::Lzma
|
|
||||||
/// ))
|
|
||||||
/// .expect("send failed");
|
|
||||||
///
|
|
||||||
/// // warning: will crash if resulting duration is negative, e.g. when resuming from standby
|
|
||||||
/// std::thread::sleep(FRAME_PACING - start.elapsed());
|
|
||||||
/// # break; // prevent doctest from hanging
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub const FRAME_PACING: Duration = Duration::from_millis(30);
|
|
||||||
|
|
||||||
// include README.md in doctest
|
// include README.md in doctest
|
||||||
#[doc = include_str!("../README.md")]
|
#[doc = include_str!("../README.md")]
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
//! Converting a packet to a command and back:
|
//! Converting a packet to a command and back:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use servicepoint::{Command, packet::Packet};
|
//! use servicepoint::{Command, Packet};
|
||||||
//! # let command = Command::Clear;
|
//! # let command = Command::Clear;
|
||||||
//! let packet: Packet = command.into();
|
//! let packet: Packet = command.into();
|
||||||
//! let command: Command = Command::try_from(packet).expect("could not read command from packet");
|
//! let command: Command = Command::try_from(packet).expect("could not read command from packet");
|
||||||
|
@ -16,20 +16,20 @@
|
||||||
//! Converting a packet to bytes and back:
|
//! Converting a packet to bytes and back:
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use servicepoint::{Command, packet::Packet};
|
//! use servicepoint::{Command, Packet};
|
||||||
//! # let command = Command::Clear;
|
//! # let command = Command::Clear;
|
||||||
//! # let packet: Packet = command.into();
|
//! # let packet: Packet = command.into();
|
||||||
//! let bytes: Vec<u8> = packet.into();
|
//! let bytes: Vec<u8> = packet.into();
|
||||||
//! let packet = Packet::try_from(bytes).expect("could not read packet from bytes");
|
//! let packet = Packet::try_from(bytes).expect("could not read packet from bytes");
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::mem::size_of;
|
use crate::command_code::CommandCode;
|
||||||
|
|
||||||
use crate::compression::into_compressed;
|
use crate::compression::into_compressed;
|
||||||
use crate::{
|
use crate::{
|
||||||
command_code::CommandCode, Bitmap, Command, CompressionCode, Grid, Offset,
|
Bitmap, Command, CompressionCode, Grid, Offset, Origin, Pixels, Tiles,
|
||||||
Origin, Pixels, Tiles, TILE_SIZE,
|
TILE_SIZE,
|
||||||
};
|
};
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
/// A raw header.
|
/// A raw header.
|
||||||
///
|
///
|
||||||
|
@ -209,6 +209,9 @@ impl From<Command> for Packet {
|
||||||
grid,
|
grid,
|
||||||
CommandCode::Cp437Data,
|
CommandCode::Cp437Data,
|
||||||
),
|
),
|
||||||
|
Command::Utf8Data(origin, grid) => {
|
||||||
|
Self::origin_grid_to_packet(origin, grid, CommandCode::Utf8Data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::slice::{Iter, IterMut};
|
use std::slice::{Iter, IterMut};
|
||||||
|
|
||||||
use crate::{DataRef, Grid};
|
use crate::*;
|
||||||
|
|
||||||
pub trait PrimitiveGridType: Sized + Default + Copy + Clone {}
|
/// A type that can be stored in a [ValueGrid], e.g. [char], [u8].
|
||||||
impl<T: Sized + Default + Copy + Clone> PrimitiveGridType for T {}
|
pub trait Value: Sized + Default + Copy + Clone + Debug {}
|
||||||
|
impl<T: Sized + Default + Copy + Clone + Debug> Value for T {}
|
||||||
|
|
||||||
/// A 2D grid of bytes
|
/// A 2D grid of values.
|
||||||
|
///
|
||||||
|
/// The memory layout is the one the display expects in [Command]s.
|
||||||
|
///
|
||||||
|
/// This structure can be used with any type that implements the [Value] trait.
|
||||||
|
/// You can also use the concrete type aliases provided in this crate, e.g. [CharGrid] and [ByteGrid].
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct PrimitiveGrid<T: PrimitiveGridType> {
|
pub struct ValueGrid<T: Value> {
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
data: Vec<T>,
|
data: Vec<T>,
|
||||||
|
@ -15,7 +22,7 @@ pub struct PrimitiveGrid<T: PrimitiveGridType> {
|
||||||
|
|
||||||
/// Error type for methods that change a whole column or row at once
|
/// Error type for methods that change a whole column or row at once
|
||||||
#[derive(thiserror::Error, Debug, PartialEq)]
|
#[derive(thiserror::Error, Debug, PartialEq)]
|
||||||
pub enum SeriesError {
|
pub enum SetValueSeriesError {
|
||||||
#[error("The index {index} was out of bounds for size {size}")]
|
#[error("The index {index} was out of bounds for size {size}")]
|
||||||
/// The index {index} was out of bounds for size {size}
|
/// The index {index} was out of bounds for size {size}
|
||||||
OutOfBounds {
|
OutOfBounds {
|
||||||
|
@ -34,15 +41,15 @@ pub enum SeriesError {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
impl<T: Value> ValueGrid<T> {
|
||||||
/// Creates a new [PrimitiveGrid] with the specified dimensions.
|
/// Creates a new [ValueGrid] with the specified dimensions.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// - width: size in x-direction
|
/// - width: size in x-direction
|
||||||
/// - height: size in y-direction
|
/// - height: size in y-direction
|
||||||
///
|
///
|
||||||
/// returns: [PrimitiveGrid] initialized to default value.
|
/// returns: [ValueGrid] initialized to default value.
|
||||||
pub fn new(width: usize, height: usize) -> Self {
|
pub fn new(width: usize, height: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: vec![Default::default(); width * height],
|
data: vec![Default::default(); width * height],
|
||||||
|
@ -51,16 +58,20 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a [PrimitiveGrid] with the specified dimensions from the provided data.
|
/// Loads a [ValueGrid] with the specified dimensions from the provided data.
|
||||||
///
|
///
|
||||||
/// returns: [PrimitiveGrid] that contains a copy of the provided data
|
/// returns: [ValueGrid] that contains a copy of the provided data
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// - when the dimensions and data size do not match exactly.
|
/// - when the dimensions and data size do not match exactly.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn load(width: usize, height: usize, data: &[T]) -> Self {
|
pub fn load(width: usize, height: usize, data: &[T]) -> Self {
|
||||||
assert_eq!(width * height, data.len());
|
assert_eq!(
|
||||||
|
width * height,
|
||||||
|
data.len(),
|
||||||
|
"dimension mismatch for data {data:?}"
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
data: Vec::from(data),
|
data: Vec::from(data),
|
||||||
width,
|
width,
|
||||||
|
@ -68,12 +79,52 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all cells in [PrimitiveGrid].
|
/// Loads a [ValueGrid] with the specified width from the provided data, wrapping to as many rows as needed.
|
||||||
|
///
|
||||||
|
/// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use servicepoint::ValueGrid;
|
||||||
|
/// let grid = ValueGrid::wrap(2, &[0, 1, 2, 3, 4, 5]).unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn wrap(
|
||||||
|
width: usize,
|
||||||
|
data: &[T],
|
||||||
|
) -> Result<Self, TryLoadValueGridError> {
|
||||||
|
let len = data.len();
|
||||||
|
if len % width != 0 {
|
||||||
|
return Err(TryLoadValueGridError::InvalidDimensions);
|
||||||
|
}
|
||||||
|
Ok(Self::load(width, len / width, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a [ValueGrid] with the specified dimensions from the provided data.
|
||||||
|
///
|
||||||
|
/// returns: [ValueGrid] that contains a copy of the provided data or [TryLoadValueGridError].
|
||||||
|
pub fn try_load(
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
data: Vec<T>,
|
||||||
|
) -> Result<Self, TryLoadValueGridError> {
|
||||||
|
if width * height != data.len() {
|
||||||
|
return Err(TryLoadValueGridError::InvalidDimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
data,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over all cells in [ValueGrid].
|
||||||
///
|
///
|
||||||
/// Order is equivalent to the following loop:
|
/// Order is equivalent to the following loop:
|
||||||
/// ```
|
/// ```
|
||||||
/// # use servicepoint::{PrimitiveGrid, Grid};
|
/// # use servicepoint::{ByteGrid, Grid};
|
||||||
/// # let grid = PrimitiveGrid::<u8>::new(2,2);
|
/// # let grid = ByteGrid::new(2,2);
|
||||||
/// for y in 0..grid.height() {
|
/// for y in 0..grid.height() {
|
||||||
/// for x in 0..grid.width() {
|
/// for x in 0..grid.width() {
|
||||||
/// grid.get(x, y);
|
/// grid.get(x, y);
|
||||||
|
@ -84,9 +135,9 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
self.data.iter()
|
self.data.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all rows in [PrimitiveGrid] top to bottom.
|
/// Iterate over all rows in [ValueGrid] top to bottom.
|
||||||
pub fn iter_rows(&self) -> IterRows<T> {
|
pub fn iter_rows(&self) -> IterGridRows<T> {
|
||||||
IterRows {
|
IterGridRows {
|
||||||
byte_grid: self,
|
byte_grid: self,
|
||||||
row: 0,
|
row: 0,
|
||||||
}
|
}
|
||||||
|
@ -132,7 +183,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert between PrimitiveGrid types.
|
/// Convert between ValueGrid types.
|
||||||
///
|
///
|
||||||
/// See also [Iterator::map].
|
/// See also [Iterator::map].
|
||||||
///
|
///
|
||||||
|
@ -140,18 +191,18 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
///
|
///
|
||||||
/// Use logic written for u8s and then convert to [Brightness] values for sending in a [Command].
|
/// Use logic written for u8s and then convert to [Brightness] values for sending in a [Command].
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn foo(grid: &mut PrimitiveGrid<u8>) {}
|
/// # fn foo(grid: &mut ByteGrid) {}
|
||||||
/// # use servicepoint::{Brightness, BrightnessGrid, Command, Origin, PrimitiveGrid, TILE_HEIGHT, TILE_WIDTH};
|
/// # use servicepoint::{Brightness, BrightnessGrid, ByteGrid, Command, Origin, TILE_HEIGHT, TILE_WIDTH};
|
||||||
/// let mut grid: PrimitiveGrid<u8> = PrimitiveGrid::new(TILE_WIDTH, TILE_HEIGHT);
|
/// let mut grid: ByteGrid = ByteGrid::new(TILE_WIDTH, TILE_HEIGHT);
|
||||||
/// foo(&mut grid);
|
/// foo(&mut grid);
|
||||||
/// let grid: BrightnessGrid = grid.map(Brightness::saturating_from);
|
/// let grid: BrightnessGrid = grid.map(Brightness::saturating_from);
|
||||||
/// let command = Command::CharBrightness(Origin::ZERO, grid);
|
/// let command = Command::CharBrightness(Origin::ZERO, grid);
|
||||||
/// ```
|
/// ```
|
||||||
/// [Brightness]: [crate::Brightness]
|
/// [Brightness]: [crate::Brightness]
|
||||||
/// [Command]: [crate::Command]
|
/// [Command]: [crate::Command]
|
||||||
pub fn map<TConverted, F>(&self, f: F) -> PrimitiveGrid<TConverted>
|
pub fn map<TConverted, F>(&self, f: F) -> ValueGrid<TConverted>
|
||||||
where
|
where
|
||||||
TConverted: PrimitiveGridType,
|
TConverted: Value,
|
||||||
F: Fn(T) -> TConverted,
|
F: Fn(T) -> TConverted,
|
||||||
{
|
{
|
||||||
let data = self
|
let data = self
|
||||||
|
@ -159,7 +210,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|elem| f(*elem))
|
.map(|elem| f(*elem))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
PrimitiveGrid::load(self.width(), self.height(), &data)
|
ValueGrid::load(self.width(), self.height(), &data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copies a row from the grid.
|
/// Copies a row from the grid.
|
||||||
|
@ -185,9 +236,13 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
/// Overwrites a column in the grid.
|
/// Overwrites a column in the grid.
|
||||||
///
|
///
|
||||||
/// Returns [Err] if x is out of bounds or `col` is not of the correct size.
|
/// Returns [Err] if x is out of bounds or `col` is not of the correct size.
|
||||||
pub fn set_col(&mut self, x: usize, col: &[T]) -> Result<(), SeriesError> {
|
pub fn set_col(
|
||||||
|
&mut self,
|
||||||
|
x: usize,
|
||||||
|
col: &[T],
|
||||||
|
) -> Result<(), SetValueSeriesError> {
|
||||||
if col.len() != self.height() {
|
if col.len() != self.height() {
|
||||||
return Err(SeriesError::InvalidLength {
|
return Err(SetValueSeriesError::InvalidLength {
|
||||||
expected: self.height(),
|
expected: self.height(),
|
||||||
actual: col.len(),
|
actual: col.len(),
|
||||||
});
|
});
|
||||||
|
@ -204,7 +259,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(SeriesError::OutOfBounds {
|
Err(SetValueSeriesError::OutOfBounds {
|
||||||
index: x,
|
index: x,
|
||||||
size: width,
|
size: width,
|
||||||
})
|
})
|
||||||
|
@ -214,10 +269,14 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
/// Overwrites a row in the grid.
|
/// Overwrites a row in the grid.
|
||||||
///
|
///
|
||||||
/// Returns [Err] if y is out of bounds or `row` is not of the correct size.
|
/// Returns [Err] if y is out of bounds or `row` is not of the correct size.
|
||||||
pub fn set_row(&mut self, y: usize, row: &[T]) -> Result<(), SeriesError> {
|
pub fn set_row(
|
||||||
|
&mut self,
|
||||||
|
y: usize,
|
||||||
|
row: &[T],
|
||||||
|
) -> Result<(), SetValueSeriesError> {
|
||||||
let width = self.width();
|
let width = self.width();
|
||||||
if row.len() != width {
|
if row.len() != width {
|
||||||
return Err(SeriesError::InvalidLength {
|
return Err(SetValueSeriesError::InvalidLength {
|
||||||
expected: width,
|
expected: width,
|
||||||
actual: row.len(),
|
actual: row.len(),
|
||||||
});
|
});
|
||||||
|
@ -226,7 +285,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
let chunk = match self.data.chunks_exact_mut(width).nth(y) {
|
let chunk = match self.data.chunks_exact_mut(width).nth(y) {
|
||||||
Some(row) => row,
|
Some(row) => row,
|
||||||
None => {
|
None => {
|
||||||
return Err(SeriesError::OutOfBounds {
|
return Err(SetValueSeriesError::OutOfBounds {
|
||||||
size: self.height(),
|
size: self.height(),
|
||||||
index: y,
|
index: y,
|
||||||
})
|
})
|
||||||
|
@ -238,8 +297,16 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> {
|
/// Errors that can occur when loading a grid
|
||||||
/// Sets the value of the cell at the specified position in the `PrimitiveGrid.
|
#[derive(Debug, thiserror::Error, PartialEq)]
|
||||||
|
pub enum TryLoadValueGridError {
|
||||||
|
#[error("The provided dimensions do not match with the data size")]
|
||||||
|
/// The provided dimensions do not match with the data size
|
||||||
|
InvalidDimensions,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Value> Grid<T> for ValueGrid<T> {
|
||||||
|
/// Sets the value of the cell at the specified position in the `ValueGrid.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
|
@ -281,7 +348,7 @@ impl<T: PrimitiveGridType> Grid<T> for PrimitiveGrid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PrimitiveGridType> DataRef<T> for PrimitiveGrid<T> {
|
impl<T: Value> DataRef<T> for ValueGrid<T> {
|
||||||
/// Get the underlying byte rows mutable
|
/// Get the underlying byte rows mutable
|
||||||
fn data_ref_mut(&mut self) -> &mut [T] {
|
fn data_ref_mut(&mut self) -> &mut [T] {
|
||||||
self.data.as_mut_slice()
|
self.data.as_mut_slice()
|
||||||
|
@ -293,19 +360,20 @@ impl<T: PrimitiveGridType> DataRef<T> for PrimitiveGrid<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PrimitiveGridType> From<PrimitiveGrid<T>> for Vec<T> {
|
impl<T: Value> From<ValueGrid<T>> for Vec<T> {
|
||||||
/// Turn into the underlying [`Vec<u8>`] containing the rows of bytes.
|
/// Turn into the underlying [`Vec<u8>`] containing the rows of bytes.
|
||||||
fn from(value: PrimitiveGrid<T>) -> Self {
|
fn from(value: ValueGrid<T>) -> Self {
|
||||||
value.data
|
value.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IterRows<'t, T: PrimitiveGridType> {
|
/// An iterator iver the rows in a [ValueGrid]
|
||||||
byte_grid: &'t PrimitiveGrid<T>,
|
pub struct IterGridRows<'t, T: Value> {
|
||||||
|
byte_grid: &'t ValueGrid<T>,
|
||||||
row: usize,
|
row: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> {
|
impl<'t, T: Value> Iterator for IterGridRows<'t, T> {
|
||||||
type Item = Iter<'t, T>;
|
type Item = Iter<'t, T>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
@ -323,11 +391,14 @@ impl<'t, T: PrimitiveGridType> Iterator for IterRows<'t, T> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{DataRef, Grid, PrimitiveGrid, SeriesError};
|
use crate::{
|
||||||
|
value_grid::{SetValueSeriesError, ValueGrid},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fill() {
|
fn fill() {
|
||||||
let mut grid = PrimitiveGrid::<usize>::new(2, 2);
|
let mut grid = ValueGrid::<usize>::new(2, 2);
|
||||||
assert_eq!(grid.data, [0x00, 0x00, 0x00, 0x00]);
|
assert_eq!(grid.data, [0x00, 0x00, 0x00, 0x00]);
|
||||||
|
|
||||||
grid.fill(42);
|
grid.fill(42);
|
||||||
|
@ -336,7 +407,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_set() {
|
fn get_set() {
|
||||||
let mut grid = PrimitiveGrid::new(2, 2);
|
let mut grid = ValueGrid::new(2, 2);
|
||||||
assert_eq!(grid.get(0, 0), 0);
|
assert_eq!(grid.get(0, 0), 0);
|
||||||
assert_eq!(grid.get(1, 1), 0);
|
assert_eq!(grid.get(1, 1), 0);
|
||||||
|
|
||||||
|
@ -351,7 +422,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load() {
|
fn load() {
|
||||||
let mut grid = PrimitiveGrid::new(2, 3);
|
let mut grid = ValueGrid::new(2, 3);
|
||||||
for x in 0..grid.width {
|
for x in 0..grid.width {
|
||||||
for y in 0..grid.height {
|
for y in 0..grid.height {
|
||||||
grid.set(x, y, (x + y) as u8);
|
grid.set(x, y, (x + y) as u8);
|
||||||
|
@ -362,13 +433,13 @@ mod tests {
|
||||||
|
|
||||||
let data: Vec<u8> = grid.into();
|
let data: Vec<u8> = grid.into();
|
||||||
|
|
||||||
let grid = PrimitiveGrid::load(2, 3, &data);
|
let grid = ValueGrid::load(2, 3, &data);
|
||||||
assert_eq!(grid.data, [0, 1, 1, 2, 2, 3]);
|
assert_eq!(grid.data, [0, 1, 1, 2, 2, 3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mut_data_ref() {
|
fn mut_data_ref() {
|
||||||
let mut vec = PrimitiveGrid::new(2, 2);
|
let mut vec = ValueGrid::new(2, 2);
|
||||||
|
|
||||||
let data_ref = vec.data_ref_mut();
|
let data_ref = vec.data_ref_mut();
|
||||||
data_ref.copy_from_slice(&[1, 2, 3, 4]);
|
data_ref.copy_from_slice(&[1, 2, 3, 4]);
|
||||||
|
@ -379,7 +450,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn iter() {
|
fn iter() {
|
||||||
let mut vec = PrimitiveGrid::new(2, 2);
|
let mut vec = ValueGrid::new(2, 2);
|
||||||
vec.set(1, 1, 5);
|
vec.set(1, 1, 5);
|
||||||
|
|
||||||
let mut iter = vec.iter();
|
let mut iter = vec.iter();
|
||||||
|
@ -391,7 +462,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn iter_mut() {
|
fn iter_mut() {
|
||||||
let mut vec = PrimitiveGrid::new(2, 3);
|
let mut vec = ValueGrid::new(2, 3);
|
||||||
for (index, cell) in vec.iter_mut().enumerate() {
|
for (index, cell) in vec.iter_mut().enumerate() {
|
||||||
*cell = index as u8;
|
*cell = index as u8;
|
||||||
}
|
}
|
||||||
|
@ -401,7 +472,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn iter_rows() {
|
fn iter_rows() {
|
||||||
let vec = PrimitiveGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]);
|
let vec = ValueGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]);
|
||||||
for (y, row) in vec.iter_rows().enumerate() {
|
for (y, row) in vec.iter_rows().enumerate() {
|
||||||
for (x, val) in row.enumerate() {
|
for (x, val) in row.enumerate() {
|
||||||
assert_eq!(*val, (x + y) as u8);
|
assert_eq!(*val, (x + y) as u8);
|
||||||
|
@ -412,20 +483,20 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn out_of_bounds_x() {
|
fn out_of_bounds_x() {
|
||||||
let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]);
|
let mut vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]);
|
||||||
vec.set(2, 1, 5);
|
vec.set(2, 1, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn out_of_bounds_y() {
|
fn out_of_bounds_y() {
|
||||||
let vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]);
|
let vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]);
|
||||||
vec.get(1, 2);
|
vec.get(1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ref_mut() {
|
fn ref_mut() {
|
||||||
let mut vec = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]);
|
let mut vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]);
|
||||||
|
|
||||||
let top_left = vec.get_ref_mut(0, 0);
|
let top_left = vec.get_ref_mut(0, 0);
|
||||||
*top_left += 5;
|
*top_left += 5;
|
||||||
|
@ -436,7 +507,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn optional() {
|
fn optional() {
|
||||||
let mut grid = PrimitiveGrid::load(2, 2, &[0, 1, 2, 3]);
|
let mut grid = ValueGrid::load(2, 2, &[0, 1, 2, 3]);
|
||||||
grid.set_optional(0, 0, 5);
|
grid.set_optional(0, 0, 5);
|
||||||
grid.set_optional(-1, 0, 8);
|
grid.set_optional(-1, 0, 8);
|
||||||
grid.set_optional(0, 8, 42);
|
grid.set_optional(0, 8, 42);
|
||||||
|
@ -448,18 +519,18 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn col() {
|
fn col() {
|
||||||
let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]);
|
let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]);
|
||||||
assert_eq!(grid.get_col(0), Some(vec![0, 2, 4]));
|
assert_eq!(grid.get_col(0), Some(vec![0, 2, 4]));
|
||||||
assert_eq!(grid.get_col(1), Some(vec![1, 3, 5]));
|
assert_eq!(grid.get_col(1), Some(vec![1, 3, 5]));
|
||||||
assert_eq!(grid.get_col(2), None);
|
assert_eq!(grid.get_col(2), None);
|
||||||
assert_eq!(grid.set_col(0, &[5, 7, 9]), Ok(()));
|
assert_eq!(grid.set_col(0, &[5, 7, 9]), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
grid.set_col(2, &[5, 7, 9]),
|
grid.set_col(2, &[5, 7, 9]),
|
||||||
Err(SeriesError::OutOfBounds { size: 2, index: 2 })
|
Err(SetValueSeriesError::OutOfBounds { size: 2, index: 2 })
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
grid.set_col(0, &[5, 7]),
|
grid.set_col(0, &[5, 7]),
|
||||||
Err(SeriesError::InvalidLength {
|
Err(SetValueSeriesError::InvalidLength {
|
||||||
expected: 3,
|
expected: 3,
|
||||||
actual: 2
|
actual: 2
|
||||||
})
|
})
|
||||||
|
@ -469,7 +540,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn row() {
|
fn row() {
|
||||||
let mut grid = PrimitiveGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]);
|
let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]);
|
||||||
assert_eq!(grid.get_row(0), Some(vec![0, 1]));
|
assert_eq!(grid.get_row(0), Some(vec![0, 1]));
|
||||||
assert_eq!(grid.get_row(2), Some(vec![4, 5]));
|
assert_eq!(grid.get_row(2), Some(vec![4, 5]));
|
||||||
assert_eq!(grid.get_row(3), None);
|
assert_eq!(grid.get_row(3), None);
|
||||||
|
@ -477,14 +548,23 @@ mod tests {
|
||||||
assert_eq!(grid.get_row(0), Some(vec![5, 7]));
|
assert_eq!(grid.get_row(0), Some(vec![5, 7]));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
grid.set_row(3, &[5, 7]),
|
grid.set_row(3, &[5, 7]),
|
||||||
Err(SeriesError::OutOfBounds { size: 3, index: 3 })
|
Err(SetValueSeriesError::OutOfBounds { size: 3, index: 3 })
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
grid.set_row(2, &[5, 7, 3]),
|
grid.set_row(2, &[5, 7, 3]),
|
||||||
Err(SeriesError::InvalidLength {
|
Err(SetValueSeriesError::InvalidLength {
|
||||||
expected: 2,
|
expected: 2,
|
||||||
actual: 3
|
actual: 3
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wrap() {
|
||||||
|
let grid = ValueGrid::wrap(2, &[0, 1, 2, 3, 4, 5]).unwrap();
|
||||||
|
assert_eq!(grid.height(), 3);
|
||||||
|
|
||||||
|
let grid = ValueGrid::wrap(4, &[0, 1, 2, 3, 4, 5]);
|
||||||
|
assert_eq!(grid.err(), Some(TryLoadValueGridError::InvalidDimensions));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
cbindgen = "0.27.0"
|
cbindgen = "0.27.0"
|
||||||
|
|
||||||
[dependencies.servicepoint]
|
[dependencies.servicepoint]
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
path = "../servicepoint"
|
path = "../servicepoint"
|
||||||
features = ["all_compressions"]
|
features = ["all_compressions"]
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ publish = false
|
||||||
test = false
|
test = false
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0"
|
cc = "1.2"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
servicepoint_binding_c = { path = "../.." }
|
servicepoint_binding_c = { path = "../.." }
|
||||||
|
|
|
@ -14,12 +14,12 @@
|
||||||
#define SP_BRIGHTNESS_LEVELS 12
|
#define SP_BRIGHTNESS_LEVELS 12
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* see [Brightness::MAX]
|
* see [servicepoint::Brightness::MAX]
|
||||||
*/
|
*/
|
||||||
#define SP_BRIGHTNESS_MAX 11
|
#define SP_BRIGHTNESS_MAX 11
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* see [Brightness::MIN]
|
* see [servicepoint::Brightness::MIN]
|
||||||
*/
|
*/
|
||||||
#define SP_BRIGHTNESS_MIN 0
|
#define SP_BRIGHTNESS_MIN 0
|
||||||
|
|
||||||
|
@ -131,6 +131,25 @@ typedef struct SPBitmap SPBitmap;
|
||||||
*/
|
*/
|
||||||
typedef struct SPBrightnessGrid SPBrightnessGrid;
|
typedef struct SPBrightnessGrid SPBrightnessGrid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A C-wrapper for grid containing UTF-8 characters.
|
||||||
|
*
|
||||||
|
* As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers.
|
||||||
|
*
|
||||||
|
* The encoding is enforced in most cases by the rust standard library
|
||||||
|
* and will panic when provided with illegal characters.
|
||||||
|
*
|
||||||
|
* # Examples
|
||||||
|
*
|
||||||
|
* ```C
|
||||||
|
* CharGrid grid = sp_char_grid_new(4, 3);
|
||||||
|
* sp_char_grid_fill(grid, '?');
|
||||||
|
* sp_char_grid_set(grid, 0, 0, '!');
|
||||||
|
* sp_char_grid_free(grid);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
typedef struct SPCharGrid SPCharGrid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A low-level display command.
|
* A low-level display command.
|
||||||
*
|
*
|
||||||
|
@ -367,6 +386,20 @@ SPBitmap *sp_bitmap_load(size_t width,
|
||||||
SPBitmap *sp_bitmap_new(size_t width,
|
SPBitmap *sp_bitmap_new(size_t width,
|
||||||
size_t height);
|
size_t height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new [SPBitmap] with a size matching the screen.
|
||||||
|
*
|
||||||
|
* returns: [SPBitmap] initialized to all pixels off. Will never return NULL.
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - the returned instance is freed in some way, either by using a consuming function or
|
||||||
|
* by explicitly calling [sp_bitmap_free].
|
||||||
|
*/
|
||||||
|
SPBitmap *sp_bitmap_new_screen_sized(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value of the specified position in the [SPBitmap].
|
* Sets the value of the specified position in the [SPBitmap].
|
||||||
*
|
*
|
||||||
|
@ -865,6 +898,196 @@ SPByteSlice sp_brightness_grid_unsafe_data_ref(SPBrightnessGrid *brightness_grid
|
||||||
*/
|
*/
|
||||||
size_t sp_brightness_grid_width(const SPBrightnessGrid *brightness_grid);
|
size_t sp_brightness_grid_width(const SPBrightnessGrid *brightness_grid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones a [SPCharGrid].
|
||||||
|
*
|
||||||
|
* Will never return NULL.
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `char_grid` is NULL
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
* - `char_grid` is not written to concurrently
|
||||||
|
* - the returned instance is freed in some way, either by using a consuming function or
|
||||||
|
* by explicitly calling `sp_char_grid_free`.
|
||||||
|
*/
|
||||||
|
SPCharGrid *sp_char_grid_clone(const SPCharGrid *char_grid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of all cells in the [SPCharGrid].
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
*
|
||||||
|
* - `char_grid`: instance to write to
|
||||||
|
* - `value`: the value to set all cells to
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `char_grid` is NULL
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
* - `char_grid` is not written to or read from concurrently
|
||||||
|
*/
|
||||||
|
void sp_char_grid_fill(SPCharGrid *char_grid, uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deallocates a [SPCharGrid].
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `char_grid` is NULL
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
* - `char_grid` is not used concurrently or after char_grid call
|
||||||
|
* - `char_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
|
||||||
|
*
|
||||||
|
* [SPCommand]: [crate::SPCommand]
|
||||||
|
*/
|
||||||
|
void sp_char_grid_free(SPCharGrid *char_grid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current value at the specified position.
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
*
|
||||||
|
* - `char_grid`: instance to read from
|
||||||
|
* - `x` and `y`: position of the cell to read
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `char_grid` is NULL
|
||||||
|
* - when accessing `x` or `y` out of bounds
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
* - `char_grid` is not written to concurrently
|
||||||
|
*/
|
||||||
|
uint32_t sp_char_grid_get(const SPCharGrid *char_grid, size_t x, size_t y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the height of the [SPCharGrid] instance.
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
*
|
||||||
|
* - `char_grid`: instance to read from
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `char_grid` is NULL
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
*/
|
||||||
|
size_t sp_char_grid_height(const SPCharGrid *char_grid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a [SPCharGrid] with the specified dimensions from the provided data.
|
||||||
|
*
|
||||||
|
* Will never return NULL.
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `data` is NULL
|
||||||
|
* - when the provided `data_length` does not match `height` and `width`
|
||||||
|
* - when `data` is not valid UTF-8
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `data` points to a valid memory location of at least `data_length`
|
||||||
|
* bytes in size.
|
||||||
|
* - the returned instance is freed in some way, either by using a consuming function or
|
||||||
|
* by explicitly calling `sp_char_grid_free`.
|
||||||
|
*/
|
||||||
|
SPCharGrid *sp_char_grid_load(size_t width,
|
||||||
|
size_t height,
|
||||||
|
const uint8_t *data,
|
||||||
|
size_t data_length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new [SPCharGrid] with the specified dimensions.
|
||||||
|
*
|
||||||
|
* returns: [SPCharGrid] initialized to 0. Will never return NULL.
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - the returned instance is freed in some way, either by using a consuming function or
|
||||||
|
* by explicitly calling `sp_char_grid_free`.
|
||||||
|
*/
|
||||||
|
SPCharGrid *sp_char_grid_new(size_t width,
|
||||||
|
size_t height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the specified position in the [SPCharGrid].
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
*
|
||||||
|
* - `char_grid`: instance to write to
|
||||||
|
* - `x` and `y`: position of the cell
|
||||||
|
* - `value`: the value to write to the cell
|
||||||
|
*
|
||||||
|
* returns: old value of the cell
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `char_grid` is NULL
|
||||||
|
* - when accessing `x` or `y` out of bounds
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `char_grid` points to a valid [SPBitVec]
|
||||||
|
* - `char_grid` is not written to or read from concurrently
|
||||||
|
*
|
||||||
|
* [SPBitVec]: [crate::SPBitVec]
|
||||||
|
*/
|
||||||
|
void sp_char_grid_set(SPCharGrid *char_grid,
|
||||||
|
size_t x,
|
||||||
|
size_t y,
|
||||||
|
uint32_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the width of the [SPCharGrid] instance.
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
*
|
||||||
|
* - `char_grid`: instance to read from
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `char_grid` is NULL
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
*/
|
||||||
|
size_t sp_char_grid_width(const SPCharGrid *char_grid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set pixel data starting at the pixel offset on screen.
|
* Set pixel data starting at the pixel offset on screen.
|
||||||
*
|
*
|
||||||
|
@ -1101,7 +1324,7 @@ SPCommand *sp_command_clear(void);
|
||||||
SPCommand *sp_command_clone(const SPCommand *command);
|
SPCommand *sp_command_clone(const SPCommand *command);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show text on the screen.
|
* Show codepage 437 encoded text on the screen.
|
||||||
*
|
*
|
||||||
* The passed [SPCp437Grid] gets consumed.
|
* The passed [SPCp437Grid] gets consumed.
|
||||||
*
|
*
|
||||||
|
@ -1201,6 +1424,30 @@ SPCommand *sp_command_hard_reset(void);
|
||||||
*/
|
*/
|
||||||
SPCommand *sp_command_try_from_packet(SPPacket *packet);
|
SPCommand *sp_command_try_from_packet(SPPacket *packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show UTF-8 encoded text on the screen.
|
||||||
|
*
|
||||||
|
* The passed [SPCharGrid] gets consumed.
|
||||||
|
*
|
||||||
|
* Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL.
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `grid` is null
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `grid` points to a valid instance of [SPCharGrid]
|
||||||
|
* - `grid` is not used concurrently or after this call
|
||||||
|
* - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
|
||||||
|
* by explicitly calling `sp_command_free`.
|
||||||
|
*/
|
||||||
|
SPCommand *sp_command_utf8_data(size_t x,
|
||||||
|
size_t y,
|
||||||
|
SPCharGrid *grid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of [SPConnection] for testing that does not actually send anything.
|
* Creates a new instance of [SPConnection] for testing that does not actually send anything.
|
||||||
*
|
*
|
||||||
|
@ -1528,7 +1775,7 @@ SPPacket *sp_packet_clone(const SPPacket *packet);
|
||||||
*
|
*
|
||||||
* # Panics
|
* # Panics
|
||||||
*
|
*
|
||||||
* - when `sp_packet_free` is NULL
|
* - when `packet` is NULL
|
||||||
*
|
*
|
||||||
* # Safety
|
* # Safety
|
||||||
*
|
*
|
||||||
|
@ -1560,6 +1807,40 @@ void sp_packet_free(SPPacket *packet);
|
||||||
*/
|
*/
|
||||||
SPPacket *sp_packet_from_command(SPCommand *command);
|
SPPacket *sp_packet_from_command(SPCommand *command);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a raw [SPPacket] from parts.
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
*
|
||||||
|
* - `command_code` specifies which command this packet contains
|
||||||
|
* - `a`, `b`, `c` and `d` are command-specific header values
|
||||||
|
* - `payload` is the optional data that is part of the command
|
||||||
|
* - `payload_len` is the size of the payload
|
||||||
|
*
|
||||||
|
* returns: new instance. Will never return null.
|
||||||
|
*
|
||||||
|
* # Panics
|
||||||
|
*
|
||||||
|
* - when `payload` is null, but `payload_len` is not zero
|
||||||
|
* - when `payload_len` is zero, but `payload` is nonnull
|
||||||
|
*
|
||||||
|
* # Safety
|
||||||
|
*
|
||||||
|
* The caller has to make sure that:
|
||||||
|
*
|
||||||
|
* - `payload` points to a valid memory region of at least `payload_len` bytes
|
||||||
|
* - `payload` is not written to concurrently
|
||||||
|
* - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
|
||||||
|
* by explicitly calling [sp_packet_free].
|
||||||
|
*/
|
||||||
|
SPPacket *sp_packet_from_parts(uint16_t command_code,
|
||||||
|
uint16_t a,
|
||||||
|
uint16_t b,
|
||||||
|
uint16_t c,
|
||||||
|
uint16_t d,
|
||||||
|
const uint8_t *payload,
|
||||||
|
size_t payload_len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to load a [SPPacket] from the passed array with the specified length.
|
* Tries to load a [SPPacket] from the passed array with the specified length.
|
||||||
*
|
*
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include "servicepoint.h"
|
#include "servicepoint.h"
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
SPConnection *connection = sp_connection_open("172.23.42.29:2342");
|
SPConnection *connection = sp_connection_open("localhost:2342");
|
||||||
if (connection == NULL)
|
if (connection == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
@ -10,9 +10,8 @@ int main(void) {
|
||||||
sp_bitmap_fill(pixels, true);
|
sp_bitmap_fill(pixels, true);
|
||||||
|
|
||||||
SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, SP_COMPRESSION_CODE_UNCOMPRESSED);
|
SPCommand *command = sp_command_bitmap_linear_win(0, 0, pixels, SP_COMPRESSION_CODE_UNCOMPRESSED);
|
||||||
while (sp_connection_send_command(connection, sp_command_clone(command)));
|
sp_connection_send_command(connection, command);
|
||||||
|
|
||||||
sp_command_free(command);
|
|
||||||
sp_connection_free(connection);
|
sp_connection_free(connection);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
//!
|
//!
|
||||||
//! prefix `sp_bitmap_`
|
//! prefix `sp_bitmap_`
|
||||||
|
|
||||||
use std::ptr::NonNull;
|
|
||||||
use servicepoint::{DataRef, Grid};
|
use servicepoint::{DataRef, Grid};
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use crate::byte_slice::SPByteSlice;
|
use crate::byte_slice::SPByteSlice;
|
||||||
|
|
||||||
|
@ -43,9 +43,23 @@ pub unsafe extern "C" fn sp_bitmap_new(
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
) -> NonNull<SPBitmap> {
|
) -> NonNull<SPBitmap> {
|
||||||
let result = Box::new(SPBitmap(servicepoint::Bitmap::new(
|
let result = Box::new(SPBitmap(servicepoint::Bitmap::new(width, height)));
|
||||||
width, height,
|
NonNull::from(Box::leak(result))
|
||||||
)));
|
}
|
||||||
|
|
||||||
|
/// Creates a new [SPBitmap] with a size matching the screen.
|
||||||
|
///
|
||||||
|
/// returns: [SPBitmap] initialized to all pixels off. Will never return NULL.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - the returned instance is freed in some way, either by using a consuming function or
|
||||||
|
/// by explicitly calling [sp_bitmap_free].
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_bitmap_new_screen_sized() -> NonNull<SPBitmap> {
|
||||||
|
let result = Box::new(SPBitmap(servicepoint::Bitmap::max_sized()));
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,9 +94,8 @@ pub unsafe extern "C" fn sp_bitmap_load(
|
||||||
) -> NonNull<SPBitmap> {
|
) -> NonNull<SPBitmap> {
|
||||||
assert!(!data.is_null());
|
assert!(!data.is_null());
|
||||||
let data = std::slice::from_raw_parts(data, data_length);
|
let data = std::slice::from_raw_parts(data, data_length);
|
||||||
let result = Box::new(SPBitmap(servicepoint::Bitmap::load(
|
let result =
|
||||||
width, height, data,
|
Box::new(SPBitmap(servicepoint::Bitmap::load(width, height, data)));
|
||||||
)));
|
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
//!
|
//!
|
||||||
//! prefix `sp_bitvec_`
|
//! prefix `sp_bitvec_`
|
||||||
|
|
||||||
use std::ptr::NonNull;
|
|
||||||
use crate::SPByteSlice;
|
use crate::SPByteSlice;
|
||||||
use servicepoint::bitvec::prelude::{BitVec, Msb0};
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
/// A vector of bits
|
/// A vector of bits
|
||||||
///
|
///
|
||||||
|
@ -14,15 +13,15 @@ use servicepoint::bitvec::prelude::{BitVec, Msb0};
|
||||||
/// sp_bitvec_set(vec, 5, true);
|
/// sp_bitvec_set(vec, 5, true);
|
||||||
/// sp_bitvec_free(vec);
|
/// sp_bitvec_free(vec);
|
||||||
/// ```
|
/// ```
|
||||||
pub struct SPBitVec(BitVec<u8, Msb0>);
|
pub struct SPBitVec(servicepoint::BitVec);
|
||||||
|
|
||||||
impl From<BitVec<u8, Msb0>> for SPBitVec {
|
impl From<servicepoint::BitVec> for SPBitVec {
|
||||||
fn from(actual: BitVec<u8, Msb0>) -> Self {
|
fn from(actual: servicepoint::BitVec) -> Self {
|
||||||
Self(actual)
|
Self(actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SPBitVec> for BitVec<u8, Msb0> {
|
impl From<SPBitVec> for servicepoint::BitVec {
|
||||||
fn from(value: SPBitVec) -> Self {
|
fn from(value: SPBitVec) -> Self {
|
||||||
value.0
|
value.0
|
||||||
}
|
}
|
||||||
|
@ -54,7 +53,7 @@ impl Clone for SPBitVec {
|
||||||
/// by explicitly calling `sp_bitvec_free`.
|
/// by explicitly calling `sp_bitvec_free`.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull<SPBitVec> {
|
pub unsafe extern "C" fn sp_bitvec_new(size: usize) -> NonNull<SPBitVec> {
|
||||||
let result = Box::new(SPBitVec(BitVec::repeat(false, size)));
|
let result = Box::new(SPBitVec(servicepoint::BitVec::repeat(false, size)));
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +80,7 @@ pub unsafe extern "C" fn sp_bitvec_load(
|
||||||
) -> NonNull<SPBitVec> {
|
) -> NonNull<SPBitVec> {
|
||||||
assert!(!data.is_null());
|
assert!(!data.is_null());
|
||||||
let data = std::slice::from_raw_parts(data, data_length);
|
let data = std::slice::from_raw_parts(data, data_length);
|
||||||
let result = Box::new(SPBitVec(BitVec::from_slice(data)));
|
let result = Box::new(SPBitVec(servicepoint::BitVec::from_slice(data)));
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
//! prefix `sp_brightness_grid_`
|
//! prefix `sp_brightness_grid_`
|
||||||
|
|
||||||
use crate::SPByteSlice;
|
use crate::SPByteSlice;
|
||||||
use servicepoint::{Brightness, DataRef, Grid, PrimitiveGrid};
|
use servicepoint::{DataRef, Grid};
|
||||||
use std::convert::Into;
|
use std::convert::Into;
|
||||||
use std::intrinsics::transmute;
|
use std::intrinsics::transmute;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
/// see [Brightness::MIN]
|
/// see [servicepoint::Brightness::MIN]
|
||||||
pub const SP_BRIGHTNESS_MIN: u8 = 0;
|
pub const SP_BRIGHTNESS_MIN: u8 = 0;
|
||||||
/// see [Brightness::MAX]
|
/// see [servicepoint::Brightness::MAX]
|
||||||
pub const SP_BRIGHTNESS_MAX: u8 = 11;
|
pub const SP_BRIGHTNESS_MAX: u8 = 11;
|
||||||
/// Count of possible brightness values
|
/// Count of possible brightness values
|
||||||
pub const SP_BRIGHTNESS_LEVELS: u8 = 12;
|
pub const SP_BRIGHTNESS_LEVELS: u8 = 12;
|
||||||
|
@ -48,9 +48,9 @@ pub unsafe extern "C" fn sp_brightness_grid_new(
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
) -> NonNull<SPBrightnessGrid> {
|
) -> NonNull<SPBrightnessGrid> {
|
||||||
let result = Box::new(SPBrightnessGrid(
|
let result = Box::new(SPBrightnessGrid(servicepoint::BrightnessGrid::new(
|
||||||
servicepoint::BrightnessGrid::new(width, height),
|
width, height,
|
||||||
));
|
)));
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ pub unsafe extern "C" fn sp_brightness_grid_load(
|
||||||
) -> NonNull<SPBrightnessGrid> {
|
) -> NonNull<SPBrightnessGrid> {
|
||||||
assert!(!data.is_null());
|
assert!(!data.is_null());
|
||||||
let data = std::slice::from_raw_parts(data, data_length);
|
let data = std::slice::from_raw_parts(data, data_length);
|
||||||
let grid = PrimitiveGrid::load(width, height, data);
|
let grid = servicepoint::ByteGrid::load(width, height, data);
|
||||||
let grid = servicepoint::BrightnessGrid::try_from(grid)
|
let grid = servicepoint::BrightnessGrid::try_from(grid)
|
||||||
.expect("invalid brightness value");
|
.expect("invalid brightness value");
|
||||||
let result = Box::new(SPBrightnessGrid(grid));
|
let result = Box::new(SPBrightnessGrid(grid));
|
||||||
|
@ -203,8 +203,8 @@ pub unsafe extern "C" fn sp_brightness_grid_set(
|
||||||
value: u8,
|
value: u8,
|
||||||
) {
|
) {
|
||||||
assert!(!brightness_grid.is_null());
|
assert!(!brightness_grid.is_null());
|
||||||
let brightness =
|
let brightness = servicepoint::Brightness::try_from(value)
|
||||||
Brightness::try_from(value).expect("invalid brightness value");
|
.expect("invalid brightness value");
|
||||||
(*brightness_grid).0.set(x, y, brightness);
|
(*brightness_grid).0.set(x, y, brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +232,8 @@ pub unsafe extern "C" fn sp_brightness_grid_fill(
|
||||||
value: u8,
|
value: u8,
|
||||||
) {
|
) {
|
||||||
assert!(!brightness_grid.is_null());
|
assert!(!brightness_grid.is_null());
|
||||||
let brightness =
|
let brightness = servicepoint::Brightness::try_from(value)
|
||||||
Brightness::try_from(value).expect("invalid brightness value");
|
.expect("invalid brightness value");
|
||||||
(*brightness_grid).0.fill(brightness);
|
(*brightness_grid).0.fill(brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,7 +311,7 @@ pub unsafe extern "C" fn sp_brightness_grid_unsafe_data_ref(
|
||||||
brightness_grid: *mut SPBrightnessGrid,
|
brightness_grid: *mut SPBrightnessGrid,
|
||||||
) -> SPByteSlice {
|
) -> SPByteSlice {
|
||||||
assert!(!brightness_grid.is_null());
|
assert!(!brightness_grid.is_null());
|
||||||
assert_eq!(core::mem::size_of::<Brightness>(), 1);
|
assert_eq!(core::mem::size_of::<servicepoint::Brightness>(), 1);
|
||||||
let data = (*brightness_grid).0.data_ref_mut();
|
let data = (*brightness_grid).0.data_ref_mut();
|
||||||
// this assumes more about the memory layout than rust guarantees. yikes!
|
// this assumes more about the memory layout than rust guarantees. yikes!
|
||||||
let data: &mut [u8] = transmute(data);
|
let data: &mut [u8] = transmute(data);
|
||||||
|
|
263
crates/servicepoint_binding_c/src/char_grid.rs
Normal file
263
crates/servicepoint_binding_c/src/char_grid.rs
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
//! C functions for interacting with [SPCharGrid]s
|
||||||
|
//!
|
||||||
|
//! prefix `sp_char_grid_`
|
||||||
|
|
||||||
|
use servicepoint::Grid;
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
/// A C-wrapper for grid containing UTF-8 characters.
|
||||||
|
///
|
||||||
|
/// As the rust [char] type is not FFI-safe, characters are passed in their UTF-32 form as 32bit unsigned integers.
|
||||||
|
///
|
||||||
|
/// The encoding is enforced in most cases by the rust standard library
|
||||||
|
/// and will panic when provided with illegal characters.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```C
|
||||||
|
/// CharGrid grid = sp_char_grid_new(4, 3);
|
||||||
|
/// sp_char_grid_fill(grid, '?');
|
||||||
|
/// sp_char_grid_set(grid, 0, 0, '!');
|
||||||
|
/// sp_char_grid_free(grid);
|
||||||
|
/// ```
|
||||||
|
pub struct SPCharGrid(pub(crate) servicepoint::CharGrid);
|
||||||
|
|
||||||
|
impl Clone for SPCharGrid {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
SPCharGrid(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [SPCharGrid] with the specified dimensions.
|
||||||
|
///
|
||||||
|
/// returns: [SPCharGrid] initialized to 0. Will never return NULL.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - the returned instance is freed in some way, either by using a consuming function or
|
||||||
|
/// by explicitly calling `sp_char_grid_free`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_char_grid_new(
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
) -> NonNull<SPCharGrid> {
|
||||||
|
let result =
|
||||||
|
Box::new(SPCharGrid(servicepoint::CharGrid::new(width, height)));
|
||||||
|
NonNull::from(Box::leak(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a [SPCharGrid] with the specified dimensions from the provided data.
|
||||||
|
///
|
||||||
|
/// Will never return NULL.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `data` is NULL
|
||||||
|
/// - when the provided `data_length` does not match `height` and `width`
|
||||||
|
/// - when `data` is not valid UTF-8
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `data` points to a valid memory location of at least `data_length`
|
||||||
|
/// bytes in size.
|
||||||
|
/// - the returned instance is freed in some way, either by using a consuming function or
|
||||||
|
/// by explicitly calling `sp_char_grid_free`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_char_grid_load(
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
data: *const u8,
|
||||||
|
data_length: usize,
|
||||||
|
) -> NonNull<SPCharGrid> {
|
||||||
|
assert!(data.is_null());
|
||||||
|
let data = std::slice::from_raw_parts(data, data_length);
|
||||||
|
let result = Box::new(SPCharGrid(
|
||||||
|
servicepoint::CharGrid::load_utf8(width, height, data.to_vec())
|
||||||
|
.unwrap(),
|
||||||
|
));
|
||||||
|
NonNull::from(Box::leak(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clones a [SPCharGrid].
|
||||||
|
///
|
||||||
|
/// Will never return NULL.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `char_grid` is NULL
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
/// - `char_grid` is not written to concurrently
|
||||||
|
/// - the returned instance is freed in some way, either by using a consuming function or
|
||||||
|
/// by explicitly calling `sp_char_grid_free`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_char_grid_clone(
|
||||||
|
char_grid: *const SPCharGrid,
|
||||||
|
) -> NonNull<SPCharGrid> {
|
||||||
|
assert!(!char_grid.is_null());
|
||||||
|
let result = Box::new((*char_grid).clone());
|
||||||
|
NonNull::from(Box::leak(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deallocates a [SPCharGrid].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `char_grid` is NULL
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
/// - `char_grid` is not used concurrently or after char_grid call
|
||||||
|
/// - `char_grid` was not passed to another consuming function, e.g. to create a [SPCommand]
|
||||||
|
///
|
||||||
|
/// [SPCommand]: [crate::SPCommand]
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_char_grid_free(char_grid: *mut SPCharGrid) {
|
||||||
|
assert!(!char_grid.is_null());
|
||||||
|
_ = Box::from_raw(char_grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the current value at the specified position.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `char_grid`: instance to read from
|
||||||
|
/// - `x` and `y`: position of the cell to read
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `char_grid` is NULL
|
||||||
|
/// - when accessing `x` or `y` out of bounds
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
/// - `char_grid` is not written to concurrently
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_char_grid_get(
|
||||||
|
char_grid: *const SPCharGrid,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
) -> u32 {
|
||||||
|
assert!(!char_grid.is_null());
|
||||||
|
(*char_grid).0.get(x, y) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the value of the specified position in the [SPCharGrid].
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `char_grid`: instance to write to
|
||||||
|
/// - `x` and `y`: position of the cell
|
||||||
|
/// - `value`: the value to write to the cell
|
||||||
|
///
|
||||||
|
/// returns: old value of the cell
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `char_grid` is NULL
|
||||||
|
/// - when accessing `x` or `y` out of bounds
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `char_grid` points to a valid [SPBitVec]
|
||||||
|
/// - `char_grid` is not written to or read from concurrently
|
||||||
|
///
|
||||||
|
/// [SPBitVec]: [crate::SPBitVec]
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_char_grid_set(
|
||||||
|
char_grid: *mut SPCharGrid,
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
value: u32,
|
||||||
|
) {
|
||||||
|
assert!(!char_grid.is_null());
|
||||||
|
(*char_grid).0.set(x, y, char::from_u32(value).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the value of all cells in the [SPCharGrid].
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `char_grid`: instance to write to
|
||||||
|
/// - `value`: the value to set all cells to
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `char_grid` is NULL
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
/// - `char_grid` is not written to or read from concurrently
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_char_grid_fill(
|
||||||
|
char_grid: *mut SPCharGrid,
|
||||||
|
value: u32,
|
||||||
|
) {
|
||||||
|
assert!(!char_grid.is_null());
|
||||||
|
(*char_grid).0.fill(char::from_u32(value).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the width of the [SPCharGrid] instance.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `char_grid`: instance to read from
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `char_grid` is NULL
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_char_grid_width(
|
||||||
|
char_grid: *const SPCharGrid,
|
||||||
|
) -> usize {
|
||||||
|
assert!(!char_grid.is_null());
|
||||||
|
(*char_grid).0.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the height of the [SPCharGrid] instance.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `char_grid`: instance to read from
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `char_grid` is NULL
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `char_grid` points to a valid [SPCharGrid]
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_char_grid_height(
|
||||||
|
char_grid: *const SPCharGrid,
|
||||||
|
) -> usize {
|
||||||
|
assert!(!char_grid.is_null());
|
||||||
|
(*char_grid).0.height()
|
||||||
|
}
|
|
@ -4,11 +4,9 @@
|
||||||
|
|
||||||
use std::ptr::{null_mut, NonNull};
|
use std::ptr::{null_mut, NonNull};
|
||||||
|
|
||||||
use servicepoint::{Brightness, Origin};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
SPBitVec, SPBitmap, SPBrightnessGrid, SPCompressionCode, SPCp437Grid,
|
SPBitVec, SPBitmap, SPBrightnessGrid, SPCharGrid, SPCompressionCode,
|
||||||
SPPacket,
|
SPCp437Grid, SPPacket,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A low-level display command.
|
/// A low-level display command.
|
||||||
|
@ -164,11 +162,10 @@ pub unsafe extern "C" fn sp_command_fade_out() -> NonNull<SPCommand> {
|
||||||
pub unsafe extern "C" fn sp_command_brightness(
|
pub unsafe extern "C" fn sp_command_brightness(
|
||||||
brightness: u8,
|
brightness: u8,
|
||||||
) -> NonNull<SPCommand> {
|
) -> NonNull<SPCommand> {
|
||||||
let brightness =
|
let brightness = servicepoint::Brightness::try_from(brightness)
|
||||||
Brightness::try_from(brightness).expect("invalid brightness");
|
.expect("invalid brightness");
|
||||||
let result = Box::new(SPCommand(
|
let result =
|
||||||
servicepoint::Command::Brightness(brightness),
|
Box::new(SPCommand(servicepoint::Command::Brightness(brightness)));
|
||||||
));
|
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,9 +195,10 @@ pub unsafe extern "C" fn sp_command_char_brightness(
|
||||||
) -> NonNull<SPCommand> {
|
) -> NonNull<SPCommand> {
|
||||||
assert!(!grid.is_null());
|
assert!(!grid.is_null());
|
||||||
let byte_grid = *Box::from_raw(grid);
|
let byte_grid = *Box::from_raw(grid);
|
||||||
let result = Box::new(SPCommand(
|
let result = Box::new(SPCommand(servicepoint::Command::CharBrightness(
|
||||||
servicepoint::Command::CharBrightness(Origin::new(x, y), byte_grid.0),
|
servicepoint::Origin::new(x, y),
|
||||||
));
|
byte_grid.0,
|
||||||
|
)));
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,13 +235,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear(
|
||||||
) -> NonNull<SPCommand> {
|
) -> NonNull<SPCommand> {
|
||||||
assert!(!bit_vec.is_null());
|
assert!(!bit_vec.is_null());
|
||||||
let bit_vec = *Box::from_raw(bit_vec);
|
let bit_vec = *Box::from_raw(bit_vec);
|
||||||
let result = Box::new(SPCommand(
|
let result = Box::new(SPCommand(servicepoint::Command::BitmapLinear(
|
||||||
servicepoint::Command::BitmapLinear(
|
offset,
|
||||||
offset,
|
bit_vec.into(),
|
||||||
bit_vec.into(),
|
compression.try_into().expect("invalid compression code"),
|
||||||
compression.try_into().expect("invalid compression code"),
|
)));
|
||||||
),
|
|
||||||
));
|
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,13 +276,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and(
|
||||||
) -> NonNull<SPCommand> {
|
) -> NonNull<SPCommand> {
|
||||||
assert!(!bit_vec.is_null());
|
assert!(!bit_vec.is_null());
|
||||||
let bit_vec = *Box::from_raw(bit_vec);
|
let bit_vec = *Box::from_raw(bit_vec);
|
||||||
let result = Box::new(SPCommand(
|
let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearAnd(
|
||||||
servicepoint::Command::BitmapLinearAnd(
|
offset,
|
||||||
offset,
|
bit_vec.into(),
|
||||||
bit_vec.into(),
|
compression.try_into().expect("invalid compression code"),
|
||||||
compression.try_into().expect("invalid compression code"),
|
)));
|
||||||
),
|
|
||||||
));
|
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,13 +317,11 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or(
|
||||||
) -> NonNull<SPCommand> {
|
) -> NonNull<SPCommand> {
|
||||||
assert!(!bit_vec.is_null());
|
assert!(!bit_vec.is_null());
|
||||||
let bit_vec = *Box::from_raw(bit_vec);
|
let bit_vec = *Box::from_raw(bit_vec);
|
||||||
let result = Box::new(SPCommand(
|
let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearOr(
|
||||||
servicepoint::Command::BitmapLinearOr(
|
offset,
|
||||||
offset,
|
bit_vec.into(),
|
||||||
bit_vec.into(),
|
compression.try_into().expect("invalid compression code"),
|
||||||
compression.try_into().expect("invalid compression code"),
|
)));
|
||||||
),
|
|
||||||
));
|
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,17 +358,15 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_xor(
|
||||||
) -> NonNull<SPCommand> {
|
) -> NonNull<SPCommand> {
|
||||||
assert!(!bit_vec.is_null());
|
assert!(!bit_vec.is_null());
|
||||||
let bit_vec = *Box::from_raw(bit_vec);
|
let bit_vec = *Box::from_raw(bit_vec);
|
||||||
let result = Box::new(SPCommand(
|
let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearXor(
|
||||||
servicepoint::Command::BitmapLinearXor(
|
offset,
|
||||||
offset,
|
bit_vec.into(),
|
||||||
bit_vec.into(),
|
compression.try_into().expect("invalid compression code"),
|
||||||
compression.try_into().expect("invalid compression code"),
|
)));
|
||||||
),
|
|
||||||
));
|
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show text on the screen.
|
/// Show codepage 437 encoded text on the screen.
|
||||||
///
|
///
|
||||||
/// The passed [SPCp437Grid] gets consumed.
|
/// The passed [SPCp437Grid] gets consumed.
|
||||||
///
|
///
|
||||||
|
@ -402,9 +392,43 @@ pub unsafe extern "C" fn sp_command_cp437_data(
|
||||||
) -> NonNull<SPCommand> {
|
) -> NonNull<SPCommand> {
|
||||||
assert!(!grid.is_null());
|
assert!(!grid.is_null());
|
||||||
let grid = *Box::from_raw(grid);
|
let grid = *Box::from_raw(grid);
|
||||||
let result = Box::new(SPCommand(
|
let result = Box::new(SPCommand(servicepoint::Command::Cp437Data(
|
||||||
servicepoint::Command::Cp437Data(Origin::new(x, y), grid.0),
|
servicepoint::Origin::new(x, y),
|
||||||
));
|
grid.0,
|
||||||
|
)));
|
||||||
|
NonNull::from(Box::leak(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show UTF-8 encoded text on the screen.
|
||||||
|
///
|
||||||
|
/// The passed [SPCharGrid] gets consumed.
|
||||||
|
///
|
||||||
|
/// Returns: a new [servicepoint::Command::Utf8Data] instance. Will never return NULL.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `grid` is null
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `grid` points to a valid instance of [SPCharGrid]
|
||||||
|
/// - `grid` is not used concurrently or after this call
|
||||||
|
/// - the returned [SPCommand] instance is freed in some way, either by using a consuming function or
|
||||||
|
/// by explicitly calling `sp_command_free`.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_command_utf8_data(
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
grid: *mut SPCharGrid,
|
||||||
|
) -> NonNull<SPCommand> {
|
||||||
|
assert!(!grid.is_null());
|
||||||
|
let grid = *Box::from_raw(grid);
|
||||||
|
let result = Box::new(SPCommand(servicepoint::Command::Utf8Data(
|
||||||
|
servicepoint::Origin::new(x, y),
|
||||||
|
grid.0,
|
||||||
|
)));
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,15 +461,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_win(
|
||||||
) -> NonNull<SPCommand> {
|
) -> NonNull<SPCommand> {
|
||||||
assert!(!bitmap.is_null());
|
assert!(!bitmap.is_null());
|
||||||
let byte_grid = (*Box::from_raw(bitmap)).0;
|
let byte_grid = (*Box::from_raw(bitmap)).0;
|
||||||
let result = Box::new(SPCommand(
|
let result = Box::new(SPCommand(servicepoint::Command::BitmapLinearWin(
|
||||||
servicepoint::Command::BitmapLinearWin(
|
servicepoint::Origin::new(x, y),
|
||||||
Origin::new(x, y),
|
byte_grid,
|
||||||
byte_grid,
|
compression_code
|
||||||
compression_code
|
.try_into()
|
||||||
.try_into()
|
.expect("invalid compression code"),
|
||||||
.expect("invalid compression code"),
|
)));
|
||||||
),
|
|
||||||
));
|
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
//!
|
//!
|
||||||
//! prefix `sp_cp437_grid_`
|
//! prefix `sp_cp437_grid_`
|
||||||
|
|
||||||
use std::ptr::NonNull;
|
|
||||||
use crate::SPByteSlice;
|
use crate::SPByteSlice;
|
||||||
use servicepoint::{DataRef, Grid};
|
use servicepoint::{DataRef, Grid};
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
/// A C-wrapper for grid containing codepage 437 characters.
|
/// A C-wrapper for grid containing codepage 437 characters.
|
||||||
///
|
///
|
||||||
|
@ -41,9 +41,8 @@ pub unsafe extern "C" fn sp_cp437_grid_new(
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
) -> NonNull<SPCp437Grid> {
|
) -> NonNull<SPCp437Grid> {
|
||||||
let result = Box::new(SPCp437Grid(
|
let result =
|
||||||
servicepoint::Cp437Grid::new(width, height),
|
Box::new(SPCp437Grid(servicepoint::Cp437Grid::new(width, height)));
|
||||||
));
|
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,9 +72,9 @@ pub unsafe extern "C" fn sp_cp437_grid_load(
|
||||||
) -> NonNull<SPCp437Grid> {
|
) -> NonNull<SPCp437Grid> {
|
||||||
assert!(data.is_null());
|
assert!(data.is_null());
|
||||||
let data = std::slice::from_raw_parts(data, data_length);
|
let data = std::slice::from_raw_parts(data, data_length);
|
||||||
let result = Box::new(SPCp437Grid(
|
let result = Box::new(SPCp437Grid(servicepoint::Cp437Grid::load(
|
||||||
servicepoint::Cp437Grid::load(width, height, data),
|
width, height, data,
|
||||||
));
|
)));
|
||||||
NonNull::from(Box::leak(result))
|
NonNull::from(Box::leak(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,20 +25,22 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
pub use crate::bitvec::*;
|
|
||||||
pub use crate::bitmap::*;
|
pub use crate::bitmap::*;
|
||||||
|
pub use crate::bitvec::*;
|
||||||
pub use crate::brightness_grid::*;
|
pub use crate::brightness_grid::*;
|
||||||
pub use crate::byte_slice::*;
|
pub use crate::byte_slice::*;
|
||||||
|
pub use crate::char_grid::*;
|
||||||
pub use crate::command::*;
|
pub use crate::command::*;
|
||||||
pub use crate::connection::*;
|
pub use crate::connection::*;
|
||||||
pub use crate::constants::*;
|
pub use crate::constants::*;
|
||||||
pub use crate::cp437_grid::*;
|
pub use crate::cp437_grid::*;
|
||||||
pub use crate::packet::*;
|
pub use crate::packet::*;
|
||||||
|
|
||||||
mod bitvec;
|
|
||||||
mod bitmap;
|
mod bitmap;
|
||||||
|
mod bitvec;
|
||||||
mod brightness_grid;
|
mod brightness_grid;
|
||||||
mod byte_slice;
|
mod byte_slice;
|
||||||
|
mod char_grid;
|
||||||
mod command;
|
mod command;
|
||||||
mod connection;
|
mod connection;
|
||||||
mod constants;
|
mod constants;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::ptr::{null_mut, NonNull};
|
||||||
use crate::SPCommand;
|
use crate::SPCommand;
|
||||||
|
|
||||||
/// The raw packet
|
/// The raw packet
|
||||||
pub struct SPPacket(pub(crate) servicepoint::packet::Packet);
|
pub struct SPPacket(pub(crate) servicepoint::Packet);
|
||||||
|
|
||||||
/// Turns a [SPCommand] into a [SPPacket].
|
/// Turns a [SPCommand] into a [SPPacket].
|
||||||
/// The [SPCommand] gets consumed.
|
/// The [SPCommand] gets consumed.
|
||||||
|
@ -59,12 +59,69 @@ pub unsafe extern "C" fn sp_packet_try_load(
|
||||||
) -> *mut SPPacket {
|
) -> *mut SPPacket {
|
||||||
assert!(!data.is_null());
|
assert!(!data.is_null());
|
||||||
let data = std::slice::from_raw_parts(data, length);
|
let data = std::slice::from_raw_parts(data, length);
|
||||||
match servicepoint::packet::Packet::try_from(data) {
|
match servicepoint::Packet::try_from(data) {
|
||||||
Err(_) => null_mut(),
|
Err(_) => null_mut(),
|
||||||
Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))),
|
Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a raw [SPPacket] from parts.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `command_code` specifies which command this packet contains
|
||||||
|
/// - `a`, `b`, `c` and `d` are command-specific header values
|
||||||
|
/// - `payload` is the optional data that is part of the command
|
||||||
|
/// - `payload_len` is the size of the payload
|
||||||
|
///
|
||||||
|
/// returns: new instance. Will never return null.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// - when `payload` is null, but `payload_len` is not zero
|
||||||
|
/// - when `payload_len` is zero, but `payload` is nonnull
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller has to make sure that:
|
||||||
|
///
|
||||||
|
/// - `payload` points to a valid memory region of at least `payload_len` bytes
|
||||||
|
/// - `payload` is not written to concurrently
|
||||||
|
/// - the returned [SPPacket] instance is freed in some way, either by using a consuming function or
|
||||||
|
/// by explicitly calling [sp_packet_free].
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn sp_packet_from_parts(
|
||||||
|
command_code: u16,
|
||||||
|
a: u16,
|
||||||
|
b: u16,
|
||||||
|
c: u16,
|
||||||
|
d: u16,
|
||||||
|
payload: *const u8,
|
||||||
|
payload_len: usize,
|
||||||
|
) -> NonNull<SPPacket> {
|
||||||
|
assert_eq!(payload.is_null(), payload_len == 0);
|
||||||
|
|
||||||
|
let payload = if payload.is_null() {
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
let payload = std::slice::from_raw_parts(payload, payload_len);
|
||||||
|
Vec::from(payload)
|
||||||
|
};
|
||||||
|
|
||||||
|
let packet = servicepoint::Packet {
|
||||||
|
header: servicepoint::Header {
|
||||||
|
command_code,
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c,
|
||||||
|
d,
|
||||||
|
},
|
||||||
|
payload,
|
||||||
|
};
|
||||||
|
let result = Box::new(SPPacket(packet));
|
||||||
|
NonNull::from(Box::leak(result))
|
||||||
|
}
|
||||||
|
|
||||||
/// Clones a [SPPacket].
|
/// Clones a [SPPacket].
|
||||||
///
|
///
|
||||||
/// Will never return NULL.
|
/// Will never return NULL.
|
||||||
|
@ -94,7 +151,7 @@ pub unsafe extern "C" fn sp_packet_clone(
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// - when `sp_packet_free` is NULL
|
/// - when `packet` is NULL
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
|
|
@ -20,7 +20,7 @@ uniffi = { version = "0.25.3" }
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
|
||||||
[dependencies.servicepoint]
|
[dependencies.servicepoint]
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
path = "../servicepoint"
|
path = "../servicepoint"
|
||||||
features = ["all_compressions"]
|
features = ["all_compressions"]
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ optional = true
|
||||||
|
|
||||||
[dependencies.uniffi-bindgen-go]
|
[dependencies.uniffi-bindgen-go]
|
||||||
git = "https://github.com/NordSecurity/uniffi-bindgen-go.git"
|
git = "https://github.com/NordSecurity/uniffi-bindgen-go.git"
|
||||||
# tag = "0.2.1+v0.25.0"
|
# tag = "0.2.2+v0.25.0"
|
||||||
rev = "a77dc0462dc18d53846c758155ab4e0a42e5b240"
|
rev = "ba23bab72f1a9bcc39ce81924d3d9265598e017c"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>ServicePoint</PackageId>
|
<PackageId>ServicePoint</PackageId>
|
||||||
<Version>0.12.0</Version>
|
<Version>0.13.0</Version>
|
||||||
<Authors>Repository Authors</Authors>
|
<Authors>Repository Authors</Authors>
|
||||||
<Company>None</Company>
|
<Company>None</Company>
|
||||||
<Product>ServicePoint</Product>
|
<Product>ServicePoint</Product>
|
||||||
|
|
|
@ -681,6 +681,10 @@ static class _UniFFILib {
|
||||||
public static extern CommandSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset(ref RustCallStatus _uniffi_out_err
|
public static extern CommandSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset(ref RustCallStatus _uniffi_out_err
|
||||||
);
|
);
|
||||||
|
|
||||||
|
[DllImport("servicepoint_binding_uniffi")]
|
||||||
|
public static extern CommandSafeHandle uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data(ulong @offsetX,ulong @offsetY,CharGridSafeHandle @grid,ref RustCallStatus _uniffi_out_err
|
||||||
|
);
|
||||||
|
|
||||||
[DllImport("servicepoint_binding_uniffi")]
|
[DllImport("servicepoint_binding_uniffi")]
|
||||||
public static extern sbyte uniffi_servicepoint_binding_uniffi_fn_method_command_equals(CommandSafeHandle @ptr,CommandSafeHandle @other,ref RustCallStatus _uniffi_out_err
|
public static extern sbyte uniffi_servicepoint_binding_uniffi_fn_method_command_equals(CommandSafeHandle @ptr,CommandSafeHandle @other,ref RustCallStatus _uniffi_out_err
|
||||||
);
|
);
|
||||||
|
@ -1255,6 +1259,10 @@ static class _UniFFILib {
|
||||||
public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset(
|
public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset(
|
||||||
);
|
);
|
||||||
|
|
||||||
|
[DllImport("servicepoint_binding_uniffi")]
|
||||||
|
public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_command_utf8_data(
|
||||||
|
);
|
||||||
|
|
||||||
[DllImport("servicepoint_binding_uniffi")]
|
[DllImport("servicepoint_binding_uniffi")]
|
||||||
public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new(
|
public static extern ushort uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new(
|
||||||
);
|
);
|
||||||
|
@ -1697,6 +1705,12 @@ static class _UniFFILib {
|
||||||
throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset` checksum `62130`, library returned `{checksum}`");
|
throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset` checksum `62130`, library returned `{checksum}`");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_command_utf8_data();
|
||||||
|
if (checksum != 2263) {
|
||||||
|
throw new UniffiContractChecksumException($"ServicePoint: uniffi bindings expected function `uniffi_servicepoint_binding_uniffi_checksum_constructor_command_utf8_data` checksum `2263`, library returned `{checksum}`");
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new();
|
var checksum = _UniFFILib.uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new();
|
||||||
if (checksum != 30445) {
|
if (checksum != 30445) {
|
||||||
|
@ -2671,6 +2685,13 @@ public class Command: FFIObject<CommandSafeHandle>, ICommand {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Command Utf8Data(ulong @offsetX, ulong @offsetY, CharGrid @grid) {
|
||||||
|
return new Command(
|
||||||
|
_UniffiHelpers.RustCall( (ref RustCallStatus _status) =>
|
||||||
|
_UniFFILib.uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data(FfiConverterUInt64.INSTANCE.Lower(@offsetX), FfiConverterUInt64.INSTANCE.Lower(@offsetY), FfiConverterTypeCharGrid.INSTANCE.Lower(@grid), ref _status)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -918,6 +918,9 @@ module UniFFILib
|
||||||
attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset,
|
attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset,
|
||||||
[RustCallStatus.by_ref],
|
[RustCallStatus.by_ref],
|
||||||
:pointer
|
:pointer
|
||||||
|
attach_function :uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data,
|
||||||
|
[:uint64, :uint64, :pointer, RustCallStatus.by_ref],
|
||||||
|
:pointer
|
||||||
attach_function :uniffi_servicepoint_binding_uniffi_fn_method_command_equals,
|
attach_function :uniffi_servicepoint_binding_uniffi_fn_method_command_equals,
|
||||||
[:pointer, :pointer, RustCallStatus.by_ref],
|
[:pointer, :pointer, RustCallStatus.by_ref],
|
||||||
:int8
|
:int8
|
||||||
|
@ -1188,6 +1191,9 @@ module UniFFILib
|
||||||
attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset,
|
attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_hard_reset,
|
||||||
[RustCallStatus.by_ref],
|
[RustCallStatus.by_ref],
|
||||||
:uint16
|
:uint16
|
||||||
|
attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_command_utf8_data,
|
||||||
|
[RustCallStatus.by_ref],
|
||||||
|
:uint16
|
||||||
attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new,
|
attach_function :uniffi_servicepoint_binding_uniffi_checksum_constructor_connection_new,
|
||||||
[RustCallStatus.by_ref],
|
[RustCallStatus.by_ref],
|
||||||
:uint16
|
:uint16
|
||||||
|
@ -1817,6 +1823,15 @@ end
|
||||||
# and just create a new instance with the required pointer.
|
# and just create a new instance with the required pointer.
|
||||||
return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset,))
|
return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_hard_reset,))
|
||||||
end
|
end
|
||||||
|
def self.utf8_data(offset_x, offset_y, grid)
|
||||||
|
offset_x = ServicepointBindingUniffi::uniffi_in_range(offset_x, "u64", 0, 2**64)
|
||||||
|
offset_y = ServicepointBindingUniffi::uniffi_in_range(offset_y, "u64", 0, 2**64)
|
||||||
|
grid = grid
|
||||||
|
# Call the (fallible) function before creating any half-baked object instances.
|
||||||
|
# Lightly yucky way to bypass the usual "initialize" logic
|
||||||
|
# and just create a new instance with the required pointer.
|
||||||
|
return _uniffi_allocate(ServicepointBindingUniffi.rust_call(:uniffi_servicepoint_binding_uniffi_fn_constructor_command_utf8_data,offset_x,offset_y,(CharGrid._uniffi_lower grid)))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def equals(other)
|
def equals(other)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = "servicepoint"
|
s.name = "servicepoint"
|
||||||
s.version = "0.12.0"
|
s.version = "0.13.0"
|
||||||
s.summary = ""
|
s.summary = ""
|
||||||
s.description = ""
|
s.description = ""
|
||||||
s.authors = ["kaesaecracker"]
|
s.authors = ["kaesaecracker"]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use servicepoint::{Grid, SeriesError};
|
use crate::cp437_grid::Cp437Grid;
|
||||||
|
use servicepoint::{Grid, SetValueSeriesError};
|
||||||
use std::convert::Into;
|
use std::convert::Into;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use crate::cp437_grid::Cp437Grid;
|
|
||||||
|
|
||||||
#[derive(uniffi::Object)]
|
#[derive(uniffi::Object)]
|
||||||
pub struct CharGrid {
|
pub struct CharGrid {
|
||||||
|
@ -107,7 +107,10 @@ impl CharGrid {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_row(y as usize)
|
.get_row(y as usize)
|
||||||
.map(String::from_iter)
|
.map(String::from_iter)
|
||||||
.ok_or(CharGridError::OutOfBounds {index: y, size: self.height()})
|
.ok_or(CharGridError::OutOfBounds {
|
||||||
|
index: y,
|
||||||
|
size: self.height(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_col(&self, x: u64) -> Result<String, CharGridError> {
|
pub fn get_col(&self, x: u64) -> Result<String, CharGridError> {
|
||||||
|
@ -116,11 +119,16 @@ impl CharGrid {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get_col(x as usize)
|
.get_col(x as usize)
|
||||||
.map(String::from_iter)
|
.map(String::from_iter)
|
||||||
.ok_or(CharGridError::OutOfBounds {index: x, size: self.width()})
|
.ok_or(CharGridError::OutOfBounds {
|
||||||
|
index: x,
|
||||||
|
size: self.width(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_cp437(&self) -> Arc<Cp437Grid> {
|
pub fn to_cp437(&self) -> Arc<Cp437Grid> {
|
||||||
Cp437Grid::internal_new(servicepoint::Cp437Grid::from(&*self.actual.read().unwrap()))
|
Cp437Grid::internal_new(servicepoint::Cp437Grid::from(
|
||||||
|
&*self.actual.read().unwrap(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,9 +141,7 @@ impl CharGrid {
|
||||||
|
|
||||||
fn str_to_char(value: String) -> Result<char, CharGridError> {
|
fn str_to_char(value: String) -> Result<char, CharGridError> {
|
||||||
if value.len() != 1 {
|
if value.len() != 1 {
|
||||||
return Err(CharGridError::StringNotOneChar {
|
return Err(CharGridError::StringNotOneChar { value });
|
||||||
value,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = value.chars().nth(0).unwrap();
|
let value = value.chars().nth(0).unwrap();
|
||||||
|
@ -143,16 +149,16 @@ impl CharGrid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SeriesError> for CharGridError {
|
impl From<SetValueSeriesError> for CharGridError {
|
||||||
fn from(e: SeriesError) -> Self {
|
fn from(e: SetValueSeriesError) -> Self {
|
||||||
match e {
|
match e {
|
||||||
SeriesError::OutOfBounds { index, size } => {
|
SetValueSeriesError::OutOfBounds { index, size } => {
|
||||||
CharGridError::OutOfBounds {
|
CharGridError::OutOfBounds {
|
||||||
index: index as u64,
|
index: index as u64,
|
||||||
size: size as u64,
|
size: size as u64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SeriesError::InvalidLength { actual, expected } => {
|
SetValueSeriesError::InvalidLength { actual, expected } => {
|
||||||
CharGridError::InvalidSeriesLength {
|
CharGridError::InvalidSeriesLength {
|
||||||
actual: actual as u64,
|
actual: actual as u64,
|
||||||
expected: expected as u64,
|
expected: expected as u64,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::bitmap::Bitmap;
|
use crate::bitmap::Bitmap;
|
||||||
use crate::bitvec::BitVec;
|
use crate::bitvec::BitVec;
|
||||||
use crate::brightness_grid::BrightnessGrid;
|
use crate::brightness_grid::BrightnessGrid;
|
||||||
|
use crate::char_grid::CharGrid;
|
||||||
use crate::compression_code::CompressionCode;
|
use crate::compression_code::CompressionCode;
|
||||||
use crate::cp437_grid::Cp437Grid;
|
use crate::cp437_grid::Cp437Grid;
|
||||||
use crate::errors::ServicePointError;
|
use crate::errors::ServicePointError;
|
||||||
|
@ -151,6 +152,18 @@ impl Command {
|
||||||
Self::internal_new(actual)
|
Self::internal_new(actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[uniffi::constructor]
|
||||||
|
pub fn utf8_data(
|
||||||
|
offset_x: u64,
|
||||||
|
offset_y: u64,
|
||||||
|
grid: &Arc<CharGrid>,
|
||||||
|
) -> Arc<Self> {
|
||||||
|
let origin = Origin::new(offset_x as usize, offset_y as usize);
|
||||||
|
let grid = grid.actual.read().unwrap().clone();
|
||||||
|
let actual = servicepoint::Command::Utf8Data(origin, grid);
|
||||||
|
Self::internal_new(actual)
|
||||||
|
}
|
||||||
|
|
||||||
#[uniffi::constructor]
|
#[uniffi::constructor]
|
||||||
pub fn clone(other: &Arc<Self>) -> Arc<Self> {
|
pub fn clone(other: &Arc<Self>) -> Arc<Self> {
|
||||||
Self::internal_new(other.actual.clone())
|
Self::internal_new(other.actual.clone())
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, uniffi::Record)]
|
#[derive(
|
||||||
|
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, uniffi::Record,
|
||||||
|
)]
|
||||||
pub struct Constants {
|
pub struct Constants {
|
||||||
pub tile_size: u64,
|
pub tile_size: u64,
|
||||||
pub tile_width: u64,
|
pub tile_width: u64,
|
||||||
|
@ -10,7 +12,7 @@ pub struct Constants {
|
||||||
|
|
||||||
#[uniffi::export]
|
#[uniffi::export]
|
||||||
fn get_constants() -> Constants {
|
fn get_constants() -> Constants {
|
||||||
Constants {
|
Constants {
|
||||||
tile_size: servicepoint::TILE_SIZE as u64,
|
tile_size: servicepoint::TILE_SIZE as u64,
|
||||||
tile_width: servicepoint::TILE_WIDTH as u64,
|
tile_width: servicepoint::TILE_WIDTH as u64,
|
||||||
tile_height: servicepoint::TILE_HEIGHT as u64,
|
tile_height: servicepoint::TILE_HEIGHT as u64,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
use crate::char_grid::CharGrid;
|
||||||
use servicepoint::{DataRef, Grid};
|
use servicepoint::{DataRef, Grid};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use crate::char_grid::CharGrid;
|
|
||||||
|
|
||||||
#[derive(uniffi::Object)]
|
#[derive(uniffi::Object)]
|
||||||
pub struct Cp437Grid {
|
pub struct Cp437Grid {
|
||||||
|
@ -72,6 +72,8 @@ impl Cp437Grid {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_utf8(&self) -> Arc<CharGrid> {
|
pub fn to_utf8(&self) -> Arc<CharGrid> {
|
||||||
CharGrid::internal_new(servicepoint::CharGrid::from(&*self.actual.read().unwrap()))
|
CharGrid::internal_new(servicepoint::CharGrid::from(
|
||||||
|
&*self.actual.read().unwrap(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,6 @@ mod char_grid;
|
||||||
mod command;
|
mod command;
|
||||||
mod compression_code;
|
mod compression_code;
|
||||||
mod connection;
|
mod connection;
|
||||||
|
mod constants;
|
||||||
mod cp437_grid;
|
mod cp437_grid;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod constants;
|
|
||||||
|
|
14
flake.lock
14
flake.lock
|
@ -7,11 +7,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1721727458,
|
"lastModified": 1736429655,
|
||||||
"narHash": "sha256-r/xppY958gmZ4oTfLiHN0ZGuQ+RSTijDblVgVLFi1mw=",
|
"narHash": "sha256-BwMekRuVlSB9C0QgwKMICiJ5EVbLGjfe4qyueyNQyGI=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "naersk",
|
"repo": "naersk",
|
||||||
"rev": "3fb418eaf352498f6b6c30592e3beb63df42ef11",
|
"rev": "0621e47bd95542b8e1ce2ee2d65d6a1f887a13ce",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -22,16 +22,16 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1730963269,
|
"lastModified": 1736549401,
|
||||||
"narHash": "sha256-rz30HrFYCHiWEBCKHMffHbMdWJ35hEkcRVU0h7ms3x0=",
|
"narHash": "sha256-ibkQrMHxF/7TqAYcQE+tOnIsSEzXmMegzyBWza6uHKM=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "83fb6c028368e465cd19bb127b86f971a5e41ebc",
|
"rev": "1dab772dd4a68a7bba5d9460685547ff8e17d899",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"ref": "nixos-24.05",
|
"ref": "nixos-24.11",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
description = "Flake for servicepoint-simulator";
|
description = "Flake for servicepoint-simulator";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
|
||||||
naersk = {
|
naersk = {
|
||||||
url = "github:nix-community/naersk";
|
url = "github:nix-community/naersk";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
];
|
];
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
xe
|
xe
|
||||||
lzma
|
xz
|
||||||
];
|
];
|
||||||
makeExample =
|
makeExample =
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue