Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e509e067dd | ||
|
|
663adde30a | ||
|
|
4984197d95 | ||
|
|
8e47d3c966 | ||
|
|
b08701c342 | ||
|
|
473bbbc3f9 | ||
|
|
8ddbaeaaa6 | ||
|
|
de8a1a2fe8 | ||
|
|
114385868a | ||
|
|
cecccb3f28 | ||
|
|
4e3d1ce208 | ||
|
|
e985140417 | ||
|
|
ff56215c06 | ||
|
|
75d24f6587 | ||
|
|
62d1666ec2 | ||
|
|
b6f5f74fa4 | ||
|
|
fe67160974 | ||
|
|
739675a9f5 | ||
|
|
b3bf57301a | ||
|
|
1cf37413e6 | ||
|
|
cbab86bd93 | ||
|
|
b69e7df635 | ||
|
|
fe1aa3ebd1 | ||
|
|
373d0efe55 | ||
|
|
eba1a6a6be | ||
|
|
617c37c713 | ||
|
|
ff886ca27d | ||
|
|
61f83a7042 | ||
|
|
9f239ec71d | ||
|
|
5ba01ec4cc | ||
|
|
3384cc4ee9 | ||
|
|
2c3d31f649 | ||
|
|
fbd42d7c47 | ||
|
|
05ab631eb6 | ||
|
|
bf2b320c81 | ||
|
|
2d72ee05a7 | ||
|
|
5e38ced392 | ||
|
|
44fe6961e7 | ||
|
|
08ed6a6fee | ||
|
|
4ccbd57ba8 | ||
|
|
28f0bd5903 | ||
|
|
18db901fb5 | ||
|
|
b178b48834 | ||
|
|
7cd26cd50e | ||
|
|
c8a38870d1 | ||
|
|
d6229ece87 | ||
|
|
03f84c337f | ||
|
|
2ff49aaf7a | ||
|
|
159abd36d9 | ||
|
|
8022b65991 | ||
|
|
e3fc56c200 | ||
|
|
9bff9bd346 | ||
|
|
427dd93088 | ||
|
|
d195f6100a | ||
|
|
c66e6db498 | ||
|
|
b691ef33f8 | ||
|
|
111f35b242 |
51 changed files with 3012 additions and 2106 deletions
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
|
|
@ -28,7 +28,7 @@ jobs:
|
|||
run: sudo apt-get update && sudo apt-get install -y liblzma-dev
|
||||
|
||||
- name: Run Clippy
|
||||
run: cargo clippy --all-targets --all-features
|
||||
run: cargo clippy --all-features
|
||||
|
||||
- name: no features -- test (without doctest)
|
||||
run: cargo test --lib --no-default-features
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -4,4 +4,5 @@ out
|
|||
.direnv
|
||||
.envrc
|
||||
result
|
||||
mutants.*
|
||||
mutants.*
|
||||
tarpaulin-report.html
|
||||
|
|
|
|||
288
Cargo.lock
generated
288
Cargo.lock
generated
|
|
@ -58,6 +58,12 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
|
|
@ -70,52 +76,30 @@ dependencies = [
|
|||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b89e7c29231c673a61a46e722602bcd138298f6b9e81e71119693534585f5c"
|
||||
checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
|
||||
dependencies = [
|
||||
"bzip2-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bzip2-sys"
|
||||
version = "0.1.12+1.0.8"
|
||||
version = "0.1.13+1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ebc2f1a417f01e1da30ef264ee86ae31d2dcd2d603ea283d3c244a883ca2a9"
|
||||
checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.14"
|
||||
version = "1.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9"
|
||||
checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
|
@ -130,9 +114,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.29"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184"
|
||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
|
@ -140,9 +124,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.29"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9"
|
||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
|
@ -152,9 +136,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.28"
|
||||
version = "4.5.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
|
@ -174,15 +158,6 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
|
|
@ -192,48 +167,16 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.35"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
|
||||
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
|
|
@ -241,24 +184,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -267,104 +212,88 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
||||
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
dependencies = [
|
||||
"getrandom 0.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.25"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.4"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.3"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.31"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
|
|
@ -398,7 +327,7 @@ version = "0.6.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -413,7 +342,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "servicepoint"
|
||||
version = "0.13.2"
|
||||
version = "0.14.0"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bzip2",
|
||||
|
|
@ -424,21 +353,9 @@ dependencies = [
|
|||
"rand",
|
||||
"rust-lzma",
|
||||
"thiserror",
|
||||
"tungstenite",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
|
@ -453,9 +370,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.98"
|
||||
version = "2.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -470,59 +387,29 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.11"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
|
|
@ -536,18 +423,21 @@ version = "0.2.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
|
@ -621,6 +511,15 @@ version = "0.52.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
|
|
@ -632,19 +531,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
version = "0.8.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
version = "0.8.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -653,27 +551,27 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.2"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.2.1"
|
||||
version = "7.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
|
||||
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.0.13+zstd.1.5.6"
|
||||
version = "2.0.15+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
|
||||
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
|
|
|
|||
55
Cargo.toml
55
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "servicepoint"
|
||||
version = "0.13.2"
|
||||
version = "0.14.0"
|
||||
publish = true
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
|
@ -9,6 +9,7 @@ homepage = "https://docs.rs/crate/servicepoint"
|
|||
repository = "https://git.berlin.ccc.de/servicepoint/servicepoint"
|
||||
readme = "README.md"
|
||||
keywords = ["cccb", "cccb-servicepoint"]
|
||||
rust-version = "1.70.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["rlib"]
|
||||
|
|
@ -21,20 +22,17 @@ bzip2 = { version = "0.5", optional = true }
|
|||
zstd = { version = "0.13", optional = true }
|
||||
rust-lzma = { version = "0.6", optional = true }
|
||||
rand = { version = "0.8", optional = true }
|
||||
tungstenite = { version = "0.26", optional = true }
|
||||
once_cell = { version = "1.20", optional = true }
|
||||
thiserror = "2.0"
|
||||
|
||||
[features]
|
||||
default = ["compression_lzma", "protocol_udp", "cp437"]
|
||||
default = ["compression_lzma", "cp437"]
|
||||
compression_zlib = ["dep:flate2"]
|
||||
compression_bzip2 = ["dep:bzip2"]
|
||||
compression_lzma = ["dep:rust-lzma"]
|
||||
compression_zstd = ["dep:zstd"]
|
||||
all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"]
|
||||
rand = ["dep:rand"]
|
||||
protocol_udp = []
|
||||
protocol_websocket = ["dep:tungstenite"]
|
||||
cp437 = ["dep:once_cell"]
|
||||
|
||||
[[example]]
|
||||
|
|
@ -45,16 +43,55 @@ required-features = ["rand"]
|
|||
name = "game_of_life"
|
||||
required-features = ["rand"]
|
||||
|
||||
[[example]]
|
||||
name = "websocket"
|
||||
required-features = ["protocol_websocket"]
|
||||
|
||||
[dev-dependencies]
|
||||
# for examples
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
|
||||
[lints.rust]
|
||||
missing-docs = "warn"
|
||||
deprecated-safe = "warn"
|
||||
future-incompatible = "warn"
|
||||
keyword-idents = "warn"
|
||||
let-underscore = "warn"
|
||||
nonstandard-style = "warn"
|
||||
refining_impl_trait_reachable = "warn"
|
||||
rust-2024-compatibility = "warn"
|
||||
|
||||
[lints.clippy]
|
||||
## Categories
|
||||
complexity = {level = "warn", priority = -1 }
|
||||
perf = {level = "warn", priority = -1 }
|
||||
style = {level = "warn", priority = -1 }
|
||||
pedantic = {level = "warn", priority = -1 }
|
||||
|
||||
## Blacklist
|
||||
unwrap_used = "warn"
|
||||
expect_used = "warn"
|
||||
panic = "warn"
|
||||
incompatible_msrv = "forbid"
|
||||
allow_attributes_without_reason = "warn"
|
||||
|
||||
## Whitelist
|
||||
# Too many false positives as often a module only contains one struct that is re-exported at top-level
|
||||
module_name_repetitions = "allow"
|
||||
# The pretty detailed exception types should be enough for now
|
||||
missing_errors_doc = "allow"
|
||||
# The few places where a panic is triggered in code are inspected and should never panic
|
||||
missing_panics_doc = "allow"
|
||||
# Does not work for all types, but should probably be fixed at some point
|
||||
iter_without_into_iter = "allow"
|
||||
|
||||
[lints.rustdoc]
|
||||
private_doc_tests = "warn"
|
||||
unescaped_backticks = "warn"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[profile.size-optimized]
|
||||
inherits = "release"
|
||||
opt-level = 'z' # Optimize for size
|
||||
lto = true # Enable link-time optimization
|
||||
codegen-units = 1 # Reduce number of codegen units to increase optimizations
|
||||
panic = 'abort' # Abort on panic
|
||||
strip = true # Strip symbols from binary
|
||||
96
README.md
96
README.md
|
|
@ -1,9 +1,11 @@
|
|||
# servicepoint
|
||||
|
||||
[](https://git.berlin.ccc.de/servicepoint/servicepoint/releases)
|
||||
[](https://crates.io/crates/servicepoint)
|
||||
[](https://crates.io/crates/servicepoint)
|
||||
[](https://docs.rs/servicepoint/latest/servicepoint/)
|
||||
[](./LICENSE)
|
||||
[](https://git.berlin.ccc.de/servicepoint/servicepoint)
|
||||
|
||||
In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wall. It is called "Service Point
|
||||
Display" or "Airport Display".
|
||||
|
|
@ -12,24 +14,22 @@ This crate contains a library for parsing, encoding and sending packets to this
|
|||
The library itself is written in Rust, but can be used from multiple languages
|
||||
via [language bindings](#supported-language-bindings).
|
||||
|
||||
This project moved
|
||||
to [git.berlin.ccc.de/servicepoint/servicepoint](https://git.berlin.ccc.de/servicepoint/servicepoint).
|
||||
The [GitHub repository](https://github.com/cccb/servicepoint) remains available as a mirror.
|
||||
|
||||
## Examples
|
||||
|
||||
```rust no_run
|
||||
use std::net::UdpSocket;
|
||||
// everything you need is in the top-level
|
||||
use servicepoint::*;
|
||||
|
||||
fn main() {
|
||||
// establish connection
|
||||
let connection = Connection::open("172.23.42.29:2342")
|
||||
.expect("connection failed");
|
||||
// this should be the IP of the real display @CCCB
|
||||
let destination = "172.23.42.29:2342";
|
||||
|
||||
// clear screen content
|
||||
connection.send(Command::Clear)
|
||||
.expect("send failed");
|
||||
// establish connection
|
||||
let connection = UdpSocket::bind(destination).expect("connection failed");
|
||||
|
||||
// clear screen content using the UdpSocketExt
|
||||
connection.send_command(ClearCommand).expect("send failed");
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ or
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
servicepoint = "0.13.2"
|
||||
servicepoint = "0.14.0"
|
||||
```
|
||||
|
||||
## Note on stability
|
||||
|
|
@ -61,21 +61,32 @@ There should be no breaking changes in patch releases, but there may also be fea
|
|||
|
||||
All of this means for you: please specify the full version including patch in your Cargo.toml until 1.0 is released.
|
||||
|
||||
Release notes are published [here](https://git.berlin.ccc.de/servicepoint/servicepoint/releases), please check them before updating.
|
||||
|
||||
Currently, this crate requires Rust [v1.70](https://releases.rs/docs/1.70.0/) from June 2023.
|
||||
|
||||
## Features
|
||||
|
||||
This library has multiple optional dependencies.
|
||||
You can choose to (not) include them by toggling the related features.
|
||||
|
||||
| Name | Default | Description | Dependencies |
|
||||
|--------------------|---------|----------------------------------------------|-----------------------------------------------------|
|
||||
| protocol_udp | true | `Connection::Udp` | |
|
||||
| cp437 | true | Conversion to and from CP-437 | [once_cell](https://crates.io/crates/once_cell) |
|
||||
| compression_lzma | true | Enable additional compression algo | [rust-lzma](https://crates.io/crates/rust-lzma) |
|
||||
| compression_zlib | false | Enable additional compression algo | [flate2](https://crates.io/crates/flate2) |
|
||||
| compression_bzip2 | false | Enable additional compression algo | [bzip2](https://crates.io/crates/bzip2) |
|
||||
| compression_zstd | false | Enable additional compression algo | [zstd](https://crates.io/crates/zstd) |
|
||||
| protocol_websocket | false | `Connection::WebSocket` | [tungstenite](https://crates.io/crates/tungstenite) |
|
||||
| rand | false | `impl Distribution<Brightness> for Standard` | [rand](https://crates.io/crates/rand) |
|
||||
| Name | Default | Description | Dependencies |
|
||||
|-------------------|---------|----------------------------------------------|-------------------------------------------------|
|
||||
| cp437 | true | Conversion to and from CP-437 | [once_cell](https://crates.io/crates/once_cell) |
|
||||
| compression_lzma | true | Enable additional compression algorithm | [rust-lzma](https://crates.io/crates/rust-lzma) |
|
||||
| compression_zlib | false | Enable additional compression algorithm | [flate2](https://crates.io/crates/flate2) |
|
||||
| compression_bzip2 | false | Enable additional compression algorithm | [bzip2](https://crates.io/crates/bzip2) |
|
||||
| compression_zstd | false | Enable additional compression algorithm | [zstd](https://crates.io/crates/zstd) |
|
||||
| rand | false | `impl Distribution<Brightness> for Standard` | [rand](https://crates.io/crates/rand) |
|
||||
|
||||
Es an example, if you only want zlib compression:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
servicepoint = { version = "0.14.0", default-features = false, features = ["compression_zlib"] }
|
||||
```
|
||||
|
||||
If you are looking at features to minimize binary size: take a look at the `tiny_announce`-example!
|
||||
|
||||
## Supported language bindings
|
||||
|
||||
|
|
@ -91,17 +102,14 @@ You can choose to (not) include them by toggling the related features.
|
|||
|
||||
## Projects using the library
|
||||
|
||||
- screen simulator (rust): [servicepoint-simulator](https://git.berlin.ccc.de/servicepoint/servicepoint-simulator)
|
||||
- A bunch of projects (C): [arfst23/ServicePoint](https://github.com/arfst23/ServicePoint), including
|
||||
- a CLI tool to display image files on the display or use the display as a TTY
|
||||
- a BSD games robots clone
|
||||
- a split-flap-display simulator
|
||||
- animations that play on the display
|
||||
- tanks game (C#): [servicepoint-tanks](https://github.com/kaesaecracker/cccb-tanks-cs)
|
||||
- cellular automata slideshow (rust): [servicepoint-life](https://github.com/kaesaecracker/servicepoint-life)
|
||||
- partial typescript implementation inspired by this library and browser
|
||||
stream: [cccb-servicepoint-browser](https://github.com/SamuelScheit/cccb-servicepoint-browser)
|
||||
- a CLI, can also share your screen: [servicepoint-cli](https://git.berlin.ccc.de/servicepoint/servicepoint-cli)
|
||||
- [servicepoint-simulator](https://git.berlin.ccc.de/servicepoint/servicepoint-simulator): a screen simulator written in rust
|
||||
- [servicepoint-tanks](https://git.berlin.ccc.de/vinzenz/servicepoint-tanks): a multiplayer game written in C# with a second screen in the browser written in React/Typescript
|
||||
- [servicepoint-life](https://git.berlin.ccc.de/vinzenz/servicepoint-life): a cellular automata slideshow written in rust
|
||||
- [servicepoint-cli](https://git.berlin.ccc.de/servicepoint/servicepoint-cli): a CLI that can:
|
||||
- share (stream) your screen
|
||||
- send image files with dithering
|
||||
- clear the display
|
||||
- ...
|
||||
|
||||
To add yourself to the list, open a pull request.
|
||||
|
||||
|
|
@ -110,10 +118,28 @@ bigger collection of projects, including some not related to this library.
|
|||
|
||||
If you have access, there is even more software linked in [the wiki](https://wiki.berlin.ccc.de/LED-Riesendisplay).
|
||||
|
||||
Some more related projects:
|
||||
|
||||
- [cccb-servicepoint-browser](https://github.com/SamuelScheit/cccb-servicepoint-browser): a partial typescript implementation inspired by this library and browser stream
|
||||
- [arfst23/ServicePoint](https://github.com/arfst23/ServicePoint): a bunch of projects in C that [used to](https://zerforschen.plus/posts/tiny-binaries-rust/) use the C bindings
|
||||
- a CLI tool to display image files on the display or use the display as a TTY
|
||||
- a BSD games robots clone
|
||||
- a split-flap-display simulator
|
||||
- animations that play on the display
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
You are welcome to contribute, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
## What happened to servicepoint2?
|
||||
## History
|
||||
|
||||
After `servicepoint2` has been merged into `servicepoint`, `servicepoint2` will not continue to get any updates.
|
||||
### Move to Forgejo
|
||||
|
||||
This project moved
|
||||
to [git.berlin.ccc.de/servicepoint/servicepoint](https://git.berlin.ccc.de/servicepoint/servicepoint).
|
||||
The [GitHub repository](https://github.com/cccb/servicepoint) remains available as a mirror.
|
||||
|
||||
|
||||
### What happened to servicepoint2?
|
||||
|
||||
`servicepoint2` was a fork of `servicepoint`. Since `servicepoint2` has been merged into `servicepoint`, `servicepoint2` did not get any updates.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
//! An example for how to send text to the display.
|
||||
|
||||
use clap::Parser;
|
||||
use servicepoint::*;
|
||||
use servicepoint::{
|
||||
CharGrid, CharGridCommand, ClearCommand, UdpSocketExt, TILE_WIDTH,
|
||||
};
|
||||
use std::net::UdpSocket;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
|
|
@ -31,18 +34,18 @@ fn main() {
|
|||
cli.text.push("Hello, CCCB!".to_string());
|
||||
}
|
||||
|
||||
let connection = Connection::open(&cli.destination)
|
||||
let connection = UdpSocket::bind(&cli.destination)
|
||||
.expect("could not connect to display");
|
||||
|
||||
if cli.clear {
|
||||
connection
|
||||
.send(Command::Clear)
|
||||
.send_command(ClearCommand)
|
||||
.expect("sending clear failed");
|
||||
}
|
||||
|
||||
let text = cli.text.join("\n");
|
||||
let grid = CharGrid::wrap_str(TILE_WIDTH, &text);
|
||||
let command: CharGridCommand = CharGrid::wrap_str(TILE_WIDTH, &text).into();
|
||||
connection
|
||||
.send(Command::Utf8Data(Origin::ZERO, grid))
|
||||
.send_command(command)
|
||||
.expect("sending text failed");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
//! Show a brightness level test pattern on screen
|
||||
|
||||
use clap::Parser;
|
||||
use servicepoint::*;
|
||||
use servicepoint::{
|
||||
Bitmap, BitmapCommand, Brightness, BrightnessGrid, BrightnessGridCommand,
|
||||
DataRef, Grid, UdpSocketExt, TILE_HEIGHT, TILE_WIDTH,
|
||||
};
|
||||
use std::net::UdpSocket;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
|
|
@ -11,27 +15,23 @@ struct Cli {
|
|||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
let connection = Connection::open(cli.destination)
|
||||
.expect("could not connect to display");
|
||||
let connection =
|
||||
UdpSocket::bind(cli.destination).expect("could not connect to display");
|
||||
|
||||
let mut pixels = Bitmap::max_sized();
|
||||
pixels.fill(true);
|
||||
let mut bitmap = Bitmap::max_sized();
|
||||
bitmap.fill(true);
|
||||
|
||||
let command = Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
pixels,
|
||||
CompressionCode::default(),
|
||||
);
|
||||
connection.send(command).expect("send failed");
|
||||
connection
|
||||
.send_command(BitmapCommand::from(bitmap))
|
||||
.expect("send failed");
|
||||
|
||||
let max_brightness: u8 = Brightness::MAX.into();
|
||||
let mut brightnesses = BrightnessGrid::new(TILE_WIDTH, TILE_HEIGHT);
|
||||
for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() {
|
||||
let level = index as u8 % max_brightness;
|
||||
let level = (index % u8::MAX as usize) as u8 % max_brightness;
|
||||
*byte = Brightness::try_from(level).unwrap();
|
||||
}
|
||||
|
||||
connection
|
||||
.send(Command::CharBrightness(Origin::ZERO, brightnesses))
|
||||
.expect("send failed");
|
||||
let command: BrightnessGridCommand = brightnesses.into();
|
||||
connection.send_command(command).expect("send failed");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
use clap::Parser;
|
||||
use rand::{distributions, Rng};
|
||||
use servicepoint::*;
|
||||
use std::thread;
|
||||
use servicepoint::{Bitmap, BitmapCommand, Grid, UdpSocketExt, FRAME_PACING};
|
||||
use std::{net::UdpSocket, thread};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
|
|
@ -16,17 +16,13 @@ struct Cli {
|
|||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
let connection = Connection::open(&cli.destination)
|
||||
let connection = UdpSocket::bind(&cli.destination)
|
||||
.expect("could not connect to display");
|
||||
let mut field = make_random_field(cli.probability);
|
||||
|
||||
loop {
|
||||
let command = Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
field.clone(),
|
||||
CompressionCode::default(),
|
||||
);
|
||||
connection.send(command).expect("could not send");
|
||||
let command = BitmapCommand::from(field.clone());
|
||||
connection.send_command(command).expect("could not send");
|
||||
thread::sleep(FRAME_PACING);
|
||||
field = iteration(field);
|
||||
}
|
||||
|
|
@ -39,10 +35,8 @@ fn iteration(field: Bitmap) -> Bitmap {
|
|||
let old_state = field.get(x, y);
|
||||
let neighbors = count_neighbors(&field, x as i32, y as i32);
|
||||
|
||||
let new_state = matches!(
|
||||
(old_state, neighbors),
|
||||
(true, 2) | (true, 3) | (false, 3)
|
||||
);
|
||||
let new_state =
|
||||
matches!((old_state, neighbors), (true, 2 | 3) | (false, 3));
|
||||
next.set(x, y, new_state);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
//! A simple example for how to send pixel data to the display.
|
||||
|
||||
use clap::Parser;
|
||||
use servicepoint::*;
|
||||
use std::thread;
|
||||
use servicepoint::{
|
||||
Bitmap, BitmapCommand, Grid, UdpSocketExt, FRAME_PACING, PIXEL_HEIGHT,
|
||||
PIXEL_WIDTH,
|
||||
};
|
||||
use std::{net::UdpSocket, thread};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
|
|
@ -11,23 +14,19 @@ struct Cli {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let connection = Connection::open(Cli::parse().destination)
|
||||
let connection = UdpSocket::bind(Cli::parse().destination)
|
||||
.expect("could not connect to display");
|
||||
|
||||
let mut pixels = Bitmap::max_sized();
|
||||
let mut bitmap = Bitmap::max_sized();
|
||||
for x_offset in 0..usize::MAX {
|
||||
pixels.fill(false);
|
||||
bitmap.fill(false);
|
||||
|
||||
for y in 0..PIXEL_HEIGHT {
|
||||
pixels.set((y + x_offset) % PIXEL_WIDTH, y, true);
|
||||
bitmap.set((y + x_offset) % PIXEL_WIDTH, y, true);
|
||||
}
|
||||
|
||||
let command = Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
pixels.clone(),
|
||||
CompressionCode::default(),
|
||||
);
|
||||
connection.send(command).expect("send failed");
|
||||
let command = BitmapCommand::from(bitmap.clone());
|
||||
connection.send_command(command).expect("send failed");
|
||||
thread::sleep(FRAME_PACING);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,12 @@
|
|||
|
||||
use clap::Parser;
|
||||
use rand::Rng;
|
||||
use servicepoint::*;
|
||||
use std::time::Duration;
|
||||
use servicepoint::{
|
||||
Bitmap, BitmapCommand, Brightness, BrightnessGrid, BrightnessGridCommand,
|
||||
GlobalBrightnessCommand, Grid, Origin, UdpSocketExt,
|
||||
TILE_HEIGHT, TILE_WIDTH,
|
||||
};
|
||||
use std::{net::UdpSocket, time::Duration};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
|
|
@ -19,7 +23,7 @@ struct Cli {
|
|||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
let connection = Connection::open(cli.destination)
|
||||
let connection = UdpSocket::bind_connect(cli.destination)
|
||||
.expect("could not connect to display");
|
||||
let wait_duration = Duration::from_millis(cli.wait_ms);
|
||||
|
||||
|
|
@ -28,17 +32,14 @@ fn main() {
|
|||
let mut filled_grid = Bitmap::max_sized();
|
||||
filled_grid.fill(true);
|
||||
|
||||
let command = Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
filled_grid,
|
||||
CompressionCode::default(),
|
||||
);
|
||||
connection.send(command).expect("send failed");
|
||||
let command = BitmapCommand::from(filled_grid);
|
||||
connection.send_command(command).expect("send failed");
|
||||
}
|
||||
|
||||
// set all pixels to the same random brightness
|
||||
let mut rng = rand::thread_rng();
|
||||
connection.send(Command::Brightness(rng.gen())).unwrap();
|
||||
let command: GlobalBrightnessCommand = rng.r#gen::<Brightness>().into();
|
||||
connection.send_command(command).unwrap();
|
||||
|
||||
// continuously update random windows to new random brightness
|
||||
loop {
|
||||
|
|
@ -54,12 +55,12 @@ fn main() {
|
|||
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
luma.set(x, y, rng.gen());
|
||||
luma.set(x, y, rng.r#gen());
|
||||
}
|
||||
}
|
||||
|
||||
connection
|
||||
.send(Command::CharBrightness(origin, luma))
|
||||
.send_command(BrightnessGridCommand { origin, grid: luma })
|
||||
.unwrap();
|
||||
std::thread::sleep(wait_duration);
|
||||
}
|
||||
|
|
|
|||
47
examples/tiny_announce.rs
Normal file
47
examples/tiny_announce.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
//! An example for how to send text to the display - but optimized for minimal binary size.
|
||||
//!
|
||||
//! See [zerforschen.plus/posts/tiny-binaries-rust](https://zerforschen.plus/posts/tiny-binaries-rust/)
|
||||
//! for details.
|
||||
//!
|
||||
//! The bulk of optimizations are compiler options, though there are some code changes that together
|
||||
//! make a huge difference.
|
||||
//!
|
||||
//! To build this example inside this repository for the smallest possible size, you can run:
|
||||
//! ```sh
|
||||
//! RUSTFLAGS="-Zlocation-detail=none -Zfmt-debug=none" \
|
||||
//! cargo build \
|
||||
//! --example=tiny_announce \
|
||||
//! --profile=size-optimized \
|
||||
//! --no-default-features --features=protocol_udp \
|
||||
//! -Zbuild-std="core,std,alloc,proc_macro,panic_abort" \
|
||||
//! -Zbuild-std-features="panic_immediate_abort"
|
||||
//!```
|
||||
//!
|
||||
//! This requires unstable rust.
|
||||
|
||||
#![no_main]
|
||||
|
||||
use servicepoint::{CharGrid, CharGridCommand, ClearCommand, UdpSocketExt};
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
|
||||
/// This is the entry point of the example.
|
||||
/// `#![no_main]` is used to remove the default rust main
|
||||
/// Because we use `#![no_main]`, this is a C-style main function.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
let addr = SocketAddr::from(([172, 23, 42, 29], 2342));
|
||||
|
||||
let connection = UdpSocket::bind(addr).unwrap();
|
||||
connection.send_command(ClearCommand).unwrap();
|
||||
|
||||
let grid = CharGrid::from_vec(
|
||||
5,
|
||||
vec!['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
connection
|
||||
.send_command(CharGridCommand::from(grid))
|
||||
.unwrap();
|
||||
0
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
//! Example for how to use the WebSocket connection
|
||||
|
||||
use servicepoint::{
|
||||
Bitmap, Command, CompressionCode, Connection, Grid, Origin,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let connection =
|
||||
Connection::open_websocket("ws://localhost:8080".parse().unwrap())
|
||||
.unwrap();
|
||||
|
||||
connection.send(Command::Clear).unwrap();
|
||||
|
||||
let mut pixels = Bitmap::max_sized();
|
||||
pixels.fill(true);
|
||||
|
||||
connection
|
||||
.send(Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
pixels,
|
||||
CompressionCode::default(),
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
//! 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::time::Duration;
|
||||
use servicepoint::{
|
||||
Bitmap, BitmapCommand, Grid, UdpSocketExt, FRAME_PACING, PIXEL_HEIGHT,
|
||||
PIXEL_WIDTH,
|
||||
};
|
||||
use std::{net::UdpSocket, thread, time::Duration};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
|
|
@ -20,8 +23,8 @@ fn main() {
|
|||
Duration::from_millis(cli.time / PIXEL_WIDTH as u64),
|
||||
);
|
||||
|
||||
let connection = Connection::open(cli.destination)
|
||||
.expect("could not connect to display");
|
||||
let connection =
|
||||
UdpSocket::bind(cli.destination).expect("could not connect to display");
|
||||
|
||||
let mut enabled_pixels = Bitmap::max_sized();
|
||||
enabled_pixels.fill(true);
|
||||
|
|
@ -31,12 +34,9 @@ fn main() {
|
|||
enabled_pixels.set(x_offset % PIXEL_WIDTH, y, false);
|
||||
}
|
||||
|
||||
let command = BitmapCommand::from(enabled_pixels.clone());
|
||||
connection
|
||||
.send(Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
enabled_pixels.clone(),
|
||||
CompressionCode::default(),
|
||||
))
|
||||
.send_command(command)
|
||||
.expect("could not send command to display");
|
||||
thread::sleep(sleep_duration);
|
||||
}
|
||||
|
|
|
|||
12
flake.lock
generated
12
flake.lock
generated
|
|
@ -7,11 +7,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1736429655,
|
||||
"narHash": "sha256-BwMekRuVlSB9C0QgwKMICiJ5EVbLGjfe4qyueyNQyGI=",
|
||||
"lastModified": 1745925850,
|
||||
"narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"rev": "0621e47bd95542b8e1ce2ee2d65d6a1f887a13ce",
|
||||
"rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -22,11 +22,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1739357830,
|
||||
"narHash": "sha256-9xim3nJJUFbVbJCz48UP4fGRStVW5nv4VdbimbKxJ3I=",
|
||||
"lastModified": 1746183838,
|
||||
"narHash": "sha256-kwaaguGkAqTZ1oK0yXeQ3ayYjs8u/W7eEfrFpFfIDFA=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0ff09db9d034a04acd4e8908820ba0b410d7a33a",
|
||||
"rev": "bf3287dac860542719fe7554e21e686108716879",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
|
|
@ -137,6 +137,9 @@
|
|||
clippy
|
||||
cargo-expand
|
||||
cargo-tarpaulin
|
||||
cargo-semver-checks
|
||||
cargo-show-asm
|
||||
cargo-flamegraph
|
||||
];
|
||||
})
|
||||
];
|
||||
|
|
|
|||
2
generate-coverage
Executable file
2
generate-coverage
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
#/usr/bin/env bash
|
||||
cargo tarpaulin --out Html --all-features
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
/// 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::*;
|
||||
}
|
||||
|
|
@ -9,15 +9,16 @@ use rand::{
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use servicepoint::{Brightness, Command, Connection};
|
||||
/// # use servicepoint::*;
|
||||
/// let b = Brightness::MAX;
|
||||
/// let val: u8 = b.into();
|
||||
///
|
||||
/// let b = Brightness::try_from(7).unwrap();
|
||||
/// # let connection = Connection::Fake;
|
||||
/// let result = connection.send(Command::Brightness(b));
|
||||
/// # let connection = FakeConnection;
|
||||
/// let result = connection.send_command(GlobalBrightnessCommand::from(b));
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
|
||||
#[repr(transparent)]
|
||||
pub struct Brightness(u8);
|
||||
|
||||
impl From<Brightness> for u8 {
|
||||
|
|
@ -50,9 +51,10 @@ impl Brightness {
|
|||
/// lowest possible brightness value, 0
|
||||
pub const MIN: Brightness = Brightness(0);
|
||||
|
||||
/// Create a brightness value without returning an error for brightnesses above [Brightness::MAX].
|
||||
/// Create a brightness value without returning an error for brightnesses above [`Brightness::MAX`].
|
||||
///
|
||||
/// returns: the specified value as a [Brightness], or [Brightness::MAX].
|
||||
/// returns: the specified value as a [Brightness], or [`Brightness::MAX`].
|
||||
#[must_use]
|
||||
pub fn saturating_from(value: u8) -> Brightness {
|
||||
if value > Brightness::MAX.into() {
|
||||
Brightness::MAX
|
||||
|
|
@ -90,7 +92,7 @@ mod tests {
|
|||
fn rand_brightness() {
|
||||
let mut rng = rand::thread_rng();
|
||||
for _ in 0..100 {
|
||||
let _: Brightness = rng.gen();
|
||||
let _: Brightness = rng.r#gen();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +106,10 @@ mod tests {
|
|||
#[cfg(feature = "rand")]
|
||||
fn test() {
|
||||
let mut rng = rand::thread_rng();
|
||||
assert_ne!(rng.gen::<Brightness>(), rng.gen());
|
||||
// two so test failure is less likely
|
||||
assert_ne!(
|
||||
[rng.r#gen::<Brightness>(), rng.r#gen()],
|
||||
[rng.r#gen(), rng.r#gen()]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
968
src/command.rs
968
src/command.rs
|
|
@ -1,968 +0,0 @@
|
|||
use crate::command_code::CommandCode;
|
||||
use crate::compression::into_decompressed;
|
||||
use crate::*;
|
||||
|
||||
/// Type alias for documenting the meaning of the u16 in enum values
|
||||
pub type Offset = usize;
|
||||
|
||||
/// A low-level display command.
|
||||
///
|
||||
/// This struct and associated functions implement the UDP protocol for the display.
|
||||
///
|
||||
/// To send a [Command], use a [connection][crate::Connection].
|
||||
///
|
||||
/// # Available commands
|
||||
///
|
||||
/// To send text, take a look at [Command::Cp437Data].
|
||||
///
|
||||
/// To draw pixels, the easiest command to use is [Command::BitmapLinearWin].
|
||||
///
|
||||
/// The other BitmapLinear-Commands operate on a region of pixel memory directly.
|
||||
/// [Command::BitmapLinear] overwrites a region.
|
||||
/// [Command::BitmapLinearOr], [Command::BitmapLinearAnd] and [Command::BitmapLinearXor] apply logical operations per pixel.
|
||||
///
|
||||
/// Out of bounds operations may be truncated or ignored by the display.
|
||||
///
|
||||
/// # Compression
|
||||
///
|
||||
/// Some commands can contain compressed payloads.
|
||||
/// To get started, use [CompressionCode::default].
|
||||
///
|
||||
/// If you want to archive the best performance (e.g. latency),
|
||||
/// you can try the different compression algorithms for your hardware and use case.
|
||||
///
|
||||
/// In memory, the payload is not compressed in the [Command].
|
||||
/// Payload (de-)compression happens when converting the [Command] into a [Packet] or vice versa.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use servicepoint::{Brightness, Command, Connection, Packet};
|
||||
/// #
|
||||
/// // create command
|
||||
/// let command = Command::Brightness(Brightness::MAX);
|
||||
///
|
||||
/// // turn command into Packet
|
||||
/// let packet: Packet = command.clone().into();
|
||||
///
|
||||
/// // read command from packet
|
||||
/// let round_tripped = Command::try_from(packet).unwrap();
|
||||
///
|
||||
/// // round tripping produces exact copy
|
||||
/// assert_eq!(command, round_tripped);
|
||||
///
|
||||
/// // send command
|
||||
/// # let connection = Connection::Fake;
|
||||
/// connection.send(command).unwrap();
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Command {
|
||||
/// Set all pixels to the off state. Does not affect brightness.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, Connection};
|
||||
/// # let connection = Connection::Fake;
|
||||
/// connection.send(Command::Clear).unwrap();
|
||||
/// ```
|
||||
Clear,
|
||||
|
||||
/// Show text on the screen.
|
||||
///
|
||||
/// The text is sent in the form of a 2D grid of UTF-8 encoded characters (the default encoding in rust).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # 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 grid = CharGrid::from("Hello,\nWorld!");
|
||||
/// let grid = Cp437Grid::from(&grid);
|
||||
/// connection.send(Command::Cp437Data(Origin::ZERO, grid)).expect("send failed");
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, Connection, Cp437Grid, Origin};
|
||||
/// # let connection = Connection::Fake;
|
||||
/// let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap();
|
||||
/// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap();
|
||||
/// ```
|
||||
/// [CP-437]: https://en.wikipedia.org/wiki/Code_page_437
|
||||
Cp437Data(Origin<Tiles>, Cp437Grid),
|
||||
|
||||
/// Overwrites a rectangular region of pixels.
|
||||
///
|
||||
/// Origin coordinates must be divisible by 8.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, CompressionCode, Grid, Bitmap};
|
||||
/// # let connection = servicepoint::Connection::Fake;
|
||||
/// #
|
||||
/// let mut pixels = Bitmap::max_sized();
|
||||
/// // draw something to the pixels here
|
||||
/// # pixels.set(2, 5, true);
|
||||
///
|
||||
/// // create command to send pixels
|
||||
/// let command = Command::BitmapLinearWin(
|
||||
/// servicepoint::Origin::ZERO,
|
||||
/// pixels,
|
||||
/// CompressionCode::default()
|
||||
/// );
|
||||
///
|
||||
/// connection.send(command).expect("send failed");
|
||||
/// ```
|
||||
BitmapLinearWin(Origin<Pixels>, Bitmap, CompressionCode),
|
||||
|
||||
/// Set the brightness of all tiles to the same value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Brightness, Command, Connection};
|
||||
/// # let connection = Connection::Fake;
|
||||
/// let command = Command::Brightness(Brightness::MAX);
|
||||
/// connection.send(command).unwrap();
|
||||
/// ```
|
||||
Brightness(Brightness),
|
||||
|
||||
/// Set the brightness of individual tiles in a rectangular area of the display.
|
||||
CharBrightness(Origin<Tiles>, BrightnessGrid),
|
||||
|
||||
/// Set pixel data starting at the pixel offset on screen.
|
||||
///
|
||||
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
|
||||
/// once the starting row is full, overwriting will continue on column 0.
|
||||
///
|
||||
/// The contained [BitVec] is always uncompressed.
|
||||
BitmapLinear(Offset, BitVec, CompressionCode),
|
||||
|
||||
/// Set pixel data according to an and-mask starting at the offset.
|
||||
///
|
||||
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
|
||||
/// once the starting row is full, overwriting will continue on column 0.
|
||||
///
|
||||
/// The contained [BitVec] is always uncompressed.
|
||||
BitmapLinearAnd(Offset, BitVec, CompressionCode),
|
||||
|
||||
/// Set pixel data according to an or-mask starting at the offset.
|
||||
///
|
||||
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
|
||||
/// once the starting row is full, overwriting will continue on column 0.
|
||||
///
|
||||
/// The contained [BitVec] is always uncompressed.
|
||||
BitmapLinearOr(Offset, BitVec, CompressionCode),
|
||||
|
||||
/// Set pixel data according to a xor-mask starting at the offset.
|
||||
///
|
||||
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
|
||||
/// once the starting row is full, overwriting will continue on column 0.
|
||||
///
|
||||
/// The contained [BitVec] is always uncompressed.
|
||||
BitmapLinearXor(Offset, BitVec, CompressionCode),
|
||||
|
||||
/// Kills the udp daemon on the display, which usually results in a restart.
|
||||
///
|
||||
/// Please do not send this in your normal program flow.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, Connection};
|
||||
/// # let connection = Connection::Fake;
|
||||
/// connection.send(Command::HardReset).unwrap();
|
||||
/// ```
|
||||
HardReset,
|
||||
|
||||
/// <div class="warning">Untested</div>
|
||||
///
|
||||
/// Slowly decrease brightness until off or something like that?
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, Connection};
|
||||
/// # let connection = Connection::Fake;
|
||||
/// connection.send(Command::FadeOut).unwrap();
|
||||
/// ```
|
||||
FadeOut,
|
||||
|
||||
/// Legacy command code, gets ignored by the real display.
|
||||
///
|
||||
/// Might be useful as a noop package.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, Connection};
|
||||
/// # let connection = Connection::Fake;
|
||||
/// // this sends a packet that does nothing
|
||||
/// # #[allow(deprecated)]
|
||||
/// connection.send(Command::BitmapLegacy).unwrap();
|
||||
/// ```
|
||||
#[deprecated]
|
||||
BitmapLegacy,
|
||||
}
|
||||
|
||||
/// Err values for [Command::try_from].
|
||||
#[derive(Debug, PartialEq, thiserror::Error)]
|
||||
pub enum TryFromPacketError {
|
||||
/// the contained command code does not correspond to a known command
|
||||
#[error("The command code {0:?} does not correspond to a known command")]
|
||||
InvalidCommand(u16),
|
||||
/// the expected payload size was n, but size m was found
|
||||
#[error("the expected payload size was {0}, but size {1} was found")]
|
||||
UnexpectedPayloadSize(usize, usize),
|
||||
/// Header fields not needed for the command have been used.
|
||||
///
|
||||
/// Note that these commands would usually still work on the actual display.
|
||||
#[error("Header fields not needed for the command have been used")]
|
||||
ExtraneousHeaderValues,
|
||||
/// The contained compression code is not known. This could be of disabled features.
|
||||
#[error("The compression code {0:?} does not correspond to a known compression algorithm.")]
|
||||
InvalidCompressionCode(u16),
|
||||
/// Decompression of the payload failed. This can be caused by corrupted packets.
|
||||
#[error("The decompression of the payload failed")]
|
||||
DecompressionFailed,
|
||||
/// The given brightness value is out of bounds
|
||||
#[error("The given brightness value {0} is out of bounds.")]
|
||||
InvalidBrightness(u8),
|
||||
#[error(transparent)]
|
||||
InvalidUtf8(#[from] std::string::FromUtf8Error),
|
||||
}
|
||||
|
||||
impl TryFrom<Packet> for Command {
|
||||
type Error = TryFromPacketError;
|
||||
|
||||
/// Try to interpret the [Packet] as one containing a [Command]
|
||||
fn try_from(packet: Packet) -> Result<Self, Self::Error> {
|
||||
let Packet {
|
||||
header: Header {
|
||||
command_code, a, ..
|
||||
},
|
||||
..
|
||||
} = packet;
|
||||
let command_code = match CommandCode::try_from(command_code) {
|
||||
Err(()) => {
|
||||
return Err(TryFromPacketError::InvalidCommand(command_code));
|
||||
}
|
||||
Ok(value) => value,
|
||||
};
|
||||
|
||||
match command_code {
|
||||
CommandCode::Clear => {
|
||||
Self::packet_into_command_only(packet, Command::Clear)
|
||||
}
|
||||
CommandCode::Brightness => Self::packet_into_brightness(&packet),
|
||||
CommandCode::HardReset => {
|
||||
Self::packet_into_command_only(packet, Command::HardReset)
|
||||
}
|
||||
CommandCode::FadeOut => {
|
||||
Self::packet_into_command_only(packet, Command::FadeOut)
|
||||
}
|
||||
CommandCode::Cp437Data => Self::packet_into_cp437(&packet),
|
||||
CommandCode::CharBrightness => {
|
||||
Self::packet_into_char_brightness(&packet)
|
||||
}
|
||||
CommandCode::Utf8Data => Self::packet_into_utf8(&packet),
|
||||
#[allow(deprecated)]
|
||||
CommandCode::BitmapLegacy => Ok(Command::BitmapLegacy),
|
||||
CommandCode::BitmapLinear => {
|
||||
let (vec, compression) =
|
||||
Self::packet_into_linear_bitmap(packet)?;
|
||||
Ok(Command::BitmapLinear(a as Offset, vec, compression))
|
||||
}
|
||||
CommandCode::BitmapLinearAnd => {
|
||||
let (vec, compression) =
|
||||
Self::packet_into_linear_bitmap(packet)?;
|
||||
Ok(Command::BitmapLinearAnd(a as Offset, vec, compression))
|
||||
}
|
||||
CommandCode::BitmapLinearOr => {
|
||||
let (vec, compression) =
|
||||
Self::packet_into_linear_bitmap(packet)?;
|
||||
Ok(Command::BitmapLinearOr(a as Offset, vec, compression))
|
||||
}
|
||||
CommandCode::BitmapLinearXor => {
|
||||
let (vec, compression) =
|
||||
Self::packet_into_linear_bitmap(packet)?;
|
||||
Ok(Command::BitmapLinearXor(a as Offset, vec, compression))
|
||||
}
|
||||
CommandCode::BitmapLinearWinUncompressed => {
|
||||
Self::packet_into_bitmap_win(
|
||||
packet,
|
||||
CompressionCode::Uncompressed,
|
||||
)
|
||||
}
|
||||
#[cfg(feature = "compression_zlib")]
|
||||
CommandCode::BitmapLinearWinZlib => {
|
||||
Self::packet_into_bitmap_win(packet, CompressionCode::Zlib)
|
||||
}
|
||||
#[cfg(feature = "compression_bzip2")]
|
||||
CommandCode::BitmapLinearWinBzip2 => {
|
||||
Self::packet_into_bitmap_win(packet, CompressionCode::Bzip2)
|
||||
}
|
||||
#[cfg(feature = "compression_lzma")]
|
||||
CommandCode::BitmapLinearWinLzma => {
|
||||
Self::packet_into_bitmap_win(packet, CompressionCode::Lzma)
|
||||
}
|
||||
#[cfg(feature = "compression_zstd")]
|
||||
CommandCode::BitmapLinearWinZstd => {
|
||||
Self::packet_into_bitmap_win(packet, CompressionCode::Zstd)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Command {
|
||||
fn packet_into_bitmap_win(
|
||||
packet: Packet,
|
||||
compression: CompressionCode,
|
||||
) -> Result<Command, TryFromPacketError> {
|
||||
let Packet {
|
||||
header:
|
||||
Header {
|
||||
command_code: _,
|
||||
a: tiles_x,
|
||||
b: pixels_y,
|
||||
c: tile_w,
|
||||
d: pixel_h,
|
||||
},
|
||||
payload,
|
||||
} = packet;
|
||||
|
||||
let payload = match into_decompressed(compression, payload) {
|
||||
None => return Err(TryFromPacketError::DecompressionFailed),
|
||||
Some(decompressed) => decompressed,
|
||||
};
|
||||
|
||||
Ok(Command::BitmapLinearWin(
|
||||
Origin::new(tiles_x as usize * TILE_SIZE, pixels_y as usize),
|
||||
Bitmap::load(
|
||||
tile_w as usize * TILE_SIZE,
|
||||
pixel_h as usize,
|
||||
&payload,
|
||||
),
|
||||
compression,
|
||||
))
|
||||
}
|
||||
|
||||
/// Helper method for checking that a packet is empty and only contains a command code
|
||||
fn packet_into_command_only(
|
||||
packet: Packet,
|
||||
command: Command,
|
||||
) -> Result<Command, TryFromPacketError> {
|
||||
let Packet {
|
||||
header:
|
||||
Header {
|
||||
command_code: _,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
},
|
||||
payload,
|
||||
} = packet;
|
||||
if !payload.is_empty() {
|
||||
Err(TryFromPacketError::UnexpectedPayloadSize(0, payload.len()))
|
||||
} else if a != 0 || b != 0 || c != 0 || d != 0 {
|
||||
Err(TryFromPacketError::ExtraneousHeaderValues)
|
||||
} else {
|
||||
Ok(command)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper method for Packets into `BitmapLinear*`-Commands
|
||||
fn packet_into_linear_bitmap(
|
||||
packet: Packet,
|
||||
) -> Result<(BitVec, CompressionCode), TryFromPacketError> {
|
||||
let Packet {
|
||||
header:
|
||||
Header {
|
||||
b: length,
|
||||
c: sub,
|
||||
d: reserved,
|
||||
..
|
||||
},
|
||||
payload,
|
||||
} = packet;
|
||||
if reserved != 0 {
|
||||
return Err(TryFromPacketError::ExtraneousHeaderValues);
|
||||
}
|
||||
let sub = match CompressionCode::try_from(sub) {
|
||||
Err(()) => {
|
||||
return Err(TryFromPacketError::InvalidCompressionCode(sub));
|
||||
}
|
||||
Ok(value) => value,
|
||||
};
|
||||
let payload = match into_decompressed(sub, payload) {
|
||||
None => return Err(TryFromPacketError::DecompressionFailed),
|
||||
Some(value) => value,
|
||||
};
|
||||
if payload.len() != length as usize {
|
||||
return Err(TryFromPacketError::UnexpectedPayloadSize(
|
||||
length as usize,
|
||||
payload.len(),
|
||||
));
|
||||
}
|
||||
Ok((BitVec::from_vec(payload), sub))
|
||||
}
|
||||
|
||||
fn packet_into_char_brightness(
|
||||
packet: &Packet,
|
||||
) -> Result<Command, TryFromPacketError> {
|
||||
let Packet {
|
||||
header:
|
||||
Header {
|
||||
command_code: _,
|
||||
a: x,
|
||||
b: y,
|
||||
c: width,
|
||||
d: height,
|
||||
},
|
||||
payload,
|
||||
} = packet;
|
||||
|
||||
let grid = ByteGrid::load(*width as usize, *height as usize, payload);
|
||||
let grid = match BrightnessGrid::try_from(grid) {
|
||||
Ok(grid) => grid,
|
||||
Err(val) => return Err(TryFromPacketError::InvalidBrightness(val)),
|
||||
};
|
||||
|
||||
Ok(Command::CharBrightness(
|
||||
Origin::new(*x as usize, *y as usize),
|
||||
grid,
|
||||
))
|
||||
}
|
||||
|
||||
fn packet_into_brightness(
|
||||
packet: &Packet,
|
||||
) -> Result<Command, TryFromPacketError> {
|
||||
let Packet {
|
||||
header:
|
||||
Header {
|
||||
command_code: _,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
},
|
||||
payload,
|
||||
} = packet;
|
||||
if payload.len() != 1 {
|
||||
return Err(TryFromPacketError::UnexpectedPayloadSize(
|
||||
1,
|
||||
payload.len(),
|
||||
));
|
||||
}
|
||||
|
||||
if *a != 0 || *b != 0 || *c != 0 || *d != 0 {
|
||||
return Err(TryFromPacketError::ExtraneousHeaderValues);
|
||||
}
|
||||
|
||||
match Brightness::try_from(payload[0]) {
|
||||
Ok(b) => Ok(Command::Brightness(b)),
|
||||
Err(_) => Err(TryFromPacketError::InvalidBrightness(payload[0])),
|
||||
}
|
||||
}
|
||||
|
||||
fn packet_into_cp437(
|
||||
packet: &Packet,
|
||||
) -> Result<Command, TryFromPacketError> {
|
||||
let Packet {
|
||||
header:
|
||||
Header {
|
||||
command_code: _,
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
},
|
||||
payload,
|
||||
} = packet;
|
||||
Ok(Command::Cp437Data(
|
||||
Origin::new(*a as usize, *b as usize),
|
||||
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)]
|
||||
mod tests {
|
||||
use crate::command::TryFromPacketError;
|
||||
use crate::command_code::CommandCode;
|
||||
use crate::{
|
||||
BitVec, Bitmap, Brightness, BrightnessGrid, CharGrid, Command,
|
||||
CompressionCode, Cp437Grid, Header, Origin, Packet, Pixels,
|
||||
};
|
||||
|
||||
fn round_trip(original: Command) {
|
||||
let packet: Packet = original.clone().into();
|
||||
let copy: Command = match Command::try_from(packet) {
|
||||
Ok(command) => command,
|
||||
Err(err) => panic!("could not reload {original:?}: {err:?}"),
|
||||
};
|
||||
assert_eq!(copy, original);
|
||||
}
|
||||
|
||||
fn all_compressions<'t>() -> &'t [CompressionCode] {
|
||||
&[
|
||||
CompressionCode::Uncompressed,
|
||||
#[cfg(feature = "compression_lzma")]
|
||||
CompressionCode::Lzma,
|
||||
#[cfg(feature = "compression_bzip2")]
|
||||
CompressionCode::Bzip2,
|
||||
#[cfg(feature = "compression_zlib")]
|
||||
CompressionCode::Zlib,
|
||||
#[cfg(feature = "compression_zstd")]
|
||||
CompressionCode::Zstd,
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_clear() {
|
||||
round_trip(Command::Clear);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_hard_reset() {
|
||||
round_trip(Command::HardReset);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_fade_out() {
|
||||
round_trip(Command::FadeOut);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_brightness() {
|
||||
round_trip(Command::Brightness(Brightness::try_from(6).unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(deprecated)]
|
||||
fn round_trip_bitmap_legacy() {
|
||||
round_trip(Command::BitmapLegacy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_char_brightness() {
|
||||
round_trip(Command::CharBrightness(
|
||||
Origin::new(5, 2),
|
||||
BrightnessGrid::new(7, 5),
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_cp437_data() {
|
||||
round_trip(Command::Cp437Data(Origin::new(5, 2), Cp437Grid::new(7, 5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_utf8_data() {
|
||||
round_trip(Command::Utf8Data(Origin::new(5, 2), CharGrid::new(7, 5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn round_trip_bitmap_linear() {
|
||||
for compression in all_compressions().iter().copied() {
|
||||
round_trip(Command::BitmapLinear(
|
||||
23,
|
||||
BitVec::repeat(false, 40),
|
||||
compression,
|
||||
));
|
||||
round_trip(Command::BitmapLinearAnd(
|
||||
23,
|
||||
BitVec::repeat(false, 40),
|
||||
compression,
|
||||
));
|
||||
round_trip(Command::BitmapLinearOr(
|
||||
23,
|
||||
BitVec::repeat(false, 40),
|
||||
compression,
|
||||
));
|
||||
round_trip(Command::BitmapLinearXor(
|
||||
23,
|
||||
BitVec::repeat(false, 40),
|
||||
compression,
|
||||
));
|
||||
round_trip(Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
Bitmap::max_sized(),
|
||||
compression,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_command() {
|
||||
let p = Packet {
|
||||
header: Header {
|
||||
command_code: 0xFF,
|
||||
a: 0x00,
|
||||
b: 0x00,
|
||||
c: 0x00,
|
||||
d: 0x00,
|
||||
},
|
||||
payload: vec![],
|
||||
};
|
||||
let result = Command::try_from(p);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(TryFromPacketError::InvalidCommand(0xFF))
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_extraneous_header_values_clear() {
|
||||
let p = Packet {
|
||||
header: Header {
|
||||
command_code: CommandCode::Clear.into(),
|
||||
a: 0x05,
|
||||
b: 0x00,
|
||||
c: 0x00,
|
||||
d: 0x00,
|
||||
},
|
||||
payload: vec![],
|
||||
};
|
||||
let result = Command::try_from(p);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(TryFromPacketError::ExtraneousHeaderValues)
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_extraneous_header_values_brightness() {
|
||||
let p = Packet {
|
||||
header: Header {
|
||||
command_code: CommandCode::Brightness.into(),
|
||||
a: 0x00,
|
||||
b: 0x13,
|
||||
c: 0x37,
|
||||
d: 0x00,
|
||||
},
|
||||
payload: vec![5],
|
||||
};
|
||||
let result = Command::try_from(p);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(TryFromPacketError::ExtraneousHeaderValues)
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_extraneous_header_hard_reset() {
|
||||
let p = Packet {
|
||||
header: Header {
|
||||
command_code: CommandCode::HardReset.into(),
|
||||
a: 0x00,
|
||||
b: 0x00,
|
||||
c: 0x00,
|
||||
d: 0x01,
|
||||
},
|
||||
payload: vec![],
|
||||
};
|
||||
let result = Command::try_from(p);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(TryFromPacketError::ExtraneousHeaderValues)
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_extraneous_header_fade_out() {
|
||||
let p = Packet {
|
||||
header: Header {
|
||||
command_code: CommandCode::FadeOut.into(),
|
||||
a: 0x10,
|
||||
b: 0x00,
|
||||
c: 0x00,
|
||||
d: 0x01,
|
||||
},
|
||||
payload: vec![],
|
||||
};
|
||||
let result = Command::try_from(p);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(TryFromPacketError::ExtraneousHeaderValues)
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_unexpected_payload() {
|
||||
let p = Packet {
|
||||
header: Header {
|
||||
command_code: CommandCode::FadeOut.into(),
|
||||
a: 0x00,
|
||||
b: 0x00,
|
||||
c: 0x00,
|
||||
d: 0x00,
|
||||
},
|
||||
payload: vec![5, 7],
|
||||
};
|
||||
let result = Command::try_from(p);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(TryFromPacketError::UnexpectedPayloadSize(0, 2))
|
||||
))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_decompression_failed_win() {
|
||||
for compression in all_compressions().iter().copied() {
|
||||
let p: Packet = Command::BitmapLinearWin(
|
||||
Origin::new(16, 8),
|
||||
Bitmap::new(8, 8),
|
||||
compression,
|
||||
)
|
||||
.into();
|
||||
|
||||
let Packet {
|
||||
header,
|
||||
mut payload,
|
||||
} = p;
|
||||
|
||||
// mangle it
|
||||
for byte in payload.iter_mut() {
|
||||
*byte -= *byte / 2;
|
||||
}
|
||||
|
||||
let p = Packet { header, payload };
|
||||
let result = Command::try_from(p);
|
||||
if compression != CompressionCode::Uncompressed {
|
||||
assert_eq!(result, Err(TryFromPacketError::DecompressionFailed))
|
||||
} else {
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_decompression_failed_and() {
|
||||
for compression in all_compressions().iter().copied() {
|
||||
let p: Packet = Command::BitmapLinearAnd(
|
||||
0,
|
||||
BitVec::repeat(false, 8),
|
||||
compression,
|
||||
)
|
||||
.into();
|
||||
let Packet {
|
||||
header,
|
||||
mut payload,
|
||||
} = p;
|
||||
|
||||
// mangle it
|
||||
for byte in payload.iter_mut() {
|
||||
*byte -= *byte / 2;
|
||||
}
|
||||
|
||||
let p = Packet { header, payload };
|
||||
let result = Command::try_from(p);
|
||||
if compression != CompressionCode::Uncompressed {
|
||||
assert_eq!(result, Err(TryFromPacketError::DecompressionFailed))
|
||||
} else {
|
||||
// when not compressing, there is no way to detect corrupted data
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unexpected_payload_size_brightness() {
|
||||
assert_eq!(
|
||||
Command::try_from(Packet {
|
||||
header: Header {
|
||||
command_code: CommandCode::Brightness.into(),
|
||||
a: 0,
|
||||
b: 0,
|
||||
c: 0,
|
||||
d: 0,
|
||||
},
|
||||
payload: vec!()
|
||||
}),
|
||||
Err(TryFromPacketError::UnexpectedPayloadSize(1, 0))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Command::try_from(Packet {
|
||||
header: Header {
|
||||
command_code: CommandCode::Brightness.into(),
|
||||
a: 0,
|
||||
b: 0,
|
||||
c: 0,
|
||||
d: 0,
|
||||
},
|
||||
payload: vec!(0, 0)
|
||||
}),
|
||||
Err(TryFromPacketError::UnexpectedPayloadSize(1, 2))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_reserved_used() {
|
||||
let Packet { header, payload } = Command::BitmapLinear(
|
||||
0,
|
||||
BitVec::repeat(false, 8),
|
||||
CompressionCode::Uncompressed,
|
||||
)
|
||||
.into();
|
||||
let Header {
|
||||
command_code: command,
|
||||
a: offset,
|
||||
b: length,
|
||||
c: sub,
|
||||
d: _reserved,
|
||||
} = header;
|
||||
let p = Packet {
|
||||
header: Header {
|
||||
command_code: command,
|
||||
a: offset,
|
||||
b: length,
|
||||
c: sub,
|
||||
d: 69,
|
||||
},
|
||||
payload,
|
||||
};
|
||||
assert_eq!(
|
||||
Command::try_from(p),
|
||||
Err(TryFromPacketError::ExtraneousHeaderValues)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_invalid_compression() {
|
||||
let Packet { header, payload } = Command::BitmapLinear(
|
||||
0,
|
||||
BitVec::repeat(false, 8),
|
||||
CompressionCode::Uncompressed,
|
||||
)
|
||||
.into();
|
||||
let Header {
|
||||
command_code: command,
|
||||
a: offset,
|
||||
b: length,
|
||||
c: _sub,
|
||||
d: reserved,
|
||||
} = header;
|
||||
let p = Packet {
|
||||
header: Header {
|
||||
command_code: command,
|
||||
a: offset,
|
||||
b: length,
|
||||
c: 42,
|
||||
d: reserved,
|
||||
},
|
||||
payload,
|
||||
};
|
||||
assert_eq!(
|
||||
Command::try_from(p),
|
||||
Err(TryFromPacketError::InvalidCompressionCode(42))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_unexpected_size() {
|
||||
let Packet { header, payload } = Command::BitmapLinear(
|
||||
0,
|
||||
BitVec::repeat(false, 8),
|
||||
CompressionCode::Uncompressed,
|
||||
)
|
||||
.into();
|
||||
let Header {
|
||||
command_code: command,
|
||||
a: offset,
|
||||
b: length,
|
||||
c: compression,
|
||||
d: reserved,
|
||||
} = header;
|
||||
let p = Packet {
|
||||
header: Header {
|
||||
command_code: command,
|
||||
a: offset,
|
||||
b: 420,
|
||||
c: compression,
|
||||
d: reserved,
|
||||
},
|
||||
payload,
|
||||
};
|
||||
assert_eq!(
|
||||
Command::try_from(p),
|
||||
Err(TryFromPacketError::UnexpectedPayloadSize(
|
||||
420,
|
||||
length as usize,
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn origin_add() {
|
||||
assert_eq!(
|
||||
Origin::<Pixels>::new(4, 2),
|
||||
Origin::new(1, 0) + Origin::new(3, 2)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packet_into_char_brightness_invalid() {
|
||||
let grid = BrightnessGrid::new(2, 2);
|
||||
let command = Command::CharBrightness(Origin::ZERO, grid);
|
||||
let mut packet: Packet = command.into();
|
||||
let slot = packet.payload.get_mut(1).unwrap();
|
||||
*slot = 23;
|
||||
assert_eq!(
|
||||
Command::try_from(packet),
|
||||
Err(TryFromPacketError::InvalidBrightness(23))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packet_into_brightness_invalid() {
|
||||
let mut packet: Packet = Command::Brightness(Brightness::MAX).into();
|
||||
let slot = packet.payload.get_mut(0).unwrap();
|
||||
*slot = 42;
|
||||
assert_eq!(
|
||||
Command::try_from(packet),
|
||||
Err(TryFromPacketError::InvalidBrightness(42))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
/// The u16 command codes used for the [Command]s.
|
||||
#[repr(u16)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) enum CommandCode {
|
||||
#[allow(missing_docs)]
|
||||
pub enum CommandCode {
|
||||
Clear = 0x0002,
|
||||
Cp437Data = 0x0003,
|
||||
CharBrightness = 0x0005,
|
||||
|
|
@ -33,8 +34,12 @@ impl From<CommandCode> for u16 {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, Eq, PartialEq)]
|
||||
#[error("The command code {0} is not known.")]
|
||||
pub struct InvalidCommandCodeError(pub u16);
|
||||
|
||||
impl TryFrom<u16> for CommandCode {
|
||||
type Error = ();
|
||||
type Error = InvalidCommandCodeError;
|
||||
|
||||
/// Returns the enum value for the specified `u16` or `Error` if the code is unknown.
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
|
|
@ -97,7 +102,7 @@ impl TryFrom<u16> for CommandCode {
|
|||
value if value == CommandCode::Utf8Data as u16 => {
|
||||
Ok(CommandCode::Utf8Data)
|
||||
}
|
||||
_ => Err(()),
|
||||
_ => Err(InvalidCommandCodeError(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue