mirror of
https://github.com/cccb/servicepoint.git
synced 2025-01-18 18:10:14 +01:00
Merge branch 'easier-text'
This commit is contained in:
commit
3a605da0d5
246
Cargo.lock
generated
246
Cargo.lock
generated
|
@ -84,12 +84,27 @@ 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.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
version = "0.4.4"
|
||||
|
@ -132,9 +147,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.18"
|
||||
version = "1.1.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476"
|
||||
checksum = "58e804ac3194a48bb129643eb1d62fcc20d18c6b8c181704489353d13120bcd1"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
|
@ -149,9 +164,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.17"
|
||||
version = "4.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
||||
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -159,9 +174,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.17"
|
||||
version = "4.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
||||
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -171,9 +186,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.13"
|
||||
version = "4.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
||||
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
|
@ -193,6 +208,15 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
|
@ -202,6 +226,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 = "csbindgen"
|
||||
version = "1.9.3"
|
||||
|
@ -212,6 +246,22 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||
|
||||
[[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 = "equivalent"
|
||||
version = "1.0.1"
|
||||
|
@ -236,20 +286,36 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
|||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.33"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253"
|
||||
checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
|
||||
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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
|
@ -263,9 +329,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
|
@ -280,10 +346,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.5.0"
|
||||
name = "http"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
|
||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
|
@ -320,9 +403,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.158"
|
||||
version = "0.2.159"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||
checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
|
@ -353,15 +436,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
|
@ -374,9 +457,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
version = "1.0.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -428,9 +511,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.6"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -440,9 +523,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.7"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -451,9 +534,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rust-lzma"
|
||||
|
@ -467,9 +550,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.36"
|
||||
version = "0.38.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36"
|
||||
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
|
@ -518,30 +601,32 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.7"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servicepoint"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bzip2",
|
||||
"clap",
|
||||
"flate2",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rand",
|
||||
"rust-lzma",
|
||||
"tungstenite",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servicepoint_binding_c"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"cbindgen",
|
||||
"servicepoint",
|
||||
|
@ -549,13 +634,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "servicepoint_binding_cs"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
dependencies = [
|
||||
"csbindgen",
|
||||
"servicepoint",
|
||||
"servicepoint_binding_c",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -570,9 +666,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.77"
|
||||
version = "2.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -587,9 +683,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.12.0"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64"
|
||||
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
|
@ -598,6 +694,26 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
|
@ -621,9 +737,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.20"
|
||||
version = "0.22.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
|
@ -633,10 +749,40 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
name = "tungstenite"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a"
|
||||
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.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
|
@ -650,6 +796,12 @@ 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"
|
||||
|
@ -740,9 +892,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.18"
|
||||
version = "0.6.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
|
@ -6,7 +6,7 @@ members = [
|
|||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
|
||||
[workspace.lints.rust]
|
||||
missing-docs = "warn"
|
||||
|
|
|
@ -20,24 +20,36 @@ bzip2 = { version = "0.4", optional = true }
|
|||
zstd = { version = "0.13", optional = true }
|
||||
rust-lzma = { version = "0.6.0", optional = true }
|
||||
rand = { version = "0.8", optional = true }
|
||||
tungstenite = { version = "0.24.0", optional = true }
|
||||
once_cell = { version = "1.20.2", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["compression_lzma"]
|
||||
default = ["compression_lzma", "protocol_udp", "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]]
|
||||
name = "random_brightness"
|
||||
required-features = ["rand"]
|
||||
|
||||
[[example]]
|
||||
name = "game_of_life"
|
||||
required-features = ["rand"]
|
||||
|
||||
[[example]]
|
||||
name = "websocket"
|
||||
required-features = ["protocol_websocket"]
|
||||
|
||||
[dev-dependencies]
|
||||
# for examples
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
rand = "0.8"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
|
@ -9,6 +9,17 @@ In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wa
|
|||
Display" or "Airport Display".
|
||||
This crate contains a library for parsing, encoding and sending packets to this display via UDP.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cargo add servicepoint
|
||||
```
|
||||
or
|
||||
```toml
|
||||
[dependencies]
|
||||
servicepoint = "0.9.0"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```rust
|
||||
|
@ -23,7 +34,7 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
More examples are available in the crate.
|
||||
More examples are available in the crate.
|
||||
Execute `cargo run --example` for a list of available examples and `cargo run --example <name>` to run one.
|
||||
|
||||
## Note on stability
|
||||
|
@ -32,22 +43,21 @@ This library is still in early development.
|
|||
You can absolutely use it, and it works, but expect minor breaking changes with every version bump.
|
||||
Please specify the full version including patch in your Cargo.toml until 1.0 is released.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cargo add servicepoint
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
This library has multiple compression libraries as optional dependencies.
|
||||
If you do not need compression/decompression support you can disable those features.
|
||||
In the likely case you only need one of them, you can include that one specifically.
|
||||
This library has multiple optional dependencies.
|
||||
You can choose to (not) include them by toggling the related features.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
servicepoint = { version = "0.8.0", default-features = false, features = ["compression-bz"] }
|
||||
```
|
||||
| Name | Default | Description |
|
||||
|--------------------|---------|--------------------------------------------|
|
||||
| compression_zlib | false | Enable additional compression algo |
|
||||
| compression_bzip2 | false | Enable additional compression algo |
|
||||
| compression_lzma | true | Enable additional compression algo |
|
||||
| compression_zstd | false | Enable additional compression algo |
|
||||
| protocol_udp | true | Connection::Udp |
|
||||
| protocol_websocket | false | Connection::WebSocket |
|
||||
| rand | false | impl Distribution<Brightness> for Standard |
|
||||
| cp437 | true | Conversion to and from CP-437 |
|
||||
|
||||
## Everything else
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use clap::Parser;
|
||||
|
||||
use servicepoint::{Command, Connection, Cp437Grid, Grid, Origin};
|
||||
use servicepoint::{CharGrid, Command, Connection, Cp437Grid, Origin};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
|
@ -31,19 +31,15 @@ fn main() {
|
|||
.expect("sending clear failed");
|
||||
}
|
||||
|
||||
let max_width = cli.text.iter().map(|t| t.len()).max().unwrap();
|
||||
let text = cli.text.iter().fold(String::new(), move |str, line| {
|
||||
let is_first = str.is_empty();
|
||||
str + if is_first { "" } else { "\n" } + line
|
||||
});
|
||||
|
||||
let mut chars = Cp437Grid::new(max_width, cli.text.len());
|
||||
for y in 0..cli.text.len() {
|
||||
let row = &cli.text[y];
|
||||
|
||||
for (x, char) in row.chars().enumerate() {
|
||||
let char = char.try_into().expect("invalid input char");
|
||||
chars.set(x, y, char);
|
||||
}
|
||||
}
|
||||
let grid = CharGrid::from(&*text);
|
||||
let cp437_grid = Cp437Grid::from(&grid);
|
||||
|
||||
connection
|
||||
.send(Command::Cp437Data(Origin::new(0, 0), chars))
|
||||
.send(Command::Cp437Data(Origin::new(0, 0), cp437_grid))
|
||||
.expect("sending text failed");
|
||||
}
|
||||
|
|
27
crates/servicepoint/examples/websocket.rs
Normal file
27
crates/servicepoint/examples/websocket.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
//! Example for how to use the WebSocket connection
|
||||
|
||||
use servicepoint::{
|
||||
Command, CompressionCode, Connection, Grid, Origin, PixelGrid,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// make connection mut
|
||||
let mut connection =
|
||||
Connection::open_websocket("ws://localhost:8080".parse().unwrap())
|
||||
.unwrap();
|
||||
|
||||
// use send_mut instead of send
|
||||
connection.send_mut(Command::Clear).unwrap();
|
||||
|
||||
let mut pixels = PixelGrid::max_sized();
|
||||
pixels.fill(true);
|
||||
|
||||
// use send_mut instead of send
|
||||
connection
|
||||
.send_mut(Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
pixels,
|
||||
CompressionCode::Lzma,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
|
@ -77,8 +77,8 @@ impl From<BrightnessGrid> for Vec<u8> {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<BrightnessGrid> for PrimitiveGrid<u8> {
|
||||
fn from(value: PrimitiveGrid<Brightness>) -> Self {
|
||||
impl From<&BrightnessGrid> for PrimitiveGrid<u8> {
|
||||
fn from(value: &PrimitiveGrid<Brightness>) -> Self {
|
||||
let u8s = value
|
||||
.iter()
|
||||
.map(|brightness| (*brightness).into())
|
||||
|
@ -109,3 +109,33 @@ impl Distribution<Brightness> for Standard {
|
|||
Brightness(rng.gen_range(Brightness::MIN.0..=Brightness::MAX.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::DataRef;
|
||||
|
||||
#[test]
|
||||
fn brightness_from_u8() {
|
||||
assert_eq!(Err(100), Brightness::try_from(100));
|
||||
assert_eq!(Ok(Brightness(1)), Brightness::try_from(1))
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "rand")]
|
||||
fn rand_brightness() {
|
||||
let mut rng = rand::thread_rng();
|
||||
for _ in 0..100 {
|
||||
let _: Brightness = rng.gen();
|
||||
}
|
||||
}
|
||||
|
||||
#[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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,50 @@
|
|||
use bitvec::prelude::BitVec;
|
||||
|
||||
use crate::{
|
||||
command_code::CommandCode, compression::into_decompressed, Brightness,
|
||||
BrightnessGrid, CompressionCode, Header, Origin, Packet, PixelGrid, Pixels,
|
||||
PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE,
|
||||
command_code::CommandCode,
|
||||
compression::into_decompressed,
|
||||
packet::{Header, Packet},
|
||||
Brightness, BrightnessGrid, CompressionCode, Cp437Grid, Origin, PixelGrid,
|
||||
Pixels, PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE,
|
||||
};
|
||||
|
||||
/// Type alias for documenting the meaning of the u16 in enum values
|
||||
pub type Offset = usize;
|
||||
|
||||
/// A grid containing codepage 437 characters.
|
||||
///
|
||||
/// The encoding is currently not enforced.
|
||||
pub type Cp437Grid = PrimitiveGrid<u8>;
|
||||
|
||||
/// A low-level display command.
|
||||
///
|
||||
/// This struct and associated functions implement the UDP protocol for the display.
|
||||
///
|
||||
/// To send a `Command`, use a `Connection`.
|
||||
/// 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::Uncompressed].
|
||||
///
|
||||
/// 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};
|
||||
///
|
||||
/// # use servicepoint::{Brightness, Command, Connection, packet::Packet};
|
||||
/// #
|
||||
/// // create command
|
||||
/// let command = Command::Brightness(Brightness::MAX);
|
||||
///
|
||||
|
@ -56,6 +76,8 @@ pub enum Command {
|
|||
|
||||
/// Show text on the screen.
|
||||
///
|
||||
/// The text is sent in the form of a 2D grid of characters.
|
||||
///
|
||||
/// <div class="warning">
|
||||
/// The library does not currently convert between UTF-8 and CP-437.
|
||||
/// Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
|
||||
|
@ -64,15 +86,45 @@ pub enum Command {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, Connection, Origin};
|
||||
/// # let connection = Connection::Fake;
|
||||
/// use servicepoint::{CharGrid, Cp437Grid};
|
||||
/// 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::open("127.0.0.1:2342").unwrap();
|
||||
/// let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'].map(move |c| c as u8);
|
||||
/// let grid = Cp437Grid::load(5, 2, &chars);
|
||||
/// # let connection = Connection::Fake;
|
||||
/// let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap();
|
||||
/// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap();
|
||||
/// ```
|
||||
Cp437Data(Origin<Tiles>, Cp437Grid),
|
||||
|
||||
/// Sets a window of pixels to the specified values
|
||||
/// Overwrites a rectangular region of pixels.
|
||||
///
|
||||
/// Origin coordinates must be divisible by 8.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, CompressionCode, Grid, PixelGrid};
|
||||
/// # let connection = servicepoint::Connection::Fake;
|
||||
/// #
|
||||
/// let mut pixels = PixelGrid::max_sized();
|
||||
/// // draw something to the pixels here
|
||||
/// # pixels.set(2, 5, true);
|
||||
///
|
||||
/// // create command to send pixels
|
||||
/// let command = Command::BitmapLinearWin(
|
||||
/// servicepoint::Origin::new(0, 0),
|
||||
/// pixels,
|
||||
/// CompressionCode::Uncompressed
|
||||
/// );
|
||||
///
|
||||
/// connection.send(command).expect("send failed");
|
||||
/// ```
|
||||
BitmapLinearWin(Origin<Pixels>, PixelGrid, CompressionCode),
|
||||
|
||||
/// Set the brightness of all tiles to the same value.
|
||||
|
@ -95,7 +147,7 @@ pub enum Command {
|
|||
/// 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.
|
||||
/// The contained [BitVec] is always uncompressed.
|
||||
BitmapLinear(Offset, SpBitVec, CompressionCode),
|
||||
|
||||
/// Set pixel data according to an and-mask starting at the offset.
|
||||
|
@ -103,7 +155,7 @@ pub enum Command {
|
|||
/// 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.
|
||||
/// The contained [BitVec] is always uncompressed.
|
||||
BitmapLinearAnd(Offset, SpBitVec, CompressionCode),
|
||||
|
||||
/// Set pixel data according to an or-mask starting at the offset.
|
||||
|
@ -111,7 +163,7 @@ pub enum Command {
|
|||
/// 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.
|
||||
/// The contained [BitVec] is always uncompressed.
|
||||
BitmapLinearOr(Offset, SpBitVec, CompressionCode),
|
||||
|
||||
/// Set pixel data according to a xor-mask starting at the offset.
|
||||
|
@ -119,7 +171,7 @@ pub enum Command {
|
|||
/// 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.
|
||||
/// The contained [BitVec] is always uncompressed.
|
||||
BitmapLinearXor(Offset, SpBitVec, CompressionCode),
|
||||
|
||||
/// Kills the udp daemon on the display, which usually results in a restart.
|
||||
|
@ -166,7 +218,7 @@ pub enum Command {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Err values for `Command::try_from`.
|
||||
/// Err values for [Command::try_from].
|
||||
#[derive(PartialEq)]
|
||||
pub enum TryFromPacketError {
|
||||
/// the contained command code does not correspond to a known command
|
||||
|
@ -188,7 +240,7 @@ pub enum TryFromPacketError {
|
|||
impl TryFrom<Packet> for Command {
|
||||
type Error = TryFromPacketError;
|
||||
|
||||
/// Try to interpret the `Packet` as one containing a `Command`
|
||||
/// Try to interpret the [Packet] as one containing a [Command]
|
||||
fn try_from(packet: Packet) -> Result<Self, Self::Error> {
|
||||
let Packet {
|
||||
header: Header {
|
||||
|
@ -443,9 +495,12 @@ impl Command {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
bitvec::prelude::BitVec, command::TryFromPacketError,
|
||||
command_code::CommandCode, origin::Pixels, Brightness, Command,
|
||||
CompressionCode, Header, Origin, Packet, PixelGrid, PrimitiveGrid,
|
||||
bitvec::prelude::BitVec,
|
||||
command::TryFromPacketError,
|
||||
command_code::CommandCode,
|
||||
origin::Pixels,
|
||||
packet::{Header, Packet},
|
||||
Brightness, Command, CompressionCode, Origin, PixelGrid, PrimitiveGrid,
|
||||
};
|
||||
|
||||
fn round_trip(original: Command) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/// The u16 command codes used for the `Commands`.
|
||||
/// The u16 command codes used for the [Command]s.
|
||||
#[repr(u16)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum CommandCode {
|
||||
|
|
|
@ -8,7 +8,7 @@ use flate2::{FlushCompress, FlushDecompress, Status};
|
|||
#[cfg(feature = "compression_zstd")]
|
||||
use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder};
|
||||
|
||||
use crate::{CompressionCode, Payload};
|
||||
use crate::{packet::Payload, CompressionCode};
|
||||
|
||||
pub(crate) fn into_decompressed(
|
||||
kind: CompressionCode,
|
||||
|
|
|
@ -1,29 +1,61 @@
|
|||
use std::fmt::Debug;
|
||||
use std::net::{ToSocketAddrs, UdpSocket};
|
||||
|
||||
use log::{debug, info};
|
||||
|
||||
use crate::Packet;
|
||||
use crate::packet::Packet;
|
||||
|
||||
/// A connection to the display.
|
||||
///
|
||||
/// Used to send [Packets][Packet] or [Commands][crate::Command].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// let connection = servicepoint::Connection::open("172.23.42.29:2342")
|
||||
/// let connection = servicepoint::Connection::open("127.0.0.1:2342")
|
||||
/// .expect("connection failed");
|
||||
/// connection.send(servicepoint::Command::Clear)
|
||||
/// .expect("send failed");
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub enum Connection {
|
||||
/// A real connection using the UDP protocol
|
||||
Udp(UdpSocket),
|
||||
/// A fake connection for testing that does not actually send anything
|
||||
/// A connection using the UDP protocol.
|
||||
///
|
||||
/// Use this when sending commands directly to the display.
|
||||
///
|
||||
/// Requires the feature "protocol_udp" which is enabled by default.
|
||||
#[cfg(feature = "protocol_udp")]
|
||||
Udp(std::net::UdpSocket),
|
||||
|
||||
/// A connection using the WebSocket protocol.
|
||||
///
|
||||
/// Note that you will need to forward the WebSocket messages via UDP to the display.
|
||||
/// You can use [servicepoint-websocket-relay] for this.
|
||||
///
|
||||
/// To create a new WebSocket automatically, use [Connection::open_websocket].
|
||||
///
|
||||
/// Requires the feature "protocol_websocket" which is disabled by default.
|
||||
///
|
||||
/// [servicepoint-websocket-relay]: https://github.com/kaesaecracker/servicepoint-websocket-relay
|
||||
#[cfg(feature = "protocol_websocket")]
|
||||
WebSocket(
|
||||
tungstenite::WebSocket<
|
||||
tungstenite::stream::MaybeTlsStream<std::net::TcpStream>,
|
||||
>,
|
||||
),
|
||||
|
||||
/// A fake connection for testing that does not actually send anything.
|
||||
///
|
||||
/// This variant allows immutable send.
|
||||
Fake,
|
||||
|
||||
/// A fake connection for testing that does not actually send anything.
|
||||
///
|
||||
/// This variant does not allow immutable send.
|
||||
FakeMutableSend,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SendError {
|
||||
IoError(std::io::Error),
|
||||
#[cfg(feature = "protocol_websocket")]
|
||||
WebsocketError(tungstenite::Error),
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
|
@ -31,24 +63,110 @@ impl Connection {
|
|||
///
|
||||
/// Note that this is UDP, which means that the open call can succeed even if the display is unreachable.
|
||||
///
|
||||
/// The address of the display in CCCB is `172.23.42.29:2342`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Any errors resulting from binding the udp socket.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// let connection = servicepoint::Connection::open("172.23.42.29:2342")
|
||||
/// let connection = servicepoint::Connection::open("127.0.0.1:2342")
|
||||
/// .expect("connection failed");
|
||||
/// ```
|
||||
pub fn open(addr: impl ToSocketAddrs + Debug) -> std::io::Result<Self> {
|
||||
info!("connecting to {addr:?}");
|
||||
let socket = UdpSocket::bind("0.0.0.0:0")?;
|
||||
#[cfg(feature = "protocol_udp")]
|
||||
pub fn open(
|
||||
addr: impl std::net::ToSocketAddrs + Debug,
|
||||
) -> std::io::Result<Self> {
|
||||
log::info!("connecting to {addr:?}");
|
||||
let socket = std::net::UdpSocket::bind("0.0.0.0:0")?;
|
||||
socket.connect(addr)?;
|
||||
Ok(Self::Udp(socket))
|
||||
}
|
||||
|
||||
/// Open a new WebSocket and connect to the provided host.
|
||||
///
|
||||
/// Requires the feature "protocol_websocket" which is disabled by default.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tungstenite::http::Uri;
|
||||
/// use servicepoint::{Command, Connection};
|
||||
/// let uri = "ws://localhost:8080".parse().unwrap();
|
||||
/// let mut connection = Connection::open_websocket(uri)
|
||||
/// .expect("could not connect");
|
||||
/// connection.send_mut(Command::Clear)
|
||||
/// .expect("send failed");
|
||||
/// ```
|
||||
#[cfg(feature = "protocol_websocket")]
|
||||
pub fn open_websocket(
|
||||
uri: tungstenite::http::Uri,
|
||||
) -> tungstenite::Result<Self> {
|
||||
use tungstenite::{
|
||||
client::IntoClientRequest, connect, ClientRequestBuilder,
|
||||
};
|
||||
|
||||
log::info!("connecting to {uri:?}");
|
||||
|
||||
let request = ClientRequestBuilder::new(uri).into_client_request()?;
|
||||
let (sock, _) = connect(request)?;
|
||||
Ok(Self::WebSocket(sock))
|
||||
}
|
||||
|
||||
/// Send something packet-like to the display. Usually this is in the form of a Command.
|
||||
///
|
||||
/// This variant can only be used for connections that support immutable send, e.g. [Connection::Udp].
|
||||
///
|
||||
/// If you want to be able to switch the protocol, you should use [Self::send_mut] instead.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `packet`: the packet-like to send
|
||||
///
|
||||
/// returns: true if packet was sent, otherwise false
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the connection does not support immutable send, e.g. for [Connection::WebSocket].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let connection = servicepoint::Connection::Fake;
|
||||
/// // turn off all pixels on display
|
||||
/// connection.send(servicepoint::Command::Clear)
|
||||
/// .expect("send failed");
|
||||
/// ```
|
||||
pub fn send(&self, packet: impl Into<Packet>) -> Result<(), SendError> {
|
||||
let packet = packet.into();
|
||||
log::debug!("sending {packet:?}");
|
||||
let data: Vec<u8> = packet.into();
|
||||
match self {
|
||||
#[cfg(feature = "protocol_udp")]
|
||||
Connection::Udp(socket) => {
|
||||
socket
|
||||
.send(&data)
|
||||
.map_err(SendError::IoError)
|
||||
.map(move |_| ()) // ignore Ok value
|
||||
}
|
||||
Connection::Fake => {
|
||||
let _ = data;
|
||||
Ok(())
|
||||
}
|
||||
#[allow(unreachable_patterns)] // depends on features
|
||||
_ => {
|
||||
panic!("Connection {:?} does not support immutable send", self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send something packet-like to the display. Usually this is in the form of a Command.
|
||||
///
|
||||
/// This variant has to be used for connections that do not support immutable send, e.g. [Connection::WebSocket].
|
||||
///
|
||||
/// If you want to be able to switch the protocol, you should use this variant.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `packet`: the packet-like to send
|
||||
|
@ -58,23 +176,70 @@ impl Connection {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # let connection = servicepoint::Connection::Fake;
|
||||
/// let mut connection = servicepoint::Connection::FakeMutableSend;
|
||||
/// // turn off all pixels on display
|
||||
/// connection.send(servicepoint::Command::Clear)
|
||||
/// connection.send_mut(servicepoint::Command::Clear)
|
||||
/// .expect("send failed");
|
||||
/// ```
|
||||
pub fn send(&self, packet: impl Into<Packet>) -> Result<(), SendError> {
|
||||
let packet = packet.into();
|
||||
debug!("sending {packet:?}");
|
||||
let data: Vec<u8> = packet.into();
|
||||
pub fn send_mut(
|
||||
&mut self,
|
||||
packet: impl Into<Packet>,
|
||||
) -> Result<(), SendError> {
|
||||
match self {
|
||||
Connection::Udp(socket) => {
|
||||
#[cfg(feature = "protocol_websocket")]
|
||||
Connection::WebSocket(socket) => {
|
||||
let packet = packet.into();
|
||||
log::debug!("sending {packet:?}");
|
||||
let data: Vec<u8> = packet.into();
|
||||
socket
|
||||
.send(&data)
|
||||
.map_err(SendError::IoError)
|
||||
.map(move |_| ()) // ignore Ok value
|
||||
.send(tungstenite::Message::Binary(data))
|
||||
.map_err(SendError::WebsocketError)
|
||||
}
|
||||
Connection::Fake => Ok(()),
|
||||
Connection::FakeMutableSend => {
|
||||
let packet = packet.into();
|
||||
log::debug!("sending {packet:?}");
|
||||
let data: Vec<u8> = packet.into();
|
||||
let _ = data;
|
||||
Ok(())
|
||||
}
|
||||
_ => self.send(packet),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Connection {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(feature = "protocol_websocket")]
|
||||
if let Connection::WebSocket(sock) = self {
|
||||
_ = sock.close(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::packet::*;
|
||||
|
||||
#[test]
|
||||
fn send_fake() {
|
||||
let data: &[u8] = &[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let packet = Packet::try_from(data).unwrap();
|
||||
Connection::Fake.send(packet).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_fake_mutable() {
|
||||
let data: &[u8] = &[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let packet = Packet::try_from(data).unwrap();
|
||||
Connection::FakeMutableSend.send_mut(packet).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn send_fake_mutable_panic() {
|
||||
let data: &[u8] = &[0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let packet = Packet::try_from(data).unwrap();
|
||||
Connection::FakeMutableSend.send(packet).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
221
crates/servicepoint/src/cp437.rs
Normal file
221
crates/servicepoint/src/cp437.rs
Normal file
|
@ -0,0 +1,221 @@
|
|||
use crate::cp437::Cp437LoadError::InvalidChar;
|
||||
use crate::{Grid, PrimitiveGrid};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A grid containing codepage 437 characters.
|
||||
///
|
||||
/// The encoding is currently not enforced.
|
||||
pub type Cp437Grid = PrimitiveGrid<u8>;
|
||||
|
||||
/// A grid containing UTF-8 characters.
|
||||
pub type CharGrid = PrimitiveGrid<char>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Cp437LoadError {
|
||||
InvalidChar { index: usize, 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, Cp437LoadError> {
|
||||
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(InvalidChar { 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)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)] // depends on features
|
||||
pub use feature_cp437::*;
|
||||
|
||||
#[cfg(feature = "cp437")]
|
||||
mod feature_cp437 {
|
||||
use super::*;
|
||||
|
||||
/// An array of 256 elements, mapping most of the CP437 values to UTF-8 characters
|
||||
///
|
||||
/// Mostly follows CP437, except for:
|
||||
/// * 0x0A & 0x0D are kept for use as line endings.
|
||||
/// * 0x1A is used for SAUCE.
|
||||
/// * 0x1B is used for ANSI escape sequences.
|
||||
///
|
||||
/// These exclusions should be fine since most programs can't even use them
|
||||
/// without issues. And this makes rendering simpler too.
|
||||
///
|
||||
/// See <https://en.wikipedia.org/wiki/Code_page_437#Character_set>
|
||||
///
|
||||
/// Copied from https://github.com/kip93/cp437-tools. License: GPL-3.0
|
||||
#[rustfmt::skip]
|
||||
const CP437_TO_UTF8: [char; 256] = [
|
||||
/* 0X */ '\0', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '\n', '♂', '♀', '\r', '♫', '☼',
|
||||
/* 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 */ '≡', '±', '≥', '≤', '⌠', '⌡', '÷', '≈', '°', '∙', '·', '√', 'ⁿ', '²', '■', ' ',
|
||||
];
|
||||
|
||||
const 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)
|
||||
});
|
||||
|
||||
const MISSING_CHAR_CP437: u8 = 0x3F;
|
||||
|
||||
impl From<&Cp437Grid> for CharGrid {
|
||||
fn from(value: &Cp437Grid) -> Self {
|
||||
let mut grid = Self::new(value.width(), value.height());
|
||||
|
||||
for y in 0..grid.height() {
|
||||
for x in 0..grid.width() {
|
||||
let converted = CP437_TO_UTF8[value.get(x, y) as usize];
|
||||
grid.set(x, y, converted);
|
||||
}
|
||||
}
|
||||
|
||||
grid
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&CharGrid> for Cp437Grid {
|
||||
fn from(value: &CharGrid) -> Self {
|
||||
let mut grid = Self::new(value.width(), value.height());
|
||||
|
||||
for y in 0..grid.height() {
|
||||
for x in 0..grid.width() {
|
||||
let char = value.get(x, y);
|
||||
let converted = *UTF8_TO_CP437
|
||||
.get(&char)
|
||||
.unwrap_or(&MISSING_CHAR_CP437);
|
||||
grid.set(x, y, converted);
|
||||
}
|
||||
}
|
||||
|
||||
grid
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for CharGrid {
|
||||
fn from(value: &str) -> Self {
|
||||
let value = value.replace("\r\n", "\n");
|
||||
let lines = value.split('\n').collect::<Vec<_>>();
|
||||
let width =
|
||||
lines.iter().fold(0, move |a, x| std::cmp::max(a, x.len()));
|
||||
|
||||
let mut grid = Self::new(width, lines.len());
|
||||
for (y, line) in lines.iter().enumerate() {
|
||||
for (x, char) in line.chars().enumerate() {
|
||||
grid.set(x, y, char);
|
||||
}
|
||||
}
|
||||
|
||||
grid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "cp437")]
|
||||
mod tests_feature_cp437 {
|
||||
use crate::{CharGrid, Cp437Grid};
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
|
@ -79,12 +79,12 @@ pub trait Grid<T> {
|
|||
assert!(
|
||||
x < self.width(),
|
||||
"cannot access index [{x}, {y}] because x is outside of bounds 0..{}",
|
||||
self.width()
|
||||
self.width() - 1
|
||||
);
|
||||
assert!(
|
||||
y < self.height(),
|
||||
"cannot access byte [{x}, {y}] because y is outside of bounds 0..{}",
|
||||
self.height()
|
||||
"cannot access index [{x}, {y}] because y is outside of bounds 0..{}",
|
||||
self.height() - 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
//! Abstractions for the UDP protocol of the CCCB servicepoint display.
|
||||
//!
|
||||
//! Your starting point is a [Connection] to the display.
|
||||
//! With a connection, you can send [Command]s.
|
||||
//! When received, the display will update the state of the pixels.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
|
@ -37,13 +41,13 @@ pub use bitvec;
|
|||
use bitvec::prelude::{BitVec, Msb0};
|
||||
|
||||
pub use crate::brightness::{Brightness, BrightnessGrid};
|
||||
pub use crate::command::{Command, Cp437Grid, Offset};
|
||||
pub use crate::command::{Command, Offset};
|
||||
pub use crate::compression_code::CompressionCode;
|
||||
pub use crate::connection::Connection;
|
||||
pub use crate::cp437::{CharGrid, Cp437Grid};
|
||||
pub use crate::data_ref::DataRef;
|
||||
pub use crate::grid::Grid;
|
||||
pub use crate::origin::{Origin, Pixels, Tiles};
|
||||
pub use crate::packet::{Header, Packet, Payload};
|
||||
pub use crate::pixel_grid::PixelGrid;
|
||||
pub use crate::primitive_grid::PrimitiveGrid;
|
||||
|
||||
|
@ -55,10 +59,11 @@ mod command_code;
|
|||
mod compression;
|
||||
mod compression_code;
|
||||
mod connection;
|
||||
mod cp437;
|
||||
mod data_ref;
|
||||
mod grid;
|
||||
mod origin;
|
||||
mod packet;
|
||||
pub mod packet;
|
||||
mod pixel_grid;
|
||||
mod primitive_grid;
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::TILE_SIZE;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// An origin marks the top left position of a window sent to the display.
|
||||
|
@ -11,7 +12,14 @@ pub struct Origin<Unit: DisplayUnit> {
|
|||
}
|
||||
|
||||
impl<Unit: DisplayUnit> Origin<Unit> {
|
||||
/// Create a new `Origin` instance for the provided position.
|
||||
/// Top-left. Equivalent to `Origin::new(0, 0)`.
|
||||
pub const ZERO: Self = Self {
|
||||
x: 0,
|
||||
y: 0,
|
||||
phantom_data: PhantomData,
|
||||
};
|
||||
|
||||
/// Create a new [Origin] instance for the provided position.
|
||||
pub fn new(x: usize, y: usize) -> Self {
|
||||
Self {
|
||||
x,
|
||||
|
@ -46,3 +54,69 @@ pub struct Tiles();
|
|||
impl DisplayUnit for Pixels {}
|
||||
|
||||
impl DisplayUnit for Tiles {}
|
||||
|
||||
impl From<&Origin<Tiles>> for Origin<Pixels> {
|
||||
fn from(value: &Origin<Tiles>) -> Self {
|
||||
Self {
|
||||
x: value.x * TILE_SIZE,
|
||||
y: value.y * TILE_SIZE,
|
||||
phantom_data: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Origin<Pixels>> for Origin<Tiles> {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &Origin<Pixels>) -> Result<Self, Self::Error> {
|
||||
let (x, x_rem) = (value.x / TILE_SIZE, value.x % TILE_SIZE);
|
||||
if x_rem != 0 {
|
||||
return Err(());
|
||||
}
|
||||
let (y, y_rem) = (value.y / TILE_SIZE, value.y % TILE_SIZE);
|
||||
if y_rem != 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
x,
|
||||
y,
|
||||
phantom_data: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn origin_tile_to_pixel() {
|
||||
let tile: Origin<Tiles> = Origin::new(1, 2);
|
||||
let actual: Origin<Pixels> = Origin::from(&tile);
|
||||
let expected: Origin<Pixels> = Origin::new(8, 16);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn origin_pixel_to_tile() {
|
||||
let pixel: Origin<Pixels> = Origin::new(8, 16);
|
||||
let actual: Origin<Tiles> = Origin::try_from(&pixel).unwrap();
|
||||
let expected: Origin<Tiles> = Origin::new(1, 2);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn origin_pixel_to_tile_fail_y() {
|
||||
let pixel: Origin<Pixels> = Origin::new(8, 15);
|
||||
let _: Origin<Tiles> = Origin::try_from(&pixel).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn origin_pixel_to_tile_fail_x() {
|
||||
let pixel: Origin<Pixels> = Origin::new(7, 16);
|
||||
let _: Origin<Tiles> = Origin::try_from(&pixel).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
//! Raw packet manipulation.
|
||||
//!
|
||||
//! Should probably only be used directly to use features not exposed by the library.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Converting a packet to a command and back:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use servicepoint::{Command, packet::Packet};
|
||||
//! # let command = Command::Clear;
|
||||
//! let packet: Packet = command.into();
|
||||
//! let command: Command = Command::try_from(packet).expect("could not read command from packet");
|
||||
//! ```
|
||||
//!
|
||||
//! Converting a packet to bytes and back:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use servicepoint::{Command, packet::Packet};
|
||||
//! # let command = Command::Clear;
|
||||
//! # let packet: Packet = command.into();
|
||||
//! let bytes: Vec<u8> = packet.into();
|
||||
//! 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::{
|
||||
Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels, Tiles,
|
||||
TILE_SIZE,
|
||||
command_code::CommandCode, Command, CompressionCode, Grid, Offset, Origin,
|
||||
PixelGrid, Pixels, Tiles, TILE_SIZE,
|
||||
};
|
||||
|
||||
/// A raw header.
|
||||
|
@ -13,8 +37,6 @@ use crate::{
|
|||
/// payload, where applicable.
|
||||
///
|
||||
/// Because the meaning of most fields depend on the command, there are no speaking names for them.
|
||||
///
|
||||
/// Should probably only be used directly to use features not exposed by the library.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Header {
|
||||
/// The first two bytes specify which command this packet represents.
|
||||
|
@ -38,26 +60,8 @@ pub type Payload = Vec<u8>;
|
|||
///
|
||||
/// Contents should probably only be used directly to use features not exposed by the library.
|
||||
///
|
||||
/// # Examples
|
||||
/// You may want to use [Command] instead.
|
||||
///
|
||||
/// Converting a packet to a command and back:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, Packet};
|
||||
/// # let command = Command::Clear;
|
||||
/// let packet: Packet = command.into();
|
||||
/// let command: Command = Command::try_from(packet).expect("could not read packet");
|
||||
/// ```
|
||||
///
|
||||
/// Converting a packet to bytes and back:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use servicepoint::{Command, Packet};
|
||||
/// # let command = Command::Clear;
|
||||
/// # let packet: Packet = command.into();
|
||||
/// let bytes: Vec<u8> = packet.into();
|
||||
/// let packet = Packet::try_from(bytes).expect("could not read packet from bytes");
|
||||
/// ```
|
||||
///
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Packet {
|
||||
|
@ -98,9 +102,9 @@ impl From<Packet> for Vec<u8> {
|
|||
impl TryFrom<&[u8]> for Packet {
|
||||
type Error = ();
|
||||
|
||||
/// Tries to interpret the bytes as a `Packet`.
|
||||
/// Tries to interpret the bytes as a [Packet].
|
||||
///
|
||||
/// returns: `Error` if slice is not long enough to be a `Packet`
|
||||
/// returns: `Error` if slice is not long enough to be a [Packet]
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
if value.len() < size_of::<Header>() {
|
||||
return Err(());
|
||||
|
@ -135,7 +139,7 @@ impl TryFrom<Vec<u8>> for Packet {
|
|||
}
|
||||
|
||||
impl From<Command> for Packet {
|
||||
/// Move the `Command` into a `Packet` instance for sending.
|
||||
/// Move the [Command] into a [Packet] instance for sending.
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn from(value: Command) -> Self {
|
||||
match value {
|
||||
|
@ -210,7 +214,7 @@ impl From<Command> for Packet {
|
|||
}
|
||||
|
||||
impl Packet {
|
||||
/// Helper method for `BitMapLinear*`-Commands into `Packet`
|
||||
/// Helper method for `BitMapLinear*`-Commands into [Packet]
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn bitmap_linear_into_packet(
|
||||
command: CommandCode,
|
||||
|
@ -312,7 +316,7 @@ impl Packet {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Header, Packet};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn round_trip() {
|
||||
|
@ -327,7 +331,7 @@ mod tests {
|
|||
payload: vec![42u8; 23],
|
||||
};
|
||||
let data: Vec<u8> = p.into();
|
||||
let p = Packet::try_from(&*data).unwrap();
|
||||
let p = Packet::try_from(data).unwrap();
|
||||
assert_eq!(
|
||||
p,
|
||||
Packet {
|
||||
|
|
|
@ -13,14 +13,14 @@ pub struct PixelGrid {
|
|||
}
|
||||
|
||||
impl PixelGrid {
|
||||
/// Creates a new `PixelGrid` with the specified dimensions.
|
||||
/// Creates a new [PixelGrid] with the specified dimensions.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `width`: size in pixels in x-direction
|
||||
/// - `height`: size in pixels in y-direction
|
||||
///
|
||||
/// returns: `PixelGrid` initialized to all pixels off
|
||||
/// returns: [PixelGrid] initialized to all pixels off
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
@ -40,14 +40,14 @@ impl PixelGrid {
|
|||
Self::new(PIXEL_WIDTH, PIXEL_HEIGHT)
|
||||
}
|
||||
|
||||
/// Loads a `PixelGrid` with the specified dimensions from the provided data.
|
||||
/// Loads a [PixelGrid] with the specified dimensions from the provided data.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `width`: size in pixels in x-direction
|
||||
/// - `height`: size in pixels in y-direction
|
||||
///
|
||||
/// returns: `PixelGrid` that contains a copy of the provided data
|
||||
/// returns: [PixelGrid] that contains a copy of the provided data
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
@ -64,7 +64,7 @@ impl PixelGrid {
|
|||
}
|
||||
}
|
||||
|
||||
/// Iterate over all cells in `PixelGrid`.
|
||||
/// Iterate over all cells in [PixelGrid].
|
||||
///
|
||||
/// Order is equivalent to the following loop:
|
||||
/// ```
|
||||
|
@ -80,7 +80,7 @@ impl PixelGrid {
|
|||
self.bit_vec.iter().by_refs()
|
||||
}
|
||||
|
||||
/// Iterate over all cells in `PixelGrid` mutably.
|
||||
/// Iterate over all cells in [PixelGrid] mutably.
|
||||
///
|
||||
/// Order is equivalent to the following loop:
|
||||
/// ```
|
||||
|
@ -107,7 +107,7 @@ impl PixelGrid {
|
|||
self.bit_vec.iter_mut()
|
||||
}
|
||||
|
||||
/// Iterate over all rows in `PixelGrid` top to bottom.
|
||||
/// Iterate over all rows in [PixelGrid] top to bottom.
|
||||
pub fn iter_rows(&self) -> IterRows {
|
||||
IterRows {
|
||||
pixel_grid: self,
|
||||
|
@ -117,7 +117,7 @@ impl PixelGrid {
|
|||
}
|
||||
|
||||
impl Grid<bool> for PixelGrid {
|
||||
/// Sets the value of the specified position in the `PixelGrid`.
|
||||
/// Sets the value of the specified position in the [PixelGrid].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
|
@ -139,7 +139,7 @@ impl Grid<bool> for PixelGrid {
|
|||
self.bit_vec[x + y * self.width]
|
||||
}
|
||||
|
||||
/// Sets the state of all pixels in the `PixelGrid`.
|
||||
/// Sets the state of all pixels in the [PixelGrid].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
|
@ -169,7 +169,7 @@ impl DataRef<u8> for PixelGrid {
|
|||
}
|
||||
|
||||
impl From<PixelGrid> for Vec<u8> {
|
||||
/// Turns a `PixelGrid` into the underlying `Vec<u8>`.
|
||||
/// Turns a [PixelGrid] into the underlying [`Vec<u8>`].
|
||||
fn from(value: PixelGrid) -> Self {
|
||||
value.bit_vec.into()
|
||||
}
|
||||
|
|
|
@ -14,14 +14,14 @@ pub struct PrimitiveGrid<T: PrimitiveGridType> {
|
|||
}
|
||||
|
||||
impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
||||
/// Creates a new `PrimitiveGrid` with the specified dimensions.
|
||||
/// Creates a new [PrimitiveGrid] with the specified dimensions.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - width: size in x-direction
|
||||
/// - height: size in y-direction
|
||||
///
|
||||
/// returns: `PrimitiveGrid` initialized to default value.
|
||||
/// returns: [PrimitiveGrid] initialized to default value.
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
data: vec![Default::default(); width * height],
|
||||
|
@ -30,9 +30,9 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Loads a `PrimitiveGrid` with the specified dimensions from the provided data.
|
||||
/// Loads a [PrimitiveGrid] with the specified dimensions from the provided data.
|
||||
///
|
||||
/// returns: `PrimitiveGrid` that contains a copy of the provided data
|
||||
/// returns: [PrimitiveGrid] that contains a copy of the provided data
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
@ -47,7 +47,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Iterate over all cells in `PrimitiveGrid`.
|
||||
/// Iterate over all cells in [PrimitiveGrid].
|
||||
///
|
||||
/// Order is equivalent to the following loop:
|
||||
/// ```
|
||||
|
@ -63,7 +63,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
|
|||
self.data.iter()
|
||||
}
|
||||
|
||||
/// Iterate over all rows in `PrimitiveGrid` top to bottom.
|
||||
/// Iterate over all rows in [PrimitiveGrid] top to bottom.
|
||||
pub fn iter_rows(&self) -> IterRows<T> {
|
||||
IterRows {
|
||||
byte_grid: self,
|
||||
|
@ -168,7 +168,7 @@ impl<T: PrimitiveGridType> DataRef<T> for PrimitiveGrid<T> {
|
|||
}
|
||||
|
||||
impl<T: PrimitiveGridType> From<PrimitiveGrid<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 {
|
||||
value.data
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@ crate-type = ["staticlib", "cdylib", "rlib"]
|
|||
cbindgen = "0.27.0"
|
||||
|
||||
[dependencies.servicepoint]
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
path = "../servicepoint"
|
||||
features = ["all_compressions"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -12,5 +12,6 @@ fn main() {
|
|||
let mut cc = cc::Build::new();
|
||||
cc.file("src/main.c");
|
||||
cc.include(&sp_include);
|
||||
cc.opt_level(2);
|
||||
cc.compile("lang_c");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Generated with cbindgen:0.26.0 */
|
||||
/* Generated with cbindgen:0.27.0 */
|
||||
|
||||
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
||||
|
||||
|
@ -1355,5 +1355,5 @@ struct SPByteSlice sp_pixel_grid_unsafe_data_ref(struct SPPixelGrid *pixel_grid)
|
|||
size_t sp_pixel_grid_width(const struct SPPixelGrid *pixel_grid);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
|
|
@ -40,7 +40,9 @@ pub unsafe extern "C" fn sp_cp437_grid_new(
|
|||
width: usize,
|
||||
height: usize,
|
||||
) -> *mut SPCp437Grid {
|
||||
Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::new(width, height))))
|
||||
Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::new(
|
||||
width, height,
|
||||
))))
|
||||
}
|
||||
|
||||
/// Loads a `SPCp437Grid` with the specified dimensions from the provided data.
|
||||
|
@ -67,7 +69,9 @@ pub unsafe extern "C" fn sp_cp437_grid_load(
|
|||
data_length: usize,
|
||||
) -> *mut SPCp437Grid {
|
||||
let data = std::slice::from_raw_parts(data, data_length);
|
||||
Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::load(width, height, data))))
|
||||
Box::into_raw(Box::new(SPCp437Grid(servicepoint::Cp437Grid::load(
|
||||
width, height, data,
|
||||
))))
|
||||
}
|
||||
|
||||
/// Clones a `SPCp437Grid`.
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::ptr::null_mut;
|
|||
use crate::SPCommand;
|
||||
|
||||
/// The raw packet
|
||||
pub struct SPPacket(pub(crate) servicepoint::Packet);
|
||||
pub struct SPPacket(pub(crate) servicepoint::packet::Packet);
|
||||
|
||||
/// Turns a `SPCommand` into a `SPPacket`.
|
||||
/// The `SPCommand` gets consumed.
|
||||
|
@ -49,7 +49,7 @@ pub unsafe extern "C" fn sp_packet_try_load(
|
|||
length: usize,
|
||||
) -> *mut SPPacket {
|
||||
let data = std::slice::from_raw_parts(data, length);
|
||||
match servicepoint::Packet::try_from(data) {
|
||||
match servicepoint::packet::Packet::try_from(data) {
|
||||
Err(_) => null_mut(),
|
||||
Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))),
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ test = false
|
|||
csbindgen = "1.9.3"
|
||||
|
||||
[dependencies]
|
||||
servicepoint_binding_c = { version = "0.8.0", path = "../servicepoint_binding_c" }
|
||||
servicepoint = { version = "0.8.0", path = "../servicepoint" }
|
||||
servicepoint_binding_c = { version = "0.9.0", path = "../servicepoint_binding_c" }
|
||||
servicepoint = { version = "0.9.0", path = "../servicepoint" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<PackageId>ServicePoint</PackageId>
|
||||
<Version>0.8.0</Version>
|
||||
<Version>0.9.0</Version>
|
||||
<Authors>Repository Authors</Authors>
|
||||
<Company>None</Company>
|
||||
<Product>ServicePoint</Product>
|
||||
|
|
Loading…
Reference in a new issue