Merge branch 'easier-text'

This commit is contained in:
Vinzenz Schroeter 2024-10-12 19:58:38 +02:00
commit 3a605da0d5
25 changed files with 951 additions and 192 deletions

246
Cargo.lock generated
View file

@ -84,12 +84,27 @@ dependencies = [
"wyz", "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]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.5.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
[[package]] [[package]]
name = "bzip2" name = "bzip2"
version = "0.4.4" version = "0.4.4"
@ -132,9 +147,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.1.18" version = "1.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" checksum = "58e804ac3194a48bb129643eb1d62fcc20d18c6b8c181704489353d13120bcd1"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
@ -149,9 +164,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.17" version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -159,9 +174,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.17" version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -171,9 +186,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.13" version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@ -193,6 +208,15 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "cpufeatures"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.4.2" version = "1.4.2"
@ -202,6 +226,16 @@ dependencies = [
"cfg-if", "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]] [[package]]
name = "csbindgen" name = "csbindgen"
version = "1.9.3" version = "1.9.3"
@ -212,6 +246,22 @@ dependencies = [
"syn", "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]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
@ -236,20 +286,36 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.33" version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "funty" name = "funty"
version = "2.0.0" version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" 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]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -263,9 +329,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.5" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
[[package]] [[package]]
name = "heck" name = "heck"
@ -280,10 +346,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "indexmap" name = "http"
version = "2.5.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -320,9 +403,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.158" version = "0.2.159"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
@ -353,15 +436,15 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.19.0" version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.30" version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
@ -374,9 +457,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.86" version = "1.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -428,9 +511,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.6" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -440,9 +523,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -451,9 +534,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "rust-lzma" name = "rust-lzma"
@ -467,9 +550,9 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.36" version = "0.38.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
@ -518,30 +601,32 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.7" version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "servicepoint" name = "servicepoint"
version = "0.8.0" version = "0.9.0"
dependencies = [ dependencies = [
"bitvec", "bitvec",
"bzip2", "bzip2",
"clap", "clap",
"flate2", "flate2",
"log", "log",
"once_cell",
"rand", "rand",
"rust-lzma", "rust-lzma",
"tungstenite",
"zstd", "zstd",
] ]
[[package]] [[package]]
name = "servicepoint_binding_c" name = "servicepoint_binding_c"
version = "0.8.0" version = "0.9.0"
dependencies = [ dependencies = [
"cbindgen", "cbindgen",
"servicepoint", "servicepoint",
@ -549,13 +634,24 @@ dependencies = [
[[package]] [[package]]
name = "servicepoint_binding_cs" name = "servicepoint_binding_cs"
version = "0.8.0" version = "0.9.0"
dependencies = [ dependencies = [
"csbindgen", "csbindgen",
"servicepoint", "servicepoint",
"servicepoint_binding_c", "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]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
@ -570,9 +666,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.77" version = "2.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -587,9 +683,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.12.0" version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"fastrand", "fastrand",
@ -598,6 +694,26 @@ dependencies = [
"windows-sys 0.59.0", "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]] [[package]]
name = "toml" name = "toml"
version = "0.8.19" version = "0.8.19"
@ -621,9 +737,9 @@ dependencies = [
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.22.20" version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
@ -633,10 +749,40 @@ dependencies = [
] ]
[[package]] [[package]]
name = "unicode-ident" name = "tungstenite"
version = "1.0.12" version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "utf8parse" name = "utf8parse"
@ -650,6 +796,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -740,9 +892,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.6.18" version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

View file

@ -6,7 +6,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.8.0" version = "0.9.0"
[workspace.lints.rust] [workspace.lints.rust]
missing-docs = "warn" missing-docs = "warn"

View file

@ -20,24 +20,36 @@ bzip2 = { version = "0.4", optional = true }
zstd = { version = "0.13", optional = true } zstd = { version = "0.13", optional = true }
rust-lzma = { version = "0.6.0", optional = true } rust-lzma = { version = "0.6.0", optional = true }
rand = { version = "0.8", optional = true } rand = { version = "0.8", optional = true }
tungstenite = { version = "0.24.0", optional = true }
once_cell = { version = "1.20.2", optional = true }
[features] [features]
default = ["compression_lzma"] default = ["compression_lzma", "protocol_udp", "cp437"]
compression_zlib = ["dep:flate2"] compression_zlib = ["dep:flate2"]
compression_bzip2 = ["dep:bzip2"] compression_bzip2 = ["dep:bzip2"]
compression_lzma = ["dep:rust-lzma"] compression_lzma = ["dep:rust-lzma"]
compression_zstd = ["dep:zstd"] compression_zstd = ["dep:zstd"]
all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"] all_compressions = ["compression_zlib", "compression_bzip2", "compression_lzma", "compression_zstd"]
rand = ["dep:rand"] rand = ["dep:rand"]
protocol_udp = []
protocol_websocket = ["dep:tungstenite"]
cp437 = ["dep:once_cell"]
[[example]] [[example]]
name = "random_brightness" name = "random_brightness"
required-features = ["rand"] required-features = ["rand"]
[[example]]
name = "game_of_life"
required-features = ["rand"]
[[example]]
name = "websocket"
required-features = ["protocol_websocket"]
[dev-dependencies] [dev-dependencies]
# for examples # for examples
clap = { version = "4.5", features = ["derive"] } clap = { version = "4.5", features = ["derive"] }
rand = "0.8"
[lints] [lints]
workspace = true workspace = true

View file

@ -9,6 +9,17 @@ In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wa
Display" or "Airport Display". Display" or "Airport Display".
This crate contains a library for parsing, encoding and sending packets to this display via UDP. 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 ## Examples
```rust ```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. Execute `cargo run --example` for a list of available examples and `cargo run --example <name>` to run one.
## Note on stability ## 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. 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. Please specify the full version including patch in your Cargo.toml until 1.0 is released.
## Installation
```bash
cargo add servicepoint
```
## Features ## Features
This library has multiple compression libraries as optional dependencies. This library has multiple optional dependencies.
If you do not need compression/decompression support you can disable those features. You can choose to (not) include them by toggling the related features.
In the likely case you only need one of them, you can include that one specifically.
```toml | Name | Default | Description |
[dependencies] |--------------------|---------|--------------------------------------------|
servicepoint = { version = "0.8.0", default-features = false, features = ["compression-bz"] } | 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 ## Everything else

View file

@ -2,7 +2,7 @@
use clap::Parser; use clap::Parser;
use servicepoint::{Command, Connection, Cp437Grid, Grid, Origin}; use servicepoint::{CharGrid, Command, Connection, Cp437Grid, Origin};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Cli { struct Cli {
@ -31,19 +31,15 @@ fn main() {
.expect("sending clear failed"); .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()); let grid = CharGrid::from(&*text);
for y in 0..cli.text.len() { let cp437_grid = Cp437Grid::from(&grid);
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);
}
}
connection connection
.send(Command::Cp437Data(Origin::new(0, 0), chars)) .send(Command::Cp437Data(Origin::new(0, 0), cp437_grid))
.expect("sending text failed"); .expect("sending text failed");
} }

View 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();
}

View file

@ -77,8 +77,8 @@ impl From<BrightnessGrid> for Vec<u8> {
} }
} }
impl From<BrightnessGrid> for PrimitiveGrid<u8> { impl From<&BrightnessGrid> for PrimitiveGrid<u8> {
fn from(value: PrimitiveGrid<Brightness>) -> Self { fn from(value: &PrimitiveGrid<Brightness>) -> Self {
let u8s = value let u8s = value
.iter() .iter()
.map(|brightness| (*brightness).into()) .map(|brightness| (*brightness).into())
@ -109,3 +109,33 @@ impl Distribution<Brightness> for Standard {
Brightness(rng.gen_range(Brightness::MIN.0..=Brightness::MAX.0)) 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]);
}
}

View file

@ -1,30 +1,50 @@
use bitvec::prelude::BitVec; use bitvec::prelude::BitVec;
use crate::{ use crate::{
command_code::CommandCode, compression::into_decompressed, Brightness, command_code::CommandCode,
BrightnessGrid, CompressionCode, Header, Origin, Packet, PixelGrid, Pixels, compression::into_decompressed,
PrimitiveGrid, SpBitVec, Tiles, TILE_SIZE, 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 /// Type alias for documenting the meaning of the u16 in enum values
pub type Offset = usize; 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. /// A low-level display command.
/// ///
/// This struct and associated functions implement the UDP protocol for the display. /// 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 /// # Examples
/// ///
/// ```rust /// ```rust
/// # use servicepoint::{Brightness, Command, Connection, Packet}; /// # use servicepoint::{Brightness, Command, Connection, packet::Packet};
/// /// #
/// // create command /// // create command
/// let command = Command::Brightness(Brightness::MAX); /// let command = Command::Brightness(Brightness::MAX);
/// ///
@ -56,6 +76,8 @@ pub enum Command {
/// Show text on the screen. /// Show text on the screen.
/// ///
/// The text is sent in the form of a 2D grid of characters.
///
/// <div class="warning"> /// <div class="warning">
/// The library does not currently convert between UTF-8 and CP-437. /// 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. /// Because Rust expects UTF-8 strings, it might be necessary to only send ASCII for now.
@ -64,15 +86,45 @@ pub enum Command {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```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}; /// # use servicepoint::{Command, Connection, Cp437Grid, Origin};
/// # let connection = Connection::open("127.0.0.1:2342").unwrap(); /// # let connection = Connection::Fake;
/// let chars = ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'].map(move |c| c as u8); /// let grid = Cp437Grid::load_ascii("Hello\nWorld", 5, false).unwrap();
/// let grid = Cp437Grid::load(5, 2, &chars);
/// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap(); /// connection.send(Command::Cp437Data(Origin::new(2, 2), grid)).unwrap();
/// ``` /// ```
Cp437Data(Origin<Tiles>, Cp437Grid), 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), BitmapLinearWin(Origin<Pixels>, PixelGrid, CompressionCode),
/// Set the brightness of all tiles to the same value. /// 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 /// 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. /// 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), BitmapLinear(Offset, SpBitVec, CompressionCode),
/// Set pixel data according to an and-mask starting at the offset. /// 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 /// 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. /// 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), BitmapLinearAnd(Offset, SpBitVec, CompressionCode),
/// Set pixel data according to an or-mask starting at the offset. /// 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 /// 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. /// 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), BitmapLinearOr(Offset, SpBitVec, CompressionCode),
/// Set pixel data according to a xor-mask starting at the offset. /// 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 /// 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. /// 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), BitmapLinearXor(Offset, SpBitVec, CompressionCode),
/// Kills the udp daemon on the display, which usually results in a restart. /// Kills the udp daemon on the display, which usually results in a restart.
@ -166,7 +218,7 @@ pub enum Command {
} }
#[derive(Debug)] #[derive(Debug)]
/// Err values for `Command::try_from`. /// Err values for [Command::try_from].
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum TryFromPacketError { pub enum TryFromPacketError {
/// the contained command code does not correspond to a known command /// the contained command code does not correspond to a known command
@ -188,7 +240,7 @@ pub enum TryFromPacketError {
impl TryFrom<Packet> for Command { impl TryFrom<Packet> for Command {
type Error = TryFromPacketError; 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> { fn try_from(packet: Packet) -> Result<Self, Self::Error> {
let Packet { let Packet {
header: Header { header: Header {
@ -443,9 +495,12 @@ impl Command {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
bitvec::prelude::BitVec, command::TryFromPacketError, bitvec::prelude::BitVec,
command_code::CommandCode, origin::Pixels, Brightness, Command, command::TryFromPacketError,
CompressionCode, Header, Origin, Packet, PixelGrid, PrimitiveGrid, command_code::CommandCode,
origin::Pixels,
packet::{Header, Packet},
Brightness, Command, CompressionCode, Origin, PixelGrid, PrimitiveGrid,
}; };
fn round_trip(original: Command) { fn round_trip(original: Command) {

View file

@ -1,4 +1,4 @@
/// The u16 command codes used for the `Commands`. /// The u16 command codes used for the [Command]s.
#[repr(u16)] #[repr(u16)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub(crate) enum CommandCode { pub(crate) enum CommandCode {

View file

@ -8,7 +8,7 @@ use flate2::{FlushCompress, FlushDecompress, Status};
#[cfg(feature = "compression_zstd")] #[cfg(feature = "compression_zstd")]
use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder}; use zstd::{Decoder as ZstdDecoder, Encoder as ZstdEncoder};
use crate::{CompressionCode, Payload}; use crate::{packet::Payload, CompressionCode};
pub(crate) fn into_decompressed( pub(crate) fn into_decompressed(
kind: CompressionCode, kind: CompressionCode,

View file

@ -1,29 +1,61 @@
use std::fmt::Debug; use std::fmt::Debug;
use std::net::{ToSocketAddrs, UdpSocket};
use log::{debug, info}; use crate::packet::Packet;
use crate::Packet;
/// A connection to the display. /// A connection to the display.
/// ///
/// Used to send [Packets][Packet] or [Commands][crate::Command].
///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// let connection = servicepoint::Connection::open("172.23.42.29:2342") /// let connection = servicepoint::Connection::open("127.0.0.1:2342")
/// .expect("connection failed"); /// .expect("connection failed");
/// connection.send(servicepoint::Command::Clear) /// connection.send(servicepoint::Command::Clear)
/// .expect("send failed"); /// .expect("send failed");
/// ``` /// ```
#[derive(Debug)]
pub enum Connection { pub enum Connection {
/// A real connection using the UDP protocol /// A connection using the UDP protocol.
Udp(UdpSocket), ///
/// A fake connection for testing that does not actually send anything /// 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, Fake,
/// A fake connection for testing that does not actually send anything.
///
/// This variant does not allow immutable send.
FakeMutableSend,
} }
#[derive(Debug)] #[derive(Debug)]
pub enum SendError { pub enum SendError {
IoError(std::io::Error), IoError(std::io::Error),
#[cfg(feature = "protocol_websocket")]
WebsocketError(tungstenite::Error),
} }
impl Connection { 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. /// 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 /// # Errors
/// ///
/// Any errors resulting from binding the udp socket. /// Any errors resulting from binding the udp socket.
/// ///
/// # Examples /// # Examples
/// ```rust /// ```rust
/// let connection = servicepoint::Connection::open("172.23.42.29:2342") /// let connection = servicepoint::Connection::open("127.0.0.1:2342")
/// .expect("connection failed"); /// .expect("connection failed");
/// ``` /// ```
pub fn open(addr: impl ToSocketAddrs + Debug) -> std::io::Result<Self> { #[cfg(feature = "protocol_udp")]
info!("connecting to {addr:?}"); pub fn open(
let socket = UdpSocket::bind("0.0.0.0:0")?; 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)?; socket.connect(addr)?;
Ok(Self::Udp(socket)) 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. /// 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 /// # Arguments
/// ///
/// - `packet`: the packet-like to send /// - `packet`: the packet-like to send
@ -58,23 +176,70 @@ impl Connection {
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # let connection = servicepoint::Connection::Fake; /// let mut connection = servicepoint::Connection::FakeMutableSend;
/// // turn off all pixels on display /// // turn off all pixels on display
/// connection.send(servicepoint::Command::Clear) /// connection.send_mut(servicepoint::Command::Clear)
/// .expect("send failed"); /// .expect("send failed");
/// ``` /// ```
pub fn send(&self, packet: impl Into<Packet>) -> Result<(), SendError> { pub fn send_mut(
let packet = packet.into(); &mut self,
debug!("sending {packet:?}"); packet: impl Into<Packet>,
let data: Vec<u8> = packet.into(); ) -> Result<(), SendError> {
match self { 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 socket
.send(&data) .send(tungstenite::Message::Binary(data))
.map_err(SendError::IoError) .map_err(SendError::WebsocketError)
.map(move |_| ()) // ignore Ok value
} }
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()
}
}

View 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);
}
}

View file

@ -79,12 +79,12 @@ pub trait Grid<T> {
assert!( assert!(
x < self.width(), x < self.width(),
"cannot access index [{x}, {y}] because x is outside of bounds 0..{}", "cannot access index [{x}, {y}] because x is outside of bounds 0..{}",
self.width() self.width() - 1
); );
assert!( assert!(
y < self.height(), y < self.height(),
"cannot access byte [{x}, {y}] because y is outside of bounds 0..{}", "cannot access index [{x}, {y}] because y is outside of bounds 0..{}",
self.height() self.height() - 1
); );
} }
} }

View file

@ -1,5 +1,9 @@
//! Abstractions for the UDP protocol of the CCCB servicepoint display. //! 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 //! # Examples
//! //!
//! ```rust //! ```rust
@ -37,13 +41,13 @@ pub use bitvec;
use bitvec::prelude::{BitVec, Msb0}; use bitvec::prelude::{BitVec, Msb0};
pub use crate::brightness::{Brightness, BrightnessGrid}; 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::compression_code::CompressionCode;
pub use crate::connection::Connection; pub use crate::connection::Connection;
pub use crate::cp437::{CharGrid, Cp437Grid};
pub use crate::data_ref::DataRef; pub use crate::data_ref::DataRef;
pub use crate::grid::Grid; pub use crate::grid::Grid;
pub use crate::origin::{Origin, Pixels, Tiles}; pub use crate::origin::{Origin, Pixels, Tiles};
pub use crate::packet::{Header, Packet, Payload};
pub use crate::pixel_grid::PixelGrid; pub use crate::pixel_grid::PixelGrid;
pub use crate::primitive_grid::PrimitiveGrid; pub use crate::primitive_grid::PrimitiveGrid;
@ -55,10 +59,11 @@ mod command_code;
mod compression; mod compression;
mod compression_code; mod compression_code;
mod connection; mod connection;
mod cp437;
mod data_ref; mod data_ref;
mod grid; mod grid;
mod origin; mod origin;
mod packet; pub mod packet;
mod pixel_grid; mod pixel_grid;
mod primitive_grid; mod primitive_grid;

View file

@ -1,3 +1,4 @@
use crate::TILE_SIZE;
use std::marker::PhantomData; use std::marker::PhantomData;
/// An origin marks the top left position of a window sent to the display. /// 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> { 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 { pub fn new(x: usize, y: usize) -> Self {
Self { Self {
x, x,
@ -46,3 +54,69 @@ pub struct Tiles();
impl DisplayUnit for Pixels {} impl DisplayUnit for Pixels {}
impl DisplayUnit for Tiles {} 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();
}
}

View file

@ -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 std::mem::size_of;
use crate::command_code::CommandCode;
use crate::compression::into_compressed; use crate::compression::into_compressed;
use crate::{ use crate::{
Command, CompressionCode, Grid, Offset, Origin, PixelGrid, Pixels, Tiles, command_code::CommandCode, Command, CompressionCode, Grid, Offset, Origin,
TILE_SIZE, PixelGrid, Pixels, Tiles, TILE_SIZE,
}; };
/// A raw header. /// A raw header.
@ -13,8 +37,6 @@ use crate::{
/// payload, where applicable. /// payload, where applicable.
/// ///
/// Because the meaning of most fields depend on the command, there are no speaking names for them. /// 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)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct Header { pub struct Header {
/// The first two bytes specify which command this packet represents. /// 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. /// 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)] #[derive(Clone, Debug, PartialEq)]
pub struct Packet { pub struct Packet {
@ -98,9 +102,9 @@ impl From<Packet> for Vec<u8> {
impl TryFrom<&[u8]> for Packet { impl TryFrom<&[u8]> for Packet {
type Error = (); 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> { fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.len() < size_of::<Header>() { if value.len() < size_of::<Header>() {
return Err(()); return Err(());
@ -135,7 +139,7 @@ impl TryFrom<Vec<u8>> for Packet {
} }
impl From<Command> 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)] #[allow(clippy::cast_possible_truncation)]
fn from(value: Command) -> Self { fn from(value: Command) -> Self {
match value { match value {
@ -210,7 +214,7 @@ impl From<Command> for Packet {
} }
impl Packet { impl Packet {
/// Helper method for `BitMapLinear*`-Commands into `Packet` /// Helper method for `BitMapLinear*`-Commands into [Packet]
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
fn bitmap_linear_into_packet( fn bitmap_linear_into_packet(
command: CommandCode, command: CommandCode,
@ -312,7 +316,7 @@ impl Packet {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{Header, Packet}; use super::*;
#[test] #[test]
fn round_trip() { fn round_trip() {
@ -327,7 +331,7 @@ mod tests {
payload: vec![42u8; 23], payload: vec![42u8; 23],
}; };
let data: Vec<u8> = p.into(); let data: Vec<u8> = p.into();
let p = Packet::try_from(&*data).unwrap(); let p = Packet::try_from(data).unwrap();
assert_eq!( assert_eq!(
p, p,
Packet { Packet {

View file

@ -13,14 +13,14 @@ pub struct PixelGrid {
} }
impl PixelGrid { impl PixelGrid {
/// Creates a new `PixelGrid` with the specified dimensions. /// Creates a new [PixelGrid] with the specified dimensions.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - `width`: size in pixels in x-direction /// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-direction /// - `height`: size in pixels in y-direction
/// ///
/// returns: `PixelGrid` initialized to all pixels off /// returns: [PixelGrid] initialized to all pixels off
/// ///
/// # Panics /// # Panics
/// ///
@ -40,14 +40,14 @@ impl PixelGrid {
Self::new(PIXEL_WIDTH, PIXEL_HEIGHT) 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 /// # Arguments
/// ///
/// - `width`: size in pixels in x-direction /// - `width`: size in pixels in x-direction
/// - `height`: size in pixels in y-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 /// # 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: /// Order is equivalent to the following loop:
/// ``` /// ```
@ -80,7 +80,7 @@ impl PixelGrid {
self.bit_vec.iter().by_refs() 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: /// Order is equivalent to the following loop:
/// ``` /// ```
@ -107,7 +107,7 @@ impl PixelGrid {
self.bit_vec.iter_mut() 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 { pub fn iter_rows(&self) -> IterRows {
IterRows { IterRows {
pixel_grid: self, pixel_grid: self,
@ -117,7 +117,7 @@ impl PixelGrid {
} }
impl Grid<bool> for 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 /// # Arguments
/// ///
@ -139,7 +139,7 @@ impl Grid<bool> for PixelGrid {
self.bit_vec[x + y * self.width] 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 /// # Arguments
/// ///
@ -169,7 +169,7 @@ impl DataRef<u8> for PixelGrid {
} }
impl From<PixelGrid> for Vec<u8> { 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 { fn from(value: PixelGrid) -> Self {
value.bit_vec.into() value.bit_vec.into()
} }

View file

@ -14,14 +14,14 @@ pub struct PrimitiveGrid<T: PrimitiveGridType> {
} }
impl<T: PrimitiveGridType> PrimitiveGrid<T> { impl<T: PrimitiveGridType> PrimitiveGrid<T> {
/// Creates a new `PrimitiveGrid` with the specified dimensions. /// Creates a new [PrimitiveGrid] with the specified dimensions.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - width: size in x-direction /// - width: size in x-direction
/// - height: size in y-direction /// - height: size in y-direction
/// ///
/// returns: `PrimitiveGrid` initialized to default value. /// returns: [PrimitiveGrid] initialized to default value.
pub fn new(width: usize, height: usize) -> Self { pub fn new(width: usize, height: usize) -> Self {
Self { Self {
data: vec![Default::default(); width * height], data: vec![Default::default(); width * height],
@ -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 /// # 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: /// Order is equivalent to the following loop:
/// ``` /// ```
@ -63,7 +63,7 @@ impl<T: PrimitiveGridType> PrimitiveGrid<T> {
self.data.iter() self.data.iter()
} }
/// Iterate over all rows in `PrimitiveGrid` top to bottom. /// Iterate over all rows in [PrimitiveGrid] top to bottom.
pub fn iter_rows(&self) -> IterRows<T> { pub fn iter_rows(&self) -> IterRows<T> {
IterRows { IterRows {
byte_grid: self, byte_grid: self,
@ -168,7 +168,7 @@ impl<T: PrimitiveGridType> DataRef<T> for PrimitiveGrid<T> {
} }
impl<T: PrimitiveGridType> From<PrimitiveGrid<T>> for Vec<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 { fn from(value: PrimitiveGrid<T>) -> Self {
value.data value.data
} }

View file

@ -17,9 +17,12 @@ crate-type = ["staticlib", "cdylib", "rlib"]
cbindgen = "0.27.0" cbindgen = "0.27.0"
[dependencies.servicepoint] [dependencies.servicepoint]
version = "0.8.0" version = "0.9.0"
path = "../servicepoint" path = "../servicepoint"
features = ["all_compressions"] features = ["all_compressions"]
[lints] [lints]
workspace = true workspace = true
[package.metadata.docs.rs]
all-features = true

View file

@ -12,5 +12,6 @@ fn main() {
let mut cc = cc::Build::new(); let mut cc = cc::Build::new();
cc.file("src/main.c"); cc.file("src/main.c");
cc.include(&sp_include); cc.include(&sp_include);
cc.opt_level(2);
cc.compile("lang_c"); cc.compile("lang_c");
} }

View file

@ -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. */ /* 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); size_t sp_pixel_grid_width(const struct SPPixelGrid *pixel_grid);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif // __cplusplus #endif // __cplusplus

View file

@ -40,7 +40,9 @@ pub unsafe extern "C" fn sp_cp437_grid_new(
width: usize, width: usize,
height: usize, height: usize,
) -> *mut SPCp437Grid { ) -> *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. /// 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, data_length: usize,
) -> *mut SPCp437Grid { ) -> *mut SPCp437Grid {
let data = std::slice::from_raw_parts(data, data_length); 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`. /// Clones a `SPCp437Grid`.

View file

@ -7,7 +7,7 @@ use std::ptr::null_mut;
use crate::SPCommand; use crate::SPCommand;
/// The raw packet /// The raw packet
pub struct SPPacket(pub(crate) servicepoint::Packet); pub struct SPPacket(pub(crate) servicepoint::packet::Packet);
/// Turns a `SPCommand` into a `SPPacket`. /// Turns a `SPCommand` into a `SPPacket`.
/// The `SPCommand` gets consumed. /// The `SPCommand` gets consumed.
@ -49,7 +49,7 @@ pub unsafe extern "C" fn sp_packet_try_load(
length: usize, length: usize,
) -> *mut SPPacket { ) -> *mut SPPacket {
let data = std::slice::from_raw_parts(data, length); 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(), Err(_) => null_mut(),
Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))), Ok(packet) => Box::into_raw(Box::new(SPPacket(packet))),
} }

View file

@ -13,8 +13,8 @@ test = false
csbindgen = "1.9.3" csbindgen = "1.9.3"
[dependencies] [dependencies]
servicepoint_binding_c = { version = "0.8.0", path = "../servicepoint_binding_c" } servicepoint_binding_c = { version = "0.9.0", path = "../servicepoint_binding_c" }
servicepoint = { version = "0.8.0", path = "../servicepoint" } servicepoint = { version = "0.9.0", path = "../servicepoint" }
[lints] [lints]
workspace = true workspace = true

View file

@ -11,7 +11,7 @@
<PropertyGroup> <PropertyGroup>
<PackageId>ServicePoint</PackageId> <PackageId>ServicePoint</PackageId>
<Version>0.8.0</Version> <Version>0.9.0</Version>
<Authors>Repository Authors</Authors> <Authors>Repository Authors</Authors>
<Company>None</Company> <Company>None</Company>
<Product>ServicePoint</Product> <Product>ServicePoint</Product>