Compare commits
34 Commits
visions-ui
...
main
Author | SHA1 | Date | |
---|---|---|---|
36eef45971 | |||
596de6525f | |||
9175b9d4cc | |||
958d18b9a8 | |||
bc9e24c0c9 | |||
b444326c1c | |||
8288fdbb6b | |||
155d2ba18e | |||
132c85e99d | |||
aea858dd17 | |||
85e5d0bb5e | |||
21c6f30a7d | |||
45dc19c329 | |||
a69a864dca | |||
f004aa3514 | |||
54dd004915 | |||
fb0e914edf | |||
47e90cc6f9 | |||
afa846f7e0 | |||
254a2aefd7 | |||
a07ecae04a | |||
76de75210f | |||
e5b3c7e4e1 | |||
704009b76c | |||
cd5837a437 | |||
a8a61cf03f | |||
0df0ff9419 | |||
44ee6ec8a5 | |||
52a0d6e3f2 | |||
200c13a14e | |||
1c3d0711e1 | |||
e16fef2b14 | |||
e0392a4150 | |||
359ab96779 |
1715
Cargo.lock
generated
1715
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"authdb",
|
"authdb",
|
||||||
|
# "bike-lights/bike",
|
||||||
"bike-lights/core",
|
"bike-lights/core",
|
||||||
"bike-lights/simulator",
|
"bike-lights/simulator",
|
||||||
"changeset",
|
"changeset",
|
||||||
@ -22,18 +23,16 @@ members = [
|
|||||||
"gm-dash/server",
|
"gm-dash/server",
|
||||||
"hex-grid",
|
"hex-grid",
|
||||||
"icon-test",
|
"icon-test",
|
||||||
|
"l10n-db",
|
||||||
"memorycache",
|
"memorycache",
|
||||||
"nom-training",
|
"nom-training",
|
||||||
"otg/core",
|
"otg/core",
|
||||||
"otg/gtk",
|
"otg/gtk",
|
||||||
|
"pico-st7789",
|
||||||
"result-extended",
|
"result-extended",
|
||||||
"screenplay",
|
"screenplay",
|
||||||
"sgf",
|
"sgf",
|
||||||
"timezone-testing",
|
"timezone-testing",
|
||||||
"tree",
|
"tree",
|
||||||
"visions/server",
|
"visions/server",
|
||||||
"visions/types",
|
|
||||||
"visions/ui",
|
|
||||||
"visions/yew-app",
|
|
||||||
# "bike-lights/bike",
|
|
||||||
]
|
]
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.0": "09r6drylvgy8vv8k20lnbvwq8gp09h7smfn6h1rxsy15pgh629si",
|
"registry+https://github.com/rust-lang/crates.io-index#adler2@2.0.0": "09r6drylvgy8vv8k20lnbvwq8gp09h7smfn6h1rxsy15pgh629si",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#adler32@1.2.0": "0d7jq7jsjyhsgbhnfq5fvrlh9j0i9g1fqrl2735ibv5f75yjgqda",
|
"registry+https://github.com/rust-lang/crates.io-index#adler32@1.2.0": "0d7jq7jsjyhsgbhnfq5fvrlh9j0i9g1fqrl2735ibv5f75yjgqda",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#adler@1.0.2": "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj",
|
"registry+https://github.com/rust-lang/crates.io-index#adler@1.0.2": "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.11": "04chdfkls5xmhp1d48gnjsmglbqibizs3bpbj6rsj604m10si7g8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.3": "05mrpkvdgp5d20y2p989f187ry9diliijgwrs254fs9s1m1x6q4f",
|
"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.3": "05mrpkvdgp5d20y2p989f187ry9diliijgwrs254fs9s1m1x6q4f",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#allocator-api2@0.2.21": "08zrzs022xwndihvzdn78yqarv2b9696y67i6h78nla3ww87jgb8",
|
"registry+https://github.com/rust-lang/crates.io-index#allocator-api2@0.2.21": "08zrzs022xwndihvzdn78yqarv2b9696y67i6h78nla3ww87jgb8",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#android-tzdata@0.1.1": "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9",
|
"registry+https://github.com/rust-lang/crates.io-index#android-tzdata@0.1.1": "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9",
|
||||||
@ -15,7 +14,6 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#anstyle-wincon@3.0.7": "0kmf0fq4c8yribdpdpylzz1zccpy84hizmcsac3wrac1f7kk8dfa",
|
"registry+https://github.com/rust-lang/crates.io-index#anstyle-wincon@3.0.7": "0kmf0fq4c8yribdpdpylzz1zccpy84hizmcsac3wrac1f7kk8dfa",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#anstyle@1.0.10": "1yai2vppmd7zlvlrp9grwll60knrmscalf8l2qpfz8b7y5lkpk2m",
|
"registry+https://github.com/rust-lang/crates.io-index#anstyle@1.0.10": "1yai2vppmd7zlvlrp9grwll60knrmscalf8l2qpfz8b7y5lkpk2m",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#anyhow@1.0.95": "010vd1ki8w84dzgx6c81sc8qm9n02fxic1gkpv52zp4nwrn0kb1l",
|
"registry+https://github.com/rust-lang/crates.io-index#anyhow@1.0.95": "010vd1ki8w84dzgx6c81sc8qm9n02fxic1gkpv52zp4nwrn0kb1l",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#assert-json-diff@2.0.2": "04mg3w0rh3schpla51l18362hsirl23q93aisws2irrj32wg5r27",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-channel@1.9.0": "0dbdlkzlncbibd3ij6y6jmvjd0cmdn48ydcfdpfhw09njd93r5c1",
|
"registry+https://github.com/rust-lang/crates.io-index#async-channel@1.9.0": "0dbdlkzlncbibd3ij6y6jmvjd0cmdn48ydcfdpfhw09njd93r5c1",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-channel@2.3.1": "0skvwxj6ysfc6d7bhczz9a2550260g62bm5gl0nmjxxyn007id49",
|
"registry+https://github.com/rust-lang/crates.io-index#async-channel@2.3.1": "0skvwxj6ysfc6d7bhczz9a2550260g62bm5gl0nmjxxyn007id49",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-executor@1.13.1": "1v6w1dbvsmw6cs4dk4lxj5dvrikc6xi479wikwaab2qy3h09mjih",
|
"registry+https://github.com/rust-lang/crates.io-index#async-executor@1.13.1": "1v6w1dbvsmw6cs4dk4lxj5dvrikc6xi479wikwaab2qy3h09mjih",
|
||||||
@ -27,13 +25,8 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#async-trait@0.1.85": "0mm0gwad44zs7mna4a0m1z4dhzpmydfj73w4wm23c8xpnhrli4rz",
|
"registry+https://github.com/rust-lang/crates.io-index#async-trait@0.1.85": "0mm0gwad44zs7mna4a0m1z4dhzpmydfj73w4wm23c8xpnhrli4rz",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#atoi@2.0.0": "0a05h42fggmy7h0ajjv6m7z72l924i7igbx13hk9d8pyign9k3gj",
|
"registry+https://github.com/rust-lang/crates.io-index#atoi@2.0.0": "0a05h42fggmy7h0ajjv6m7z72l924i7igbx13hk9d8pyign9k3gj",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2": "1h5av1lw56m0jf0fd3bchxq8a30xv0b4wv8s4zkp4s0i7mfvs18m",
|
"registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2": "1h5av1lw56m0jf0fd3bchxq8a30xv0b4wv8s4zkp4s0i7mfvs18m",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#auto-future@1.0.0": "0wykbakzh227vz6frx9p48zsq0wpswgmb7v3917m53m7gr2pw7iw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#autocfg@0.1.8": "0y4vw4l4izdxq1v0rrhvmlbqvalrqrmk60v1z0dqlgnlbzkl7phd",
|
"registry+https://github.com/rust-lang/crates.io-index#autocfg@0.1.8": "0y4vw4l4izdxq1v0rrhvmlbqvalrqrmk60v1z0dqlgnlbzkl7phd",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#autocfg@1.4.0": "09lz3by90d2hphbq56znag9v87gfpd9gb8nr82hll8z6x2nhprdc",
|
"registry+https://github.com/rust-lang/crates.io-index#autocfg@1.4.0": "09lz3by90d2hphbq56znag9v87gfpd9gb8nr82hll8z6x2nhprdc",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#axum-core@0.4.5": "16b1496c4gm387q20hkv5ic3k5bd6xmnvk50kwsy6ymr8rhvvwh9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#axum-macros@0.4.2": "1klv77c889jm05bzayaaiinalarhvh2crc2w4nvp3l581xaj7lap",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#axum-test@16.4.1": "1p5qxacvxsagnqq30nr2wznjyhgb8svsfb925ah3d2b0s91s9qv3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#axum@0.7.9": "07z7wqczi9i8xb4460rvn39p4wjqwr32hx907crd1vwb2fy8ijpd",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#az@1.2.1": "0ww9k1w3al7x5qmb7f13v3s9c2pg1pdxbs8xshqy6zyrchj4qzkv",
|
"registry+https://github.com/rust-lang/crates.io-index#az@1.2.1": "0ww9k1w3al7x5qmb7f13v3s9c2pg1pdxbs8xshqy6zyrchj4qzkv",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#backtrace@0.3.74": "06pfif7nwx66qf2zaanc2fcq7m64i91ki9imw9xd3bnz5hrwp0ld",
|
"registry+https://github.com/rust-lang/crates.io-index#backtrace@0.3.74": "06pfif7nwx66qf2zaanc2fcq7m64i91ki9imw9xd3bnz5hrwp0ld",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#base64@0.21.7": "0rw52yvsk75kar9wgqfwgb414kvil1gn7mqkrhn9zf1537mpsacx",
|
"registry+https://github.com/rust-lang/crates.io-index#base64@0.21.7": "0rw52yvsk75kar9wgqfwgb414kvil1gn7mqkrhn9zf1537mpsacx",
|
||||||
@ -53,7 +46,6 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#bytemuck@1.21.0": "18wj81x9xhqcd6985r8qxmbik6szjfjfj62q3xklw8h2p3x7srgg",
|
"registry+https://github.com/rust-lang/crates.io-index#bytemuck@1.21.0": "18wj81x9xhqcd6985r8qxmbik6szjfjfj62q3xklw8h2p3x7srgg",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0": "0jzncxyf404mwqdbspihyzpkndfgda450l0893pz5xj685cg5l0z",
|
"registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0": "0jzncxyf404mwqdbspihyzpkndfgda450l0893pz5xj685cg5l0z",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bytes@1.9.0": "16ykzx24v1x4f42v2lxyvlczqhdfji3v7r4ghwckpwijzvb1hn9j",
|
"registry+https://github.com/rust-lang/crates.io-index#bytes@1.9.0": "16ykzx24v1x4f42v2lxyvlczqhdfji3v7r4ghwckpwijzvb1hn9j",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bytesize@1.3.0": "1k3aak70iwz4s2gsjbxf0ws4xnixqbdz6p2ha96s06748fpniqx3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cairo-rs@0.18.5": "1qjfkcq3mrh3p01nnn71dy3kn99g21xx3j8xcdvzn8ll2pq6x8lc",
|
"registry+https://github.com/rust-lang/crates.io-index#cairo-rs@0.18.5": "1qjfkcq3mrh3p01nnn71dy3kn99g21xx3j8xcdvzn8ll2pq6x8lc",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cairo-sys-rs@0.18.2": "0lfsxl7ylw3phbnwmz3k58j1gnqi6kc2hdc7g3bb7f4hwnl9yp38",
|
"registry+https://github.com/rust-lang/crates.io-index#cairo-sys-rs@0.18.2": "0lfsxl7ylw3phbnwmz3k58j1gnqi6kc2hdc7g3bb7f4hwnl9yp38",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.10": "0aaj2ivamhfzhgb9maasnfkh03s2mzhzpzwrkghgzbkfnv5qy80k",
|
"registry+https://github.com/rust-lang/crates.io-index#cc@1.2.10": "0aaj2ivamhfzhgb9maasnfkh03s2mzhzpzwrkghgzbkfnv5qy80k",
|
||||||
@ -64,9 +56,9 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#chrono-tz@0.8.6": "0vlksnmpb6rd4h55245agnfhphnpslwnq9al3aw3is43dd3f16nm",
|
"registry+https://github.com/rust-lang/crates.io-index#chrono-tz@0.8.6": "0vlksnmpb6rd4h55245agnfhphnpslwnq9al3aw3is43dd3f16nm",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#chrono@0.4.39": "09g8nf409lb184kl9j4s85k0kn8wzgjkp5ls9zid50b886fwqdky",
|
"registry+https://github.com/rust-lang/crates.io-index#chrono@0.4.39": "09g8nf409lb184kl9j4s85k0kn8wzgjkp5ls9zid50b886fwqdky",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clang-sys@1.8.1": "1x1r9yqss76z8xwpdanw313ss6fniwc1r7dzb5ycjn0ph53kj0hb",
|
"registry+https://github.com/rust-lang/crates.io-index#clang-sys@1.8.1": "1x1r9yqss76z8xwpdanw313ss6fniwc1r7dzb5ycjn0ph53kj0hb",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clap@4.5.26": "10v7qvn90calfbhap1c4r249i5c7fbxj09fn3szfz9pkis85xsx8",
|
"registry+https://github.com/rust-lang/crates.io-index#clap@4.5.30": "0vcyrn4ymq2gd56sl3xnfki8q8llg64sj3rj3qx33mgsf66v3dwj",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clap_builder@4.5.26": "08f1mzcvi7zjhm7hvz6al4jnv70ccqhwiaq74hihlspwnl0iic4n",
|
"registry+https://github.com/rust-lang/crates.io-index#clap_builder@4.5.30": "0369xis2ar46icsaxqyy37976mlb62alzyx4j53k99vq2w3v4pd3",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clap_derive@4.5.24": "131ih3dm76srkbpfx7zfspp9b556zgzj31wqhl0ji2b39lcmbdsl",
|
"registry+https://github.com/rust-lang/crates.io-index#clap_derive@4.5.28": "1vgigkhljp3r8r5lwdrn1ij93nafmjwh8cx77nppb9plqsaysk5z",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clap_lex@0.7.4": "19nwfls5db269js5n822vkc8dw0wjq2h1wf0hgr06ld2g52d2spl",
|
"registry+https://github.com/rust-lang/crates.io-index#clap_lex@0.7.4": "19nwfls5db269js5n822vkc8dw0wjq2h1wf0hgr06ld2g52d2spl",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cloudabi@0.0.3": "0kxcg83jlihy0phnd2g8c2c303px3l2p3pkjz357ll6llnd5pz6x",
|
"registry+https://github.com/rust-lang/crates.io-index#cloudabi@0.0.3": "0kxcg83jlihy0phnd2g8c2c303px3l2p3pkjz357ll6llnd5pz6x",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#color_quant@1.1.0": "12q1n427h2bbmmm1mnglr57jaz2dj9apk0plcxw7nwqiai7qjyrx",
|
"registry+https://github.com/rust-lang/crates.io-index#color_quant@1.1.0": "12q1n427h2bbmmm1mnglr57jaz2dj9apk0plcxw7nwqiai7qjyrx",
|
||||||
@ -76,7 +68,6 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#convert_case@0.6.0": "1jn1pq6fp3rri88zyw6jlhwwgf6qiyc08d6gjv0qypgkl862n67c",
|
"registry+https://github.com/rust-lang/crates.io-index#convert_case@0.6.0": "1jn1pq6fp3rri88zyw6jlhwwgf6qiyc08d6gjv0qypgkl862n67c",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cookie-factory@0.3.3": "18mka6fk3843qq3jw1fdfvzyv05kx7kcmirfbs2vg2kbw9qzm1cq",
|
"registry+https://github.com/rust-lang/crates.io-index#cookie-factory@0.3.3": "18mka6fk3843qq3jw1fdfvzyv05kx7kcmirfbs2vg2kbw9qzm1cq",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cookie@0.17.0": "096c52jg9iq4lfcps2psncswv33fc30mmnaa2sbzzcfcw71kgyvy",
|
"registry+https://github.com/rust-lang/crates.io-index#cookie@0.17.0": "096c52jg9iq4lfcps2psncswv33fc30mmnaa2sbzzcfcw71kgyvy",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cookie@0.18.1": "0iy749flficrlvgr3hjmf3igr738lk81n5akzf4ym4cs6cxg7pjd",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cool_asserts@2.0.3": "1v18dg7ifx41k2f82j3gsnpm1fg9wk5s4zv7sf42c7pnad72b7zf",
|
"registry+https://github.com/rust-lang/crates.io-index#cool_asserts@2.0.3": "1v18dg7ifx41k2f82j3gsnpm1fg9wk5s4zv7sf42c7pnad72b7zf",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#core-foundation-sys@0.8.7": "12w8j73lazxmr1z0h98hf3z623kl8ms7g07jch7n4p8f9nwlhdkp",
|
"registry+https://github.com/rust-lang/crates.io-index#core-foundation-sys@0.8.7": "12w8j73lazxmr1z0h98hf3z623kl8ms7g07jch7n4p8f9nwlhdkp",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#core-foundation@0.9.4": "13zvbbj07yk3b61b8fhwfzhy35535a583irf23vlcg59j7h9bqci",
|
"registry+https://github.com/rust-lang/crates.io-index#core-foundation@0.9.4": "13zvbbj07yk3b61b8fhwfzhy35535a583irf23vlcg59j7h9bqci",
|
||||||
@ -94,7 +85,6 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#deflate@0.8.6": "0x6iqlayg129w63999kz97m279m0jj4x4sm6gkqlvmp73y70yxvk",
|
"registry+https://github.com/rust-lang/crates.io-index#deflate@0.8.6": "0x6iqlayg129w63999kz97m279m0jj4x4sm6gkqlvmp73y70yxvk",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#der@0.7.9": "1h4vzjfa1lczxdf8avfj9qlwh1qianqlxdy1g5rn762qnvkzhnzm",
|
"registry+https://github.com/rust-lang/crates.io-index#der@0.7.9": "1h4vzjfa1lczxdf8avfj9qlwh1qianqlxdy1g5rn762qnvkzhnzm",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11": "1d1ibqqnr5qdrpw8rclwrf1myn3wf0dygl04idf4j2s49ah6yaxl",
|
"registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.11": "1d1ibqqnr5qdrpw8rclwrf1myn3wf0dygl04idf4j2s49ah6yaxl",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#diff@0.1.13": "1j0nzjxci2zqx63hdcihkp0a4dkdmzxd7my4m7zk6cjyfy34j9an",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7": "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy",
|
"registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7": "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#dimensioned@0.7.0": "09ky8s3higkf677lmyqg30hmj66gpg7hx907s6hfvbk2a9av05r5",
|
"registry+https://github.com/rust-lang/crates.io-index#dimensioned@0.7.0": "09ky8s3higkf677lmyqg30hmj66gpg7hx907s6hfvbk2a9av05r5",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#dimensioned@0.8.0": "15s3j4ry943xqlac63bp81sgdk9s3yilysabzww35j9ibmnaic50",
|
"registry+https://github.com/rust-lang/crates.io-index#dimensioned@0.8.0": "15s3j4ry943xqlac63bp81sgdk9s3yilysabzww35j9ibmnaic50",
|
||||||
@ -110,8 +100,6 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#event-listener@2.5.3": "1q4w3pndc518crld6zsqvvpy9lkzwahp2zgza9kbzmmqh9gif1h2",
|
"registry+https://github.com/rust-lang/crates.io-index#event-listener@2.5.3": "1q4w3pndc518crld6zsqvvpy9lkzwahp2zgza9kbzmmqh9gif1h2",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#event-listener@5.4.0": "1bii2gn3vaa33s0gr2zph7cagiq0ppcfxcxabs24ri9z9kgar4il",
|
"registry+https://github.com/rust-lang/crates.io-index#event-listener@5.4.0": "1bii2gn3vaa33s0gr2zph7cagiq0ppcfxcxabs24ri9z9kgar4il",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#exr@1.73.0": "1q47yq78q9k210r6jy1wwrilxwwxqavik9l3l426rd17k7srfcgq",
|
"registry+https://github.com/rust-lang/crates.io-index#exr@1.73.0": "1q47yq78q9k210r6jy1wwrilxwwxqavik9l3l426rd17k7srfcgq",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fallible-iterator@0.3.0": "0ja6l56yka5vn4y4pk6hn88z0bpny7a8k1919aqjzp0j1yhy9k1a",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fallible-streaming-iterator@0.1.9": "0nj6j26p71bjy8h42x6jahx1hn0ng6mc2miwpgwnp8vnwqf4jq3k",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fastrand@2.3.0": "1ghiahsw1jd68df895cy5h3gzwk30hndidn3b682zmshpgmrx41p",
|
"registry+https://github.com/rust-lang/crates.io-index#fastrand@2.3.0": "1ghiahsw1jd68df895cy5h3gzwk30hndidn3b682zmshpgmrx41p",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fdeflate@0.3.7": "130ga18vyxbb5idbgi07njymdaavvk6j08yh1dfarm294ssm6s0y",
|
"registry+https://github.com/rust-lang/crates.io-index#fdeflate@0.3.7": "130ga18vyxbb5idbgi07njymdaavvk6j08yh1dfarm294ssm6s0y",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#field-offset@0.3.6": "0zq5sssaa2ckmcmxxbly8qgz3sxpb8g1lwv90sdh1z74qif2gqiq",
|
"registry+https://github.com/rust-lang/crates.io-index#field-offset@0.3.6": "0zq5sssaa2ckmcmxxbly8qgz3sxpb8g1lwv90sdh1z74qif2gqiq",
|
||||||
@ -146,6 +134,7 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.11.2": "0a7w8w0rg47nmcinnfzv443lcyb8mplwc251p1jyr5xj2yh6wzv6",
|
"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.11.2": "0a7w8w0rg47nmcinnfzv443lcyb8mplwc251p1jyr5xj2yh6wzv6",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7": "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45",
|
"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7": "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.2.15": "1mzlnrb3dgyd1fb84gvw10pyr8wdqdl4ry4sr64i1s8an66pqmn4",
|
"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.2.15": "1mzlnrb3dgyd1fb84gvw10pyr8wdqdl4ry4sr64i1s8an66pqmn4",
|
||||||
|
"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.1": "1y154yzby383p63ndw6zpfm0fz3vf6c0zdwc7df6vkl150wrr923",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gif@0.11.4": "01hbw3isapzpzff8l6aw55jnaqx2bcscrbwyf3rglkbbfp397p9y",
|
"registry+https://github.com/rust-lang/crates.io-index#gif@0.11.4": "01hbw3isapzpzff8l6aw55jnaqx2bcscrbwyf3rglkbbfp397p9y",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gif@0.13.1": "1whrkvdg26gp1r7f95c6800y6ijqw5y0z8rgj6xihpi136dxdciz",
|
"registry+https://github.com/rust-lang/crates.io-index#gif@0.13.1": "1whrkvdg26gp1r7f95c6800y6ijqw5y0z8rgj6xihpi136dxdciz",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gimli@0.31.1": "0gvqc0ramx8szv76jhfd4dms0zyamvlg4whhiz11j34hh3dqxqh7",
|
"registry+https://github.com/rust-lang/crates.io-index#gimli@0.31.1": "0gvqc0ramx8szv76jhfd4dms0zyamvlg4whhiz11j34hh3dqxqh7",
|
||||||
@ -170,10 +159,8 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#gtk4@0.7.3": "0hh8nzglmz94v1m1h6vy8z12m6fr7ia467ry0md5fa4p7sm53sss",
|
"registry+https://github.com/rust-lang/crates.io-index#gtk4@0.7.3": "0hh8nzglmz94v1m1h6vy8z12m6fr7ia467ry0md5fa4p7sm53sss",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#h2@0.3.26": "1s7msnfv7xprzs6xzfj5sg6p8bjcdpcqcmjjbkd345cyi1x55zl1",
|
"registry+https://github.com/rust-lang/crates.io-index#h2@0.3.26": "1s7msnfv7xprzs6xzfj5sg6p8bjcdpcqcmjjbkd345cyi1x55zl1",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#half@2.4.1": "123q4zzw1x4309961i69igzd1wb7pj04aaii3kwasrz3599qrl3d",
|
"registry+https://github.com/rust-lang/crates.io-index#half@2.4.1": "123q4zzw1x4309961i69igzd1wb7pj04aaii3kwasrz3599qrl3d",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.14.5": "1wa1vy1xs3mp11bn3z9dv0jricgr6a2j0zkf1g19yz3vw4il89z5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.2": "12dj0yfn59p3kh3679ac0w1fagvzf4z2zp87a13gbbqbzw0185dz",
|
"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.15.2": "12dj0yfn59p3kh3679ac0w1fagvzf4z2zp87a13gbbqbzw0185dz",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hashlink@0.10.0": "1h8lzvnl9qxi3zyagivzz2p1hp6shgddfmccyf6jv7s1cdicz0kk",
|
"registry+https://github.com/rust-lang/crates.io-index#hashlink@0.10.0": "1h8lzvnl9qxi3zyagivzz2p1hp6shgddfmccyf6jv7s1cdicz0kk",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hashlink@0.9.1": "1byq4nyrflm5s6wdx5qwp96l1qbp2d0nljvrr5yqrsfy51qzz93b",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#headers-core@0.2.0": "0ab469xfpd411mc3dhmjhmzrhqikzyj8a17jn5bkj9zfpy0n9xp7",
|
"registry+https://github.com/rust-lang/crates.io-index#headers-core@0.2.0": "0ab469xfpd411mc3dhmjhmzrhqikzyj8a17jn5bkj9zfpy0n9xp7",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#headers@0.3.9": "0w62gnwh2p1lml0zqdkrx9dp438881nhz32zrzdy61qa0a9kns06",
|
"registry+https://github.com/rust-lang/crates.io-index#headers@0.3.9": "0w62gnwh2p1lml0zqdkrx9dp438881nhz32zrzdy61qa0a9kns06",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#heck@0.4.1": "1a7mqsnycv5z4z5vnv1k34548jzmc0ajic7c1j8jsaspnhw5ql4m",
|
"registry+https://github.com/rust-lang/crates.io-index#heck@0.4.1": "1a7mqsnycv5z4z5vnv1k34548jzmc0ajic7c1j8jsaspnhw5ql4m",
|
||||||
@ -185,19 +172,15 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#hkdf@0.12.4": "1xxxzcarz151p1b858yn5skmhyrvn8fs4ivx5km3i1kjmnr8wpvv",
|
"registry+https://github.com/rust-lang/crates.io-index#hkdf@0.12.4": "1xxxzcarz151p1b858yn5skmhyrvn8fs4ivx5km3i1kjmnr8wpvv",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hmac@0.12.1": "0pmbr069sfg76z7wsssfk5ddcqd9ncp79fyz6zcm6yn115yc6jbc",
|
"registry+https://github.com/rust-lang/crates.io-index#hmac@0.12.1": "0pmbr069sfg76z7wsssfk5ddcqd9ncp79fyz6zcm6yn115yc6jbc",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#home@0.5.11": "1kxb4k87a9sayr8jipr7nq9wpgmjk4hk4047hmf9kc24692k75aq",
|
"registry+https://github.com/rust-lang/crates.io-index#home@0.5.11": "1kxb4k87a9sayr8jipr7nq9wpgmjk4hk4047hmf9kc24692k75aq",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#http-body-util@0.1.2": "0kslwazg4400qnc2azkrgqqci0fppv12waicnsy5d8hncvbjjd3r",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#http-body@0.4.6": "1lmyjfk6bqk6k9gkn1dxq770sb78pqbqshga241hr5p995bb5skw",
|
"registry+https://github.com/rust-lang/crates.io-index#http-body@0.4.6": "1lmyjfk6bqk6k9gkn1dxq770sb78pqbqshga241hr5p995bb5skw",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#http-body@1.0.1": "111ir5k2b9ihz5nr9cz7cwm7fnydca7dx4hc7vr16scfzghxrzhy",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#http@0.2.12": "1w81s4bcbmcj9bjp7mllm8jlz6b31wzvirz8bgpzbqkpwmbvn730",
|
"registry+https://github.com/rust-lang/crates.io-index#http@0.2.12": "1w81s4bcbmcj9bjp7mllm8jlz6b31wzvirz8bgpzbqkpwmbvn730",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#http@1.2.0": "1skglzdf98j5nzxlii540n11is0w4l80mi5sm3xrj716asps4v7i",
|
"registry+https://github.com/rust-lang/crates.io-index#http@1.2.0": "1skglzdf98j5nzxlii540n11is0w4l80mi5sm3xrj716asps4v7i",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#httparse@1.9.5": "0ip9v8m9lvgvq1lznl31wvn0ch1v254na7lhid9p29yx9rbx6wbx",
|
"registry+https://github.com/rust-lang/crates.io-index#httparse@1.9.5": "0ip9v8m9lvgvq1lznl31wvn0ch1v254na7lhid9p29yx9rbx6wbx",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3": "1aa9rd2sac0zhjqh24c9xvir96g188zldkx0hr6dnnlx5904cfyz",
|
"registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3": "1aa9rd2sac0zhjqh24c9xvir96g188zldkx0hr6dnnlx5904cfyz",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#humantime@2.1.0": "1r55pfkkf5v0ji1x6izrjwdq9v6sc7bv99xj6srywcar37xmnfls",
|
"registry+https://github.com/rust-lang/crates.io-index#humantime@2.1.0": "1r55pfkkf5v0ji1x6izrjwdq9v6sc7bv99xj6srywcar37xmnfls",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hyper-tls@0.5.0": "01crgy13102iagakf6q4mb75dprzr7ps1gj0l5hxm1cvm7gks66n",
|
"registry+https://github.com/rust-lang/crates.io-index#hyper-tls@0.5.0": "01crgy13102iagakf6q4mb75dprzr7ps1gj0l5hxm1cvm7gks66n",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hyper-util@0.1.10": "1d1iwrkysjhq63pg54zk3vfby1j7zmxzm9zzyfr4lwvp0szcybfz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hyper@0.10.16": "0wwjh9p3mzvg3fss2lqz5r7ddcgl1fh9w6my2j69d6k0lbcm41ha",
|
"registry+https://github.com/rust-lang/crates.io-index#hyper@0.10.16": "0wwjh9p3mzvg3fss2lqz5r7ddcgl1fh9w6my2j69d6k0lbcm41ha",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hyper@0.14.32": "1rvcb0smz8q1i0y6p7rwxr02x5sclfg2hhxf3g0774zczn0cgps1",
|
"registry+https://github.com/rust-lang/crates.io-index#hyper@0.14.32": "1rvcb0smz8q1i0y6p7rwxr02x5sclfg2hhxf3g0774zczn0cgps1",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hyper@1.5.2": "1q7akfb443yrjzkmnnbp2vs8zi15hgbk466rr4y144v4ppabhvr5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#iana-time-zone-haiku@0.1.2": "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k",
|
"registry+https://github.com/rust-lang/crates.io-index#iana-time-zone-haiku@0.1.2": "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#iana-time-zone@0.1.61": "085jjsls330yj1fnwykfzmb2f10zp6l7w4fhq81ng81574ghhpi3",
|
"registry+https://github.com/rust-lang/crates.io-index#iana-time-zone@0.1.61": "085jjsls330yj1fnwykfzmb2f10zp6l7w4fhq81ng81574ghhpi3",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#icu_collections@1.5.0": "09j5kskirl59mvqc8kabhy7005yyy7dp88jw9f6f3gkf419a8byv",
|
"registry+https://github.com/rust-lang/crates.io-index#icu_collections@1.5.0": "09j5kskirl59mvqc8kabhy7005yyy7dp88jw9f6f3gkf419a8byv",
|
||||||
@ -215,8 +198,6 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.0": "0wggnkiivaj5lw0g0384ql2d7zk4ppkn3b1ry4n0ncjpr7qivjns",
|
"registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.0": "0wggnkiivaj5lw0g0384ql2d7zk4ppkn3b1ry4n0ncjpr7qivjns",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#image@0.23.14": "18gn2f7xp30pf9aqka877knlq308khxqiwjvsccvzaa4f9zcpzr4",
|
"registry+https://github.com/rust-lang/crates.io-index#image@0.23.14": "18gn2f7xp30pf9aqka877knlq308khxqiwjvsccvzaa4f9zcpzr4",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#image@0.24.9": "17gnr6ifnpzvhjf6dwbl9hki8x6bji5mwcqp0048x1jm5yfi742n",
|
"registry+https://github.com/rust-lang/crates.io-index#image@0.24.9": "17gnr6ifnpzvhjf6dwbl9hki8x6bji5mwcqp0048x1jm5yfi742n",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#include_dir@0.7.4": "1pfh3g45z88kwq93skng0n6g3r7zkhq9ldqs9y8rvr7i11s12gcj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#include_dir_macros@0.7.4": "0x8smnf6knd86g69p19z5lpfsaqp8w0nx14kdpkz1m8bxnkqbavw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#indent_write@2.2.0": "1hqjp80argdskrhd66g9sh542yxy8qi77j6rc69qd0l7l52rdzhc",
|
"registry+https://github.com/rust-lang/crates.io-index#indent_write@2.2.0": "1hqjp80argdskrhd66g9sh542yxy8qi77j6rc69qd0l7l52rdzhc",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.0": "07s7jmdymvd0rm4yswp0j3napx57hkjm9gs9n55lvs2g78vj5y32",
|
"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.7.0": "07s7jmdymvd0rm4yswp0j3napx57hkjm9gs9n55lvs2g78vj5y32",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#intl-memoizer@0.5.2": "1nkvql7c7b76axv4g68di1p2m9bnxq1cbn6mlqcawf72zhhf08py",
|
"registry+https://github.com/rust-lang/crates.io-index#intl-memoizer@0.5.2": "1nkvql7c7b76axv4g68di1p2m9bnxq1cbn6mlqcawf72zhhf08py",
|
||||||
@ -251,7 +232,6 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#log@0.4.25": "17ydv5zhfv1zzygy458bmg3f3jx1vfziv9d74817w76yhfqgbjq4",
|
"registry+https://github.com/rust-lang/crates.io-index#log@0.4.25": "17ydv5zhfv1zzygy458bmg3f3jx1vfziv9d74817w76yhfqgbjq4",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#logger@0.4.0": "14xlxvkspcfnspjil0xi63qj5cybxn1hjmr5gq8m4v1g9k5p54bc",
|
"registry+https://github.com/rust-lang/crates.io-index#logger@0.4.0": "14xlxvkspcfnspjil0xi63qj5cybxn1hjmr5gq8m4v1g9k5p54bc",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#matches@0.1.10": "1994402fq4viys7pjhzisj4wcw894l53g798kkm2y74laxk0jci5",
|
"registry+https://github.com/rust-lang/crates.io-index#matches@0.1.10": "1994402fq4viys7pjhzisj4wcw894l53g798kkm2y74laxk0jci5",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#matchit@0.7.3": "156bgdmmlv4crib31qhgg49nsjk88dxkdqp80ha2pk2rk6n6ax0f",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#md-5@0.10.6": "1kvq5rnpm4fzwmyv5nmnxygdhhb2369888a06gdc9pxyrzh7x7nq",
|
"registry+https://github.com/rust-lang/crates.io-index#md-5@0.10.6": "1kvq5rnpm4fzwmyv5nmnxygdhhb2369888a06gdc9pxyrzh7x7nq",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#memchr@2.7.4": "18z32bhxrax0fnjikv475z7ii718hq457qwmaryixfxsl2qrmjkq",
|
"registry+https://github.com/rust-lang/crates.io-index#memchr@2.7.4": "18z32bhxrax0fnjikv475z7ii718hq457qwmaryixfxsl2qrmjkq",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#memoffset@0.9.1": "12i17wh9a9plx869g7j4whf62xw68k5zd4k0k5nh6ys5mszid028",
|
"registry+https://github.com/rust-lang/crates.io-index#memoffset@0.9.1": "12i17wh9a9plx869g7j4whf62xw68k5zd4k0k5nh6ys5mszid028",
|
||||||
@ -317,10 +297,9 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#polling@3.7.4": "0bs4nhwfwsvlzlhah2gbhj3aa9ynvchv2g350wapswh26a65c156",
|
"registry+https://github.com/rust-lang/crates.io-index#polling@3.7.4": "0bs4nhwfwsvlzlhah2gbhj3aa9ynvchv2g350wapswh26a65c156",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0": "14ckj2xdpkhv3h6l5sdmb9f1d57z8hbfpdldjc2vl5givq2y77j3",
|
"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0": "14ckj2xdpkhv3h6l5sdmb9f1d57z8hbfpdldjc2vl5givq2y77j3",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#ppv-lite86@0.2.20": "017ax9ssdnpww7nrl1hvqh2lzncpv04nnsibmnw9nxjnaqlpp5bp",
|
"registry+https://github.com/rust-lang/crates.io-index#ppv-lite86@0.2.20": "017ax9ssdnpww7nrl1hvqh2lzncpv04nnsibmnw9nxjnaqlpp5bp",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pretty_assertions@1.4.1": "0v8iq35ca4rw3rza5is3wjxwsf88303ivys07anc5yviybi31q9s",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pretty_env_logger@0.5.0": "076w9dnvcpx6d3mdbkqad8nwnsynb7c8haxmscyrz7g3vga28mw6",
|
"registry+https://github.com/rust-lang/crates.io-index#pretty_env_logger@0.5.0": "076w9dnvcpx6d3mdbkqad8nwnsynb7c8haxmscyrz7g3vga28mw6",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-crate@1.3.1": "069r1k56bvgk0f58dm5swlssfcp79im230affwk6d9ck20g04k3z",
|
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-crate@1.3.1": "069r1k56bvgk0f58dm5swlssfcp79im230affwk6d9ck20g04k3z",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-crate@2.0.2": "092x5acqnic14cw6vacqap5kgknq3jn4c6jij9zi6j85839jc3xh",
|
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-crate@2.0.0": "1s23imns07vmacn2xjd5hv2h6rr94iqq3fd2frwa6i4h2nk6d0vy",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-error-attr@1.0.4": "0sgq6m5jfmasmwwy8x4mjygx5l7kp8s4j60bv25ckv2j1qc41gm1",
|
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-error-attr@1.0.4": "0sgq6m5jfmasmwwy8x4mjygx5l7kp8s4j60bv25ckv2j1qc41gm1",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-error@1.0.4": "1373bhxaf0pagd8zkyd03kkx6bchzf6g0dkwrwzsnal9z47lj9fs",
|
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-error@1.0.4": "1373bhxaf0pagd8zkyd03kkx6bchzf6g0dkwrwzsnal9z47lj9fs",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.93": "169dw9wch753if1mgyi2nfl1il77gslvh6y2q46qplprwml6m530",
|
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.93": "169dw9wch753if1mgyi2nfl1il77gslvh6y2q46qplprwml6m530",
|
||||||
@ -353,11 +332,7 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#regex@1.11.1": "148i41mzbx8bmq32hsj1q4karkzzx5m60qza6gdw4pdc9qdyyi5m",
|
"registry+https://github.com/rust-lang/crates.io-index#regex@1.11.1": "148i41mzbx8bmq32hsj1q4karkzzx5m60qza6gdw4pdc9qdyyi5m",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#remove_dir_all@0.5.3": "1rzqbsgkmr053bxxl04vmvsd1njyz0nxvly97aip6aa2cmb15k9s",
|
"registry+https://github.com/rust-lang/crates.io-index#remove_dir_all@0.5.3": "1rzqbsgkmr053bxxl04vmvsd1njyz0nxvly97aip6aa2cmb15k9s",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#reqwest@0.11.27": "0qjary4hpplpgdi62d2m0xvbn6lnzckwffm0rgkm2x51023m6ryx",
|
"registry+https://github.com/rust-lang/crates.io-index#reqwest@0.11.27": "0qjary4hpplpgdi62d2m0xvbn6lnzckwffm0rgkm2x51023m6ryx",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#reserve-port@2.0.1": "10x21rdb1hjzp6n5flbbw3hfd7brmirckz1q0zsf3a7s5d516f4q",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rsa@0.9.7": "06amqm85raq26v6zg00fbf93lbj3kx559n2lpxc3wrvbbiy5vis7",
|
"registry+https://github.com/rust-lang/crates.io-index#rsa@0.9.7": "06amqm85raq26v6zg00fbf93lbj3kx559n2lpxc3wrvbbiy5vis7",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rusqlite@0.32.1": "0vlx040bppl414pbjgbp7qr4jdxwszi9krx0m63zzf2f2whvflvp",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rusqlite_migration@1.3.1": "076dm65g0sngzrb93r07va4l5zl3gjx9gq5mlsh21p7p0bl44fwj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rust-multipart-rfc7578_2@0.6.1": "0mwd3i2mk91n6diaxnkw28vyjbifhrm5ls73pcpfzz8a1i0lidq3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rustc-demangle@0.1.24": "07zysaafgrkzy2rjgwqdj2a8qdpsm6zv6f5pgpk9x0lm40z9b6vi",
|
"registry+https://github.com/rust-lang/crates.io-index#rustc-demangle@0.1.24": "07zysaafgrkzy2rjgwqdj2a8qdpsm6zv6f5pgpk9x0lm40z9b6vi",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rustc-hash@1.1.0": "1qkc5khrmv5pqi5l5ca9p5nl5hs742cagrndhbrlk3dhlrx3zm08",
|
"registry+https://github.com/rust-lang/crates.io-index#rustc-hash@1.1.0": "1qkc5khrmv5pqi5l5ca9p5nl5hs742cagrndhbrlk3dhlrx3zm08",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rustc_version@0.4.1": "14lvdsmr5si5qbqzrajgb6vfn69k0sfygrvfvr2mps26xwi3mjyg",
|
"registry+https://github.com/rust-lang/crates.io-index#rustc_version@0.4.1": "14lvdsmr5si5qbqzrajgb6vfn69k0sfygrvfvr2mps26xwi3mjyg",
|
||||||
@ -377,10 +352,9 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#self_cell@1.1.0": "1gmxk5bvnnimcif7v1jk8ai2azfvh9djki545nd86vsnphjgrzf2",
|
"registry+https://github.com/rust-lang/crates.io-index#self_cell@1.1.0": "1gmxk5bvnnimcif7v1jk8ai2azfvh9djki545nd86vsnphjgrzf2",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#semver@1.0.24": "1fmvjjkd3f64y5fqr1nakkq371mnwzv09fbz5mbmdxril63ypdiw",
|
"registry+https://github.com/rust-lang/crates.io-index#semver@1.0.24": "1fmvjjkd3f64y5fqr1nakkq371mnwzv09fbz5mbmdxril63ypdiw",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde@0.9.15": "1bsla8l5xr9pp5sirkal6mngxcq6q961km88jvf339j5ff8j7dil",
|
"registry+https://github.com/rust-lang/crates.io-index#serde@0.9.15": "1bsla8l5xr9pp5sirkal6mngxcq6q961km88jvf339j5ff8j7dil",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.217": "0w2ck1p1ajmrv1cf51qf7igjn2nc51r0izzc00fzmmhkvxjl5z02",
|
"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.218": "0q6z4bnrwagnms0bds4886711l6mc68s979i49zd3xnvkg8wkpz8",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.217": "180r3rj5gi5s1m23q66cr5wlfgc5jrs6n1mdmql2njnhk37zg6ss",
|
"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.218": "0azqd74xbpb1v5vf6w1fdbgmwp39ljjfj25cib5rgrzlj7hh75gh",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.136": "1lipcjhh1zazh283i4wsl4l14knh81q2rlkwmag8v8s2rwihqsik",
|
"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.139": "19kj3irpa22a7djz1jaf4wambzh7psiqa6zyafqnb76crhx6ry24",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_path_to_error@0.1.16": "19hlz2359l37ifirskpcds7sxg0gzpqvfilibs7whdys0128i6dg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_spanned@0.6.8": "1q89g70azwi4ybilz5jb8prfpa575165lmrffd49vmcf76qpqq47",
|
"registry+https://github.com/rust-lang/crates.io-index#serde_spanned@0.6.8": "1q89g70azwi4ybilz5jb8prfpa575165lmrffd49vmcf76qpqq47",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_urlencoded@0.7.1": "1zgklbdaysj3230xivihs30qi5vkhigg323a9m62k8jwf4a1qjfk",
|
"registry+https://github.com/rust-lang/crates.io-index#serde_urlencoded@0.7.1": "1zgklbdaysj3230xivihs30qi5vkhigg323a9m62k8jwf4a1qjfk",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_yml@0.0.12": "1p8xwz4znd6fj962y22fdvvv16gb8c0hx4iv5hjplngiidcdvqjr",
|
"registry+https://github.com/rust-lang/crates.io-index#serde_yml@0.0.12": "1p8xwz4znd6fj962y22fdvvv16gb8c0hx4iv5hjplngiidcdvqjr",
|
||||||
@ -412,14 +386,13 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#syn@1.0.109": "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj",
|
"registry+https://github.com/rust-lang/crates.io-index#syn@1.0.109": "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.96": "102wk3cgawimi3i0q3r3xw3i858zkyingg6y7gsxfy733amsvl6m",
|
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.96": "102wk3cgawimi3i0q3r3xw3i858zkyingg6y7gsxfy733amsvl6m",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@0.1.2": "0q01lyj0gr9a93n10nxsn8lwbzq97jqd6b768x17c8f7v7gccir0",
|
"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@0.1.2": "0q01lyj0gr9a93n10nxsn8lwbzq97jqd6b768x17c8f7v7gccir0",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sync_wrapper@1.0.2": "0qvjyasd6w18mjg5xlaq5jgy84jsjfsvmnn12c13gypxbv75dwhb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.1": "0wc9f002ia2zqcbj0q2id5x6n7g1zjqba7qkg2mr0qvvmdk7dby8",
|
"registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.1": "0wc9f002ia2zqcbj0q2id5x6n7g1zjqba7qkg2mr0qvvmdk7dby8",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#system-configuration-sys@0.5.0": "1jckxvdr37bay3i9v52izgy52dg690x5xfg3hd394sv2xf4b2px7",
|
"registry+https://github.com/rust-lang/crates.io-index#system-configuration-sys@0.5.0": "1jckxvdr37bay3i9v52izgy52dg690x5xfg3hd394sv2xf4b2px7",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#system-configuration@0.5.1": "1rz0r30xn7fiyqay2dvzfy56cvaa3km74hnbz2d72p97bkf3lfms",
|
"registry+https://github.com/rust-lang/crates.io-index#system-configuration@0.5.1": "1rz0r30xn7fiyqay2dvzfy56cvaa3km74hnbz2d72p97bkf3lfms",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#system-deps@6.2.2": "0j93ryw031n3h8b0nfpj5xwh3ify636xmv8kxianvlyyipmkbrd3",
|
"registry+https://github.com/rust-lang/crates.io-index#system-deps@6.2.2": "0j93ryw031n3h8b0nfpj5xwh3ify636xmv8kxianvlyyipmkbrd3",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.12.16": "1cg3bnx1gdkdr5hac1hzxy64fhw4g7dqkd0n3dxy5lfngpr1mi31",
|
"registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.12.16": "1cg3bnx1gdkdr5hac1hzxy64fhw4g7dqkd0n3dxy5lfngpr1mi31",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tempdir@0.3.7": "1n5n86zxpgd85y0mswrp5cfdisizq2rv3la906g6ipyc03xvbwhm",
|
"registry+https://github.com/rust-lang/crates.io-index#tempdir@0.3.7": "1n5n86zxpgd85y0mswrp5cfdisizq2rv3la906g6ipyc03xvbwhm",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tempfile@3.15.0": "016pmkbwn3shas44gcwq1kc9lajalb90qafhiip5fvv8h6f5b2ls",
|
"registry+https://github.com/rust-lang/crates.io-index#tempfile@3.17.1": "0c52ggq5vy5mzgk5ly36cgzs1cig3cv6r1jarijmzxgkn6na1r92",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1": "0mappjh3fj3p2nmrg4y7qv94rchwi9mzmgmfflr8p2awdj7lyy86",
|
"registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.1": "0mappjh3fj3p2nmrg4y7qv94rchwi9mzmgmfflr8p2awdj7lyy86",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#thiserror-impl@1.0.69": "1h84fmn2nai41cxbhk6pqf46bxqq1b344v8yz089w1chzi76rvjg",
|
"registry+https://github.com/rust-lang/crates.io-index#thiserror-impl@1.0.69": "1h84fmn2nai41cxbhk6pqf46bxqq1b344v8yz089w1chzi76rvjg",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#thiserror-impl@2.0.11": "1hkkn7p2y4cxbffcrprybkj0qy1rl1r6waxmxqvr764axaxc3br6",
|
"registry+https://github.com/rust-lang/crates.io-index#thiserror-impl@2.0.11": "1hkkn7p2y4cxbffcrprybkj0qy1rl1r6waxmxqvr764axaxc3br6",
|
||||||
@ -440,14 +413,12 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#tokio-tungstenite@0.21.0": "0f5wj0crsx74rlll97lhw0wk6y12nhdnqvmnjx002hjn08fmcfy8",
|
"registry+https://github.com/rust-lang/crates.io-index#tokio-tungstenite@0.21.0": "0f5wj0crsx74rlll97lhw0wk6y12nhdnqvmnjx002hjn08fmcfy8",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tokio-util@0.7.13": "0y0h10a52c7hrldmr3410bp7j3fadq0jn9nf7awddgd2an6smz6p",
|
"registry+https://github.com/rust-lang/crates.io-index#tokio-util@0.7.13": "0y0h10a52c7hrldmr3410bp7j3fadq0jn9nf7awddgd2an6smz6p",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tokio@1.43.0": "17pdm49ihlhfw3rpxix3kdh2ppl1yv7nwp1kxazi5r1xz97zlq9x",
|
"registry+https://github.com/rust-lang/crates.io-index#tokio@1.43.0": "17pdm49ihlhfw3rpxix3kdh2ppl1yv7nwp1kxazi5r1xz97zlq9x",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#toml@0.8.2": "0g9ysjaqvm2mv8q85xpqfn7hi710hj24sd56k49wyddvvyq8lp8q",
|
"registry+https://github.com/rust-lang/crates.io-index#toml@0.8.20": "0j012b37iz1mihksr6a928s6dzszxvblzg3l5wxp7azzsv6sb1yd",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#toml_datetime@0.6.3": "0jsy7v8bdvmzsci6imj8fzgd255fmy5fzp6zsri14yrry7i77nkw",
|
"registry+https://github.com/rust-lang/crates.io-index#toml_datetime@0.6.8": "0hgv7v9g35d7y9r2afic58jvlwnf73vgd1mz2k8gihlgrf73bmqd",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#toml_edit@0.19.15": "08bl7rp5g6jwmfpad9s8jpw8wjrciadpnbaswgywpr9hv9qbfnqv",
|
"registry+https://github.com/rust-lang/crates.io-index#toml_edit@0.19.15": "08bl7rp5g6jwmfpad9s8jpw8wjrciadpnbaswgywpr9hv9qbfnqv",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#toml_edit@0.20.2": "0f7k5svmxw98fhi28jpcyv7ldr2s3c867pjbji65bdxjpd44svir",
|
"registry+https://github.com/rust-lang/crates.io-index#toml_edit@0.20.2": "0f7k5svmxw98fhi28jpcyv7ldr2s3c867pjbji65bdxjpd44svir",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tower-http@0.6.2": "15wnvhl6cpir9125s73bqjzjsvfb0fmndmsimnl2ddnlhfvs6gs0",
|
"registry+https://github.com/rust-lang/crates.io-index#toml_edit@0.22.24": "0x0lgp70x5cl9nla03xqs5vwwwlrwmd0djkdrp3h3lpdymgpkd0p",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tower-layer@0.3.3": "03kq92fdzxin51w8iqix06dcfgydyvx7yr6izjq0p626v9n2l70j",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3": "1hzfkvkci33ra94xjx64vv3pp0sq346w06fpkcdwjcid7zhvdycd",
|
"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.3": "1hzfkvkci33ra94xjx64vv3pp0sq346w06fpkcdwjcid7zhvdycd",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tower@0.5.2": "1ybmd59nm4abl9bsvy6rx31m4zvzp5rja2slzpn712y9b68ssffh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tracing-attributes@0.1.28": "0v92l9cxs42rdm4m5hsa8z7ln1xsiw1zc2iil8c6k7lzq0jf2nir",
|
"registry+https://github.com/rust-lang/crates.io-index#tracing-attributes@0.1.28": "0v92l9cxs42rdm4m5hsa8z7ln1xsiw1zc2iil8c6k7lzq0jf2nir",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tracing-core@0.1.33": "170gc7cxyjx824r9kr17zc9gvzx89ypqfdzq259pr56gg5bwjwp6",
|
"registry+https://github.com/rust-lang/crates.io-index#tracing-core@0.1.33": "170gc7cxyjx824r9kr17zc9gvzx89ypqfdzq259pr56gg5bwjwp6",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tracing@0.1.41": "1l5xrzyjfyayrwhvhldfnwdyligi1mpqm8mzbi2m1d6y6p2hlkkq",
|
"registry+https://github.com/rust-lang/crates.io-index#tracing@0.1.41": "1l5xrzyjfyayrwhvhldfnwdyligi1mpqm8mzbi2m1d6y6p2hlkkq",
|
||||||
@ -474,7 +445,6 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#unsafe-any@0.4.2": "0zwwphsqkw5qaiqmjwngnfpv9ym85qcsyj7adip9qplzjzbn00zk",
|
"registry+https://github.com/rust-lang/crates.io-index#unsafe-any@0.4.2": "0zwwphsqkw5qaiqmjwngnfpv9ym85qcsyj7adip9qplzjzbn00zk",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#url@1.7.2": "0nim1c90mxpi9wgdw2xh8dqd72vlklwlzam436akcrhjac6pqknx",
|
"registry+https://github.com/rust-lang/crates.io-index#url@1.7.2": "0nim1c90mxpi9wgdw2xh8dqd72vlklwlzam436akcrhjac6pqknx",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#url@2.5.4": "0q6sgznyy2n4l5lm16zahkisvc9nip9aa5q1pps7656xra3bdy1j",
|
"registry+https://github.com/rust-lang/crates.io-index#url@2.5.4": "0q6sgznyy2n4l5lm16zahkisvc9nip9aa5q1pps7656xra3bdy1j",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#urlencoding@2.1.3": "1nj99jp37k47n0hvaz5fvz7z6jd0sb4ppvfy3nphr1zbnyixpy6s",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#utf-8@0.7.6": "1a9ns3fvgird0snjkd3wbdhwd3zdpc2h5gpyybrfr6ra5pkqxk09",
|
"registry+https://github.com/rust-lang/crates.io-index#utf-8@0.7.6": "1a9ns3fvgird0snjkd3wbdhwd3zdpc2h5gpyybrfr6ra5pkqxk09",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#utf16_iter@1.0.5": "0ik2krdr73hfgsdzw0218fn35fa09dg2hvbi1xp3bmdfrp9js8y8",
|
"registry+https://github.com/rust-lang/crates.io-index#utf16_iter@1.0.5": "0ik2krdr73hfgsdzw0218fn35fa09dg2hvbi1xp3bmdfrp9js8y8",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4": "1gmna9flnj8dbyd8ba17zigrp9c4c3zclngf5lnb5yvz1ri41hdn",
|
"registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4": "1gmna9flnj8dbyd8ba17zigrp9c4c3zclngf5lnb5yvz1ri41hdn",
|
||||||
@ -492,6 +462,7 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#warp@0.3.7": "07137zd13lchy5hxpspd0hs6sl19b0fv2zc1chf02nwnzw1d4y23",
|
"registry+https://github.com/rust-lang/crates.io-index#warp@0.3.7": "07137zd13lchy5hxpspd0hs6sl19b0fv2zc1chf02nwnzw1d4y23",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasi@0.10.0+wasi-snapshot-preview1": "07y3l8mzfzzz4cj09c8y90yak4hpsi9g7pllyzpr6xvwrabka50s",
|
"registry+https://github.com/rust-lang/crates.io-index#wasi@0.10.0+wasi-snapshot-preview1": "07y3l8mzfzzz4cj09c8y90yak4hpsi9g7pllyzpr6xvwrabka50s",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.0+wasi-snapshot-preview1": "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw",
|
"registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.0+wasi-snapshot-preview1": "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw",
|
||||||
|
"registry+https://github.com/rust-lang/crates.io-index#wasi@0.13.3+wasi-0.2.2": "1lnapbvdcvi3kc749wzqvwrpd483win2kicn1faa4dja38p6v096",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasite@0.1.0": "0nw5h9nmcl4fyf4j5d4mfdjfgvwi1cakpi349wc4zrr59wxxinmq",
|
"registry+https://github.com/rust-lang/crates.io-index#wasite@0.1.0": "0nw5h9nmcl4fyf4j5d4mfdjfgvwi1cakpi349wc4zrr59wxxinmq",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-backend@0.2.100": "1ihbf1hq3y81c4md9lyh6lcwbx6a5j0fw4fygd423g62lm8hc2ig",
|
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-backend@0.2.100": "1ihbf1hq3y81c4md9lyh6lcwbx6a5j0fw4fygd423g62lm8hc2ig",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-futures@0.4.50": "0q8ymi6i9r3vxly551dhxcyai7nc491mspj0j1wbafxwq074fpam",
|
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-futures@0.4.50": "0q8ymi6i9r3vxly551dhxcyai7nc491mspj0j1wbafxwq074fpam",
|
||||||
@ -528,11 +499,13 @@
|
|||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.48.5": "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d",
|
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.48.5": "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.52.6": "1v7rb5cibyzx8vak29pdrk8nx9hycsjs4w0jgms08qk49jl6v7sq",
|
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.52.6": "1v7rb5cibyzx8vak29pdrk8nx9hycsjs4w0jgms08qk49jl6v7sq",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#winnow@0.5.40": "0xk8maai7gyxda673mmw3pj1hdizy5fpi7287vaywykkk19sk4zm",
|
"registry+https://github.com/rust-lang/crates.io-index#winnow@0.5.40": "0xk8maai7gyxda673mmw3pj1hdizy5fpi7287vaywykkk19sk4zm",
|
||||||
|
"registry+https://github.com/rust-lang/crates.io-index#winnow@0.7.3": "1c9bmhpdwbdmll6b4l6skabz0296dchnmnxw84hh2y3ggyllwzqf",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#winreg@0.50.0": "1cddmp929k882mdh6i9f2as848f13qqna6czwsqzkh1pqnr5fkjj",
|
"registry+https://github.com/rust-lang/crates.io-index#winreg@0.50.0": "1cddmp929k882mdh6i9f2as848f13qqna6czwsqzkh1pqnr5fkjj",
|
||||||
|
"registry+https://github.com/rust-lang/crates.io-index#wit-bindgen-rt@0.33.0": "0g4lwfp9x6a2i1hgjn8k14nr4fsnpd5izxhc75zpi2s5cvcg6s1j",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#write16@1.0.0": "0dnryvrrbrnl7vvf5vb1zkmwldhjkf2n5znliviam7bm4900z2fi",
|
"registry+https://github.com/rust-lang/crates.io-index#write16@1.0.0": "0dnryvrrbrnl7vvf5vb1zkmwldhjkf2n5znliviam7bm4900z2fi",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#writeable@0.5.5": "0lawr6y0bwqfyayf3z8zmqlhpnzhdx0ahs54isacbhyjwa7g778y",
|
"registry+https://github.com/rust-lang/crates.io-index#writeable@0.5.5": "0lawr6y0bwqfyayf3z8zmqlhpnzhdx0ahs54isacbhyjwa7g778y",
|
||||||
|
"registry+https://github.com/rust-lang/crates.io-index#xml-rs@0.8.25": "1i73ajf6scni5bi1a51r19xykgrambdx5fkks0fyg5jqqbml1ff5",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#yansi-term@0.1.2": "1w8vjlvxba6yvidqdvxddx3crl6z66h39qxj8xi6aqayw2nk0p7y",
|
"registry+https://github.com/rust-lang/crates.io-index#yansi-term@0.1.2": "1w8vjlvxba6yvidqdvxddx3crl6z66h39qxj8xi6aqayw2nk0p7y",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#yansi@1.0.1": "0jdh55jyv0dpd38ij4qh60zglbw9aa8wafqai6m0wa7xaxk3mrfg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.7.5": "0m4i4a7gy826bfvnqa9wy6sp90qf0as3wps3wb0smjaamn68g013",
|
"registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.7.5": "0m4i4a7gy826bfvnqa9wy6sp90qf0as3wps3wb0smjaamn68g013",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#yoke@0.7.5": "0h3znzrdmll0a7sglzf9ji0p5iqml11wrj1dypaf6ad6kbpnl3hj",
|
"registry+https://github.com/rust-lang/crates.io-index#yoke@0.7.5": "0h3znzrdmll0a7sglzf9ji0p5iqml11wrj1dypaf6ad6kbpnl3hj",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#zerocopy-derive@0.7.35": "0gnf2ap2y92nwdalzz3x7142f2b83sni66l39vxp2ijd6j080kzs",
|
"registry+https://github.com/rust-lang/crates.io-index#zerocopy-derive@0.7.35": "0gnf2ap2y92nwdalzz3x7142f2b83sni66l39vxp2ijd6j080kzs",
|
||||||
|
24
flake.lock
generated
24
flake.lock
generated
@ -1,5 +1,20 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"crane": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1739936662,
|
||||||
|
"narHash": "sha256-x4syUjNUuRblR07nDPeLDP7DpphaBVbUaSoeZkFbGSk=",
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"rev": "19de14aaeb869287647d9461cbd389187d8ecdb7",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "ipetkov",
|
||||||
|
"repo": "crane",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
@ -20,16 +35,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1704732714,
|
"lastModified": 1740339700,
|
||||||
"narHash": "sha256-ABqK/HggMYA/jMUXgYyqVAcQ8QjeMyr1jcXfTpSHmps=",
|
"narHash": "sha256-cbrw7EgQhcdFnu6iS3vane53bEagZQy/xyIkDWpCgVE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "6723fa4e4f1a30d42a633bef5eb01caeb281adc3",
|
"rev": "04ef94c4c1582fd485bbfdb8c4a8ba250e359195",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"id": "nixpkgs",
|
||||||
"ref": "nixos-23.11",
|
"ref": "nixos-24.11",
|
||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -50,6 +65,7 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"crane": "crane",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"typeshare": "typeshare",
|
"typeshare": "typeshare",
|
||||||
"unstable": "unstable"
|
"unstable": "unstable"
|
||||||
|
14
flake.nix
14
flake.nix
@ -2,12 +2,13 @@
|
|||||||
description = "Lumenescent Dreams Tools";
|
description = "Lumenescent Dreams Tools";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "nixpkgs/nixos-23.11";
|
nixpkgs.url = "nixpkgs/nixos-24.11";
|
||||||
unstable.url = "nixpkgs/nixos-unstable";
|
unstable.url = "nixpkgs/nixos-unstable";
|
||||||
typeshare.url = "github:1Password/typeshare";
|
typeshare.url = "github:1Password/typeshare";
|
||||||
|
crane.url = "github:ipetkov/crane";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, unstable, typeshare, ... }:
|
outputs = { self, nixpkgs, unstable, typeshare, crane, ... }:
|
||||||
let
|
let
|
||||||
version = builtins.string 0 8 self.lastModifiedDate;
|
version = builtins.string 0 8 self.lastModifiedDate;
|
||||||
supportedSystems = [ "x86_64-linux" ];
|
supportedSystems = [ "x86_64-linux" ];
|
||||||
@ -25,7 +26,6 @@
|
|||||||
pkgs.cargo-watch
|
pkgs.cargo-watch
|
||||||
pkgs.clang
|
pkgs.clang
|
||||||
pkgs.crate2nix
|
pkgs.crate2nix
|
||||||
pkgs.trunk
|
|
||||||
pkgs.glib
|
pkgs.glib
|
||||||
pkgs.gst_all_1.gst-plugins-bad
|
pkgs.gst_all_1.gst-plugins-bad
|
||||||
pkgs.gst_all_1.gst-plugins-base
|
pkgs.gst_all_1.gst-plugins-base
|
||||||
@ -57,6 +57,8 @@
|
|||||||
packages."x86_64-linux" =
|
packages."x86_64-linux" =
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||||
|
craneLib = crane.mkLib pkgs;
|
||||||
|
src = craneLib.cleanCargoSource ./.;
|
||||||
|
|
||||||
gtkNativeInputs = [
|
gtkNativeInputs = [
|
||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
@ -89,6 +91,11 @@
|
|||||||
dashboard = cargo_nix.workspaceMembers.dashboard.build;
|
dashboard = cargo_nix.workspaceMembers.dashboard.build;
|
||||||
# file-service = cargo_nix.workspaceMembers.file-service.build;
|
# file-service = cargo_nix.workspaceMembers.file-service.build;
|
||||||
fitnesstrax = cargo_nix.workspaceMembers.fitnesstrax.build;
|
fitnesstrax = cargo_nix.workspaceMembers.fitnesstrax.build;
|
||||||
|
l10n-db = craneLib.buildPackage {
|
||||||
|
pname = "l10n-db";
|
||||||
|
cargoExtraArgs = "-p l10n-db";
|
||||||
|
src = ./.;
|
||||||
|
};
|
||||||
otg-gtk = cargo_nix.workspaceMembers.otg-gtk.build;
|
otg-gtk = cargo_nix.workspaceMembers.otg-gtk.build;
|
||||||
|
|
||||||
all = pkgs.symlinkJoin {
|
all = pkgs.symlinkJoin {
|
||||||
@ -99,6 +106,7 @@
|
|||||||
dashboard
|
dashboard
|
||||||
# file-service
|
# file-service
|
||||||
fitnesstrax
|
fitnesstrax
|
||||||
|
l10n-db
|
||||||
otg-gtk
|
otg-gtk
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
24
l10n-db/Cargo.toml
Normal file
24
l10n-db/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "l10n-db"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = { version = "0.4.39", features = ["serde"] }
|
||||||
|
clap = { version = "4.5.30", features = ["derive"] }
|
||||||
|
icu_locid = { version = "1.5.0", features = ["serde"] }
|
||||||
|
serde = { version = "1.0.218", features = ["derive"] }
|
||||||
|
serde_json = "1.0.139"
|
||||||
|
tempfile = "3.17.1"
|
||||||
|
thiserror = "2.0.11"
|
||||||
|
toml = "0.8.20"
|
||||||
|
xml-rs = "0.8.25"
|
||||||
|
|
||||||
|
# [lib]
|
||||||
|
# name = "l10n_db"
|
||||||
|
# path = "src/lib.rs"
|
||||||
|
#
|
||||||
|
# [[bin]]
|
||||||
|
# name = "l10n-db"
|
||||||
|
# path = "src/main.rs"
|
||||||
|
|
8
l10n-db/config.toml
Normal file
8
l10n-db/config.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
db_path = "./i18n"
|
||||||
|
base_locale = "en"
|
||||||
|
locales = [
|
||||||
|
"en",
|
||||||
|
"eo",
|
||||||
|
"de",
|
||||||
|
"es",
|
||||||
|
]
|
22
l10n-db/i18n/SaveSettings.toml
Normal file
22
l10n-db/i18n/SaveSettings.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
key = "SaveSettings"
|
||||||
|
description = "This is a label on a button which will save the settings when clicked"
|
||||||
|
|
||||||
|
[variants.eo]
|
||||||
|
locale = "eo"
|
||||||
|
content = "Konservi Agordojn"
|
||||||
|
modified = "2025-02-24T19:32:11.246639077Z"
|
||||||
|
|
||||||
|
[variants.es]
|
||||||
|
locale = "es"
|
||||||
|
content = "Guardar Configuraciones"
|
||||||
|
modified = "2025-02-24T19:33:23.861329923Z"
|
||||||
|
|
||||||
|
[variants.en]
|
||||||
|
locale = "en"
|
||||||
|
content = "Save Settings"
|
||||||
|
modified = "2025-02-22T23:44:18.874218939Z"
|
||||||
|
|
||||||
|
[variants.de]
|
||||||
|
locale = "de"
|
||||||
|
content = "Einstellungen Speichern"
|
||||||
|
modified = "2025-02-24T19:33:19.516005843Z"
|
22
l10n-db/i18n/TimeDistance.toml
Normal file
22
l10n-db/i18n/TimeDistance.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
key = "TimeDistance"
|
||||||
|
description = "A summary of a workout or many workouts that involve a time and a distance"
|
||||||
|
|
||||||
|
[variants.es]
|
||||||
|
locale = "es"
|
||||||
|
content = "{distance} de {activity} en {hours, plural, one {}=1 {{hours} hora} other {{hours} horas}} y {minutes, plural, one {}=1 {{minutes} minuto} other {{minutes} minutos}}"
|
||||||
|
modified = "2025-02-24T19:33:23.861604738Z"
|
||||||
|
|
||||||
|
[variants.eo]
|
||||||
|
locale = "eo"
|
||||||
|
content = "{distance} de {activity} en {hours, plural, =1 {{hours} horo} other {{hours} horoj}} {minutes, plural, =1 {{minutes} minuto} other {{minutes} minutoj}}"
|
||||||
|
modified = "2025-02-24T19:32:11.246943602Z"
|
||||||
|
|
||||||
|
[variants.en]
|
||||||
|
locale = "en"
|
||||||
|
content = "{distance} of {activity} in {hours, plural, =1 {{hours} hour} other {{hours} hours}} and {minutes, plural, =1 {{minutes} minute} other {{minutes} minutes}}"
|
||||||
|
modified = "2025-02-24T14:09:17.361641899Z"
|
||||||
|
|
||||||
|
[variants.de]
|
||||||
|
locale = "de"
|
||||||
|
content = "{distance} von {activity} in {hours, plural, one {}=1 {{hours} Stunde} other {{hours} Stunden}} und {minutes, plural, one {}=1 {{minutes} Minute} other {{minutes} Minuten}}"
|
||||||
|
modified = "2025-02-24T19:33:19.516210807Z"
|
22
l10n-db/i18n/Welcome.toml
Normal file
22
l10n-db/i18n/Welcome.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
key = "Welcome"
|
||||||
|
description = "This is a welcome content that will be shown on first app opening, before configuration."
|
||||||
|
|
||||||
|
[variants.en]
|
||||||
|
locale = "en"
|
||||||
|
content = "Welcome to FitnessTrax, the privacy-centered fitness tracker"
|
||||||
|
modified = "2025-02-25T02:12:25.757240004Z"
|
||||||
|
|
||||||
|
[variants.eo]
|
||||||
|
locale = "eo"
|
||||||
|
content = "Bonvenon al FitnessTrax"
|
||||||
|
modified = "2025-02-24T19:32:11.246407627Z"
|
||||||
|
|
||||||
|
[variants.es]
|
||||||
|
locale = "es"
|
||||||
|
content = "Bienvenido a FitnessTrax"
|
||||||
|
modified = "2025-02-24T19:33:23.861143003Z"
|
||||||
|
|
||||||
|
[variants.de]
|
||||||
|
locale = "de"
|
||||||
|
content = "Willkommen bei FitnessTrax"
|
||||||
|
modified = "2025-02-24T19:33:19.515861453Z"
|
143
l10n-db/src/bin/l10n-db.rs
Normal file
143
l10n-db/src/bin/l10n-db.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
use std::{fmt, path::PathBuf};
|
||||||
|
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
use icu_locid::{langid, LanguageIdentifier};
|
||||||
|
use l10n_db::{
|
||||||
|
self, js, read_file,
|
||||||
|
xliff::{self, import_file},
|
||||||
|
Bundle, Editor, ReadError,
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Option<Commands>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum Commands {
|
||||||
|
/// Edit, potentially creating, a key
|
||||||
|
EditKey {
|
||||||
|
#[arg(short, long)]
|
||||||
|
key: String,
|
||||||
|
#[arg(short, long)]
|
||||||
|
locale: Option<String>,
|
||||||
|
},
|
||||||
|
/// List al keys in the database
|
||||||
|
ListKeys,
|
||||||
|
// Search the database
|
||||||
|
// Search { },
|
||||||
|
Import {
|
||||||
|
#[arg(short, long)]
|
||||||
|
file: String,
|
||||||
|
},
|
||||||
|
/// Export the database
|
||||||
|
Export {
|
||||||
|
#[arg(short, long)]
|
||||||
|
format: String,
|
||||||
|
#[arg(short, long)]
|
||||||
|
file: String,
|
||||||
|
#[arg(short, long)]
|
||||||
|
locale: Option<String>,
|
||||||
|
},
|
||||||
|
Report,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Config {
|
||||||
|
db_path: PathBuf,
|
||||||
|
base_locale: LanguageIdentifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edit_key(bundle: &mut Bundle, key: String, locale: LanguageIdentifier, editor: &str) {
|
||||||
|
let message = bundle.message(key);
|
||||||
|
Editor::edit(message, locale, editor);
|
||||||
|
bundle.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
struct Report {
|
||||||
|
keys: Vec<String>,
|
||||||
|
// source_deleted: Vec<String>,
|
||||||
|
out_of_date: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Report {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
writeln!(f, "{} messages in bundle", self.keys.len())?;
|
||||||
|
writeln!(f, "Out of date messages")?;
|
||||||
|
for key in self.out_of_date.iter() {
|
||||||
|
writeln!(f, "\t{}", key)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_report(bundle: &Bundle, base_locale: &LanguageIdentifier) -> Report {
|
||||||
|
let mut report: Report = Default::default();
|
||||||
|
for (key, message) in bundle.message_iter() {
|
||||||
|
report.keys.push(key.to_owned());
|
||||||
|
if !message.variants_out_of_date(base_locale).is_empty() {
|
||||||
|
report.out_of_date.push(key.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
report
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let editor = std::env::var("EDITOR").expect("Set EDITOR to the path to your favorite editor");
|
||||||
|
|
||||||
|
let config: Config = read_file(&PathBuf::from("./config.toml"))
|
||||||
|
.and_then(|bytes| String::from_utf8(bytes).map_err(|_| ReadError::InvalidFormat))
|
||||||
|
.and_then(|content| toml::from_str(&content).map_err(|_| ReadError::InvalidFormat))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
let mut bundle = Bundle::load_from_disk(PathBuf::from(&config.db_path));
|
||||||
|
|
||||||
|
match &cli.command {
|
||||||
|
Some(Commands::EditKey { key, locale }) => {
|
||||||
|
let identifier = locale.as_ref()
|
||||||
|
.map(|l| l.parse::<LanguageIdentifier>().unwrap())
|
||||||
|
.unwrap_or(config.base_locale);
|
||||||
|
edit_key(&mut bundle, key.to_owned(), identifier, &editor)
|
||||||
|
}
|
||||||
|
Some(Commands::ListKeys) => {
|
||||||
|
for (key, _) in bundle.message_iter() {
|
||||||
|
println!("{}", key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Commands::Import { file }) => {
|
||||||
|
import_file(&mut bundle, &PathBuf::from(file)).unwrap();
|
||||||
|
bundle.save();
|
||||||
|
}
|
||||||
|
Some(Commands::Export {
|
||||||
|
format,
|
||||||
|
file,
|
||||||
|
locale,
|
||||||
|
}) => {
|
||||||
|
let locale = locale
|
||||||
|
.as_ref()
|
||||||
|
.map(|l| l.clone().parse::<LanguageIdentifier>().unwrap())
|
||||||
|
.unwrap_or(langid!("en"));
|
||||||
|
|
||||||
|
match format.as_ref() {
|
||||||
|
"js" => js::export_file(&bundle, locale, &PathBuf::from(file)).unwrap(),
|
||||||
|
"xliff" => {
|
||||||
|
xliff::export_file(&bundle, locale, &PathBuf::from(file)).unwrap()
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Commands::Report) => {
|
||||||
|
let report = generate_report(&bundle, &config.base_locale);
|
||||||
|
println!("{}", report);
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
143
l10n-db/src/bundle.rs
Normal file
143
l10n-db/src/bundle.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
use std::{collections::HashMap, fs::File, io::{BufReader, Read, Write}, path::{Path, PathBuf}};
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use icu_locid::LanguageIdentifier;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub struct Bundle {
|
||||||
|
path: PathBuf,
|
||||||
|
messages: HashMap<String, Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bundle {
|
||||||
|
pub fn load_from_disk(path: PathBuf) -> Self {
|
||||||
|
let mut messages = HashMap::new();
|
||||||
|
if path.is_dir() {
|
||||||
|
for entry in std::fs::read_dir(&path).unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
let path = entry.path().clone();
|
||||||
|
let key = path.file_stem().unwrap();
|
||||||
|
|
||||||
|
let message = Message::from_file(&entry.path());
|
||||||
|
messages.insert(key.to_str().unwrap().to_owned(), message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self { path, messages }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_iter(&self) -> impl Iterator<Item = (&String, &Message)> {
|
||||||
|
self.messages.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message(&mut self, name: String) -> &mut Message {
|
||||||
|
self.messages
|
||||||
|
.entry(name.to_owned())
|
||||||
|
.or_insert(Message::new(name.to_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self) {
|
||||||
|
self.messages.iter().for_each(|(key, value)| {
|
||||||
|
let mut path = self.path.clone();
|
||||||
|
path.push(key);
|
||||||
|
path.set_extension("toml");
|
||||||
|
save_file(&path, toml::to_string(value).unwrap().as_bytes());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_file(path: &PathBuf, s: &[u8]) {
|
||||||
|
let mut f = File::create(path).unwrap();
|
||||||
|
let _ = f.write(s).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
pub struct Message {
|
||||||
|
key: String,
|
||||||
|
description: String,
|
||||||
|
variants: HashMap<LanguageIdentifier, Variant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message {
|
||||||
|
pub fn new(key: String) -> Self {
|
||||||
|
Self {
|
||||||
|
key,
|
||||||
|
description: "".to_owned(),
|
||||||
|
variants: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file(path: &Path) -> Message {
|
||||||
|
let file = std::fs::File::open(path).unwrap();
|
||||||
|
let mut content = Vec::new();
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
let _ = reader.read_to_end(&mut content);
|
||||||
|
toml::from_str(&String::from_utf8(content).unwrap()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_description(&mut self, desc: String) {
|
||||||
|
self.description = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(&self) -> &str {
|
||||||
|
&self.description
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variant(&self, locale: &LanguageIdentifier) -> Option<&Variant> {
|
||||||
|
self.variants.get(locale)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variant_mut(&mut self, locale: LanguageIdentifier) -> &mut Variant {
|
||||||
|
self.variants.entry(locale.clone()).or_insert(Variant {
|
||||||
|
locale,
|
||||||
|
content: "".to_owned(),
|
||||||
|
modified: Utc::now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variants_out_of_date(
|
||||||
|
&self,
|
||||||
|
base_locale: &LanguageIdentifier,
|
||||||
|
) -> Vec<LanguageIdentifier> {
|
||||||
|
match self
|
||||||
|
.variants
|
||||||
|
.get(base_locale)
|
||||||
|
.map(|variant| variant.modified())
|
||||||
|
{
|
||||||
|
Some(base_date) => self
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, value)| base_date > value.modified())
|
||||||
|
.map(|(locale, _)| locale.clone())
|
||||||
|
.collect(),
|
||||||
|
None => vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn missing_variants(&self, locals: Vec<LanguageIdentifier>) -> Vec<LanguageIdentifier> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||||
|
pub struct Variant {
|
||||||
|
locale: LanguageIdentifier,
|
||||||
|
content: String,
|
||||||
|
modified: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Variant {
|
||||||
|
pub fn content(&self) -> &str {
|
||||||
|
&self.content
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_content(&mut self, content: String) {
|
||||||
|
self.content = content;
|
||||||
|
self.modified = Utc::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modified(&self) -> DateTime<Utc> {
|
||||||
|
self.modified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {}
|
43
l10n-db/src/editor.rs
Normal file
43
l10n-db/src/editor.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use std::{io::Write, process::Command};
|
||||||
|
|
||||||
|
use icu_locid::{langid, LanguageIdentifier};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{read_fh, Message};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
struct EditorMessage {
|
||||||
|
description: String,
|
||||||
|
source: String,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Editor {}
|
||||||
|
|
||||||
|
impl Editor {
|
||||||
|
pub fn edit(msg: &mut Message, locale: LanguageIdentifier, editor: &str) {
|
||||||
|
let description = msg.description().to_owned();
|
||||||
|
let source_string = msg.variant_mut(langid!("en")).content().to_owned();
|
||||||
|
let variant = msg.variant_mut(locale);
|
||||||
|
// let editable_content = EditorMessage::from_variant(description, &variant);
|
||||||
|
let editable_content = EditorMessage {
|
||||||
|
description,
|
||||||
|
source: source_string,
|
||||||
|
content: variant.content().to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut file = tempfile::NamedTempFile::new().unwrap();
|
||||||
|
let _ = file.write(toml::to_string(&editable_content).unwrap().as_bytes());
|
||||||
|
let _ = file.flush();
|
||||||
|
let mut cmd = Command::new(editor).args([file.path()]).spawn().unwrap();
|
||||||
|
cmd.wait().unwrap();
|
||||||
|
let file = file.reopen().unwrap();
|
||||||
|
let content = read_fh(&file).unwrap();
|
||||||
|
|
||||||
|
let new_variant: EditorMessage =
|
||||||
|
toml::from_str(&String::from_utf8(content).unwrap()).unwrap();
|
||||||
|
|
||||||
|
variant.set_content(new_variant.content);
|
||||||
|
msg.set_description(new_variant.description);
|
||||||
|
}
|
||||||
|
}
|
22
l10n-db/src/formats/js.rs
Normal file
22
l10n-db/src/formats/js.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use std::{collections::BTreeMap, fs::File, io::Write, path::Path};
|
||||||
|
|
||||||
|
use icu_locid::LanguageIdentifier;
|
||||||
|
|
||||||
|
use crate::{Bundle, WriteError};
|
||||||
|
|
||||||
|
pub fn export_file(bundle: &Bundle, locale: LanguageIdentifier, path: &Path) -> Result<(), WriteError> {
|
||||||
|
let mut file = File::create(path).unwrap();
|
||||||
|
export_fh(bundle, locale, &mut file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn export_fh(bundle: &Bundle, locale: LanguageIdentifier, fh: &mut File) -> Result<(), WriteError> {
|
||||||
|
let messages = bundle.message_iter().map(|(key, message)| {
|
||||||
|
let content = message.variant(&locale).unwrap().content().to_owned();
|
||||||
|
(key.to_owned(), content)
|
||||||
|
}).collect::<Vec<(String, String)>>();
|
||||||
|
|
||||||
|
let messages: BTreeMap<String, String> = messages.into_iter().collect();
|
||||||
|
let _ = fh.write(serde_json::to_string_pretty(&messages).unwrap().as_bytes()).unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
4
l10n-db/src/formats/mod.rs
Normal file
4
l10n-db/src/formats/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod js;
|
||||||
|
pub mod xliff;
|
||||||
|
|
||||||
|
|
146
l10n-db/src/formats/xliff.rs
Normal file
146
l10n-db/src/formats/xliff.rs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{BufReader, Read, Write},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
use icu_locid::{langid, LanguageIdentifier};
|
||||||
|
use xml::{attribute::OwnedAttribute, reader, writer, EmitterConfig, EventReader, EventWriter};
|
||||||
|
|
||||||
|
use crate::{Bundle, Message, ReadError, WriteError};
|
||||||
|
|
||||||
|
pub fn export_file(
|
||||||
|
bundle: &Bundle,
|
||||||
|
locale: LanguageIdentifier,
|
||||||
|
path: &Path,
|
||||||
|
) -> Result<(), WriteError> {
|
||||||
|
let mut file = File::create(path).unwrap();
|
||||||
|
export_fh(bundle, locale, &mut file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn export_fh<W>(bundle: &Bundle, locale: LanguageIdentifier, fh: W) -> Result<(), WriteError>
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
let mut writer = EmitterConfig::new().perform_indent(true).create_writer(fh);
|
||||||
|
|
||||||
|
writer
|
||||||
|
.write(
|
||||||
|
writer::XmlEvent::start_element("xliff")
|
||||||
|
.attr("xmlns", "urn:oasis:names:tc:xliff:document:2.0")
|
||||||
|
.attr("version", "2.0")
|
||||||
|
.attr("srcLang", &format!("{}", locale)),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writer
|
||||||
|
.write(writer::XmlEvent::start_element("file").attr("id", "main"))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for (key, message) in bundle.message_iter() {
|
||||||
|
write_message(&mut writer, key, message, &locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write(writer::XmlEvent::end_element()).unwrap();
|
||||||
|
writer.write(writer::XmlEvent::end_element()).unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn import_file(bundle: &mut Bundle, path: &Path) -> Result<(), ReadError> {
|
||||||
|
let file = File::open(path).unwrap();
|
||||||
|
let file = BufReader::new(file);
|
||||||
|
|
||||||
|
import_reader(bundle, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn import_reader<R>(bundle: &mut Bundle, fh: R) -> Result<(), ReadError>
|
||||||
|
where
|
||||||
|
R: Read,
|
||||||
|
{
|
||||||
|
let parser = EventReader::new(fh);
|
||||||
|
|
||||||
|
let mut locale: LanguageIdentifier = langid!("en");
|
||||||
|
let mut current_key = None;
|
||||||
|
let mut current_text: Option<String> = None;
|
||||||
|
let mut in_target = false;
|
||||||
|
|
||||||
|
for event in parser {
|
||||||
|
match event {
|
||||||
|
Ok(reader::XmlEvent::StartElement {
|
||||||
|
name, attributes, ..
|
||||||
|
}) => match name.local_name.as_ref() {
|
||||||
|
"xliff" => {
|
||||||
|
locale = find_attribute(&attributes, "trgLang")
|
||||||
|
.unwrap()
|
||||||
|
.parse::<LanguageIdentifier>()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"unit" => current_key = find_attribute(&attributes, "id"),
|
||||||
|
"target" => in_target = true,
|
||||||
|
_ => println!("name: {}", name),
|
||||||
|
},
|
||||||
|
Ok(reader::XmlEvent::EndElement { name }) => match name.local_name.as_ref() {
|
||||||
|
"unit" => {
|
||||||
|
if let Some(key) = current_key {
|
||||||
|
let message = bundle.message(key);
|
||||||
|
let variant = message.variant_mut(locale.clone());
|
||||||
|
if let Some(ref text) = current_text {
|
||||||
|
variant.set_content(text.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_key = None;
|
||||||
|
}
|
||||||
|
"target" => in_target = false,
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Ok(reader::XmlEvent::Characters(data)) => {
|
||||||
|
if in_target {
|
||||||
|
current_text = Some(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("error: {e}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_message<T>(
|
||||||
|
writer: &mut EventWriter<T>,
|
||||||
|
key: &str,
|
||||||
|
message: &Message,
|
||||||
|
locale: &LanguageIdentifier,
|
||||||
|
) where
|
||||||
|
T: std::io::Write,
|
||||||
|
{
|
||||||
|
[
|
||||||
|
writer::XmlEvent::start_element("unit")
|
||||||
|
.attr("id", key)
|
||||||
|
.into(),
|
||||||
|
writer::XmlEvent::start_element("notes").into(),
|
||||||
|
writer::XmlEvent::start_element("note").into(),
|
||||||
|
writer::XmlEvent::characters(message.description()),
|
||||||
|
writer::XmlEvent::end_element().into(),
|
||||||
|
writer::XmlEvent::end_element().into(),
|
||||||
|
writer::XmlEvent::start_element("segment").into(),
|
||||||
|
writer::XmlEvent::start_element("source").into(),
|
||||||
|
writer::XmlEvent::characters(message.variant(locale).unwrap().content()),
|
||||||
|
writer::XmlEvent::end_element().into(),
|
||||||
|
writer::XmlEvent::end_element().into(),
|
||||||
|
writer::XmlEvent::end_element().into(),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|elem: writer::XmlEvent| writer.write(elem).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_attribute(attrs: &Vec<OwnedAttribute>, name: &str) -> Option<String> {
|
||||||
|
for f in attrs {
|
||||||
|
if name == f.name.local_name {
|
||||||
|
return Some(f.value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
11
l10n-db/src/lib.rs
Normal file
11
l10n-db/src/lib.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
mod bundle;
|
||||||
|
pub use bundle::{Bundle, Message, Variant};
|
||||||
|
|
||||||
|
mod editor;
|
||||||
|
pub use editor::Editor;
|
||||||
|
|
||||||
|
mod formats;
|
||||||
|
pub use formats::{js, xliff};
|
||||||
|
|
||||||
|
mod utils;
|
||||||
|
pub use utils::*;
|
37
l10n-db/src/utils.rs
Normal file
37
l10n-db/src/utils.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use std::{fs::File, io::{BufReader, ErrorKind, Read}, path::Path};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ReadError {
|
||||||
|
#[error("file not found")]
|
||||||
|
FileNotFound,
|
||||||
|
|
||||||
|
#[error("invalid file format")]
|
||||||
|
InvalidFormat,
|
||||||
|
|
||||||
|
#[error("unhandled read error")]
|
||||||
|
Unhandled(ErrorKind),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_file(path: &Path) -> Result<Vec<u8>, ReadError> {
|
||||||
|
let file = File::open(path).map_err(|err| {
|
||||||
|
match err.kind() {
|
||||||
|
ErrorKind::NotFound => ReadError::FileNotFound,
|
||||||
|
_ => ReadError::Unhandled( err.kind()),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
read_fh(&file)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_fh(file: &File) -> Result<Vec<u8>, ReadError> {
|
||||||
|
let mut content = Vec::new();
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
reader.read_to_end(&mut content).map_err(|err| ReadError::Unhandled(err.kind()))?;
|
||||||
|
Ok(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum WriteError {
|
||||||
|
#[error("unhandled write error")]
|
||||||
|
Unhandled(ErrorKind),
|
||||||
|
}
|
11
pico-st7789/.cargo/config.toml
Normal file
11
pico-st7789/.cargo/config.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[build]
|
||||||
|
target = "thumbv6m-none-eabi"
|
||||||
|
|
||||||
|
[target.thumbv6m-none-eabi]
|
||||||
|
rustflags = [
|
||||||
|
"-C", "link-arg=--nmagic",
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
"-C", "no-vectorize-loops",
|
||||||
|
]
|
||||||
|
|
||||||
|
runner = "elf2uf2-rs -d"
|
13
pico-st7789/Cargo.toml
Normal file
13
pico-st7789/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "pico-st7789"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitflags = "2.9.0"
|
||||||
|
cortex-m-rt = "0.7.3"
|
||||||
|
embedded-alloc = "0.6.0"
|
||||||
|
embedded-hal = "1.0.0"
|
||||||
|
fugit = "0.3.7"
|
||||||
|
panic-halt = "1.0.0"
|
||||||
|
rp-pico = "0.9.0"
|
62
pico-st7789/src/canvas.rs
Normal file
62
pico-st7789/src/canvas.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// a a a
|
||||||
|
// f b
|
||||||
|
// f b
|
||||||
|
// f b
|
||||||
|
// g g g
|
||||||
|
// e c
|
||||||
|
// e c
|
||||||
|
// e c
|
||||||
|
// d d d
|
||||||
|
|
||||||
|
use crate::font::{Font, Glyph};
|
||||||
|
|
||||||
|
pub struct RGB {
|
||||||
|
pub r: u8,
|
||||||
|
pub g: u8,
|
||||||
|
pub b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Canvas {
|
||||||
|
fn set_pixel(&mut self, x: usize, y: usize, color: &RGB);
|
||||||
|
|
||||||
|
fn fill(&mut self, x1: usize, y1: usize, x2: usize, y2: usize, color: &RGB) {
|
||||||
|
for x in x1..x2 + 1 {
|
||||||
|
for y in y1..y2 + 1 {
|
||||||
|
self.set_pixel(x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(&mut self, x1: usize, y1: usize, x2: usize, y2: usize, color: &RGB) {
|
||||||
|
for x in x1..x2 + 1 {
|
||||||
|
self.set_pixel(x, y1, color);
|
||||||
|
}
|
||||||
|
for x in x1..x2 + 1 {
|
||||||
|
self.set_pixel(x, y2, color);
|
||||||
|
}
|
||||||
|
for y in y1..y2 + 1 {
|
||||||
|
self.set_pixel(x1, y, color);
|
||||||
|
}
|
||||||
|
for y in y1..y2 + 1 {
|
||||||
|
self.set_pixel(x2, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print<A>(
|
||||||
|
canvas: &mut impl Canvas,
|
||||||
|
font: &impl Font<A>,
|
||||||
|
sx: usize,
|
||||||
|
sy: usize,
|
||||||
|
text: &str,
|
||||||
|
color: &RGB,
|
||||||
|
) where
|
||||||
|
A: Glyph,
|
||||||
|
{
|
||||||
|
let mut x = sx;
|
||||||
|
for c in text.chars().map(|c| font.glyph(c)) {
|
||||||
|
c.draw(canvas, x, sy, color);
|
||||||
|
let (dx, _) = c.extents();
|
||||||
|
x = x + dx + 1;
|
||||||
|
}
|
||||||
|
}
|
513
pico-st7789/src/font/bits_5_8.rs
Normal file
513
pico-st7789/src/font/bits_5_8.rs
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
use alloc::collections::btree_map::BTreeMap;
|
||||||
|
|
||||||
|
use crate::canvas::{Canvas, RGB};
|
||||||
|
|
||||||
|
use super::{Font, Glyph};
|
||||||
|
|
||||||
|
pub struct BitmapGlyph([u8; 7]);
|
||||||
|
|
||||||
|
pub struct BitmapFont(BTreeMap<char, BitmapGlyph>);
|
||||||
|
|
||||||
|
impl BitmapFont {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut font = BTreeMap::new();
|
||||||
|
font.insert(' ', BitmapGlyph([
|
||||||
|
0b00000,
|
||||||
|
0b00000,
|
||||||
|
0b00000,
|
||||||
|
0b00000,
|
||||||
|
0b00000,
|
||||||
|
0b00000,
|
||||||
|
0b00000,
|
||||||
|
]));
|
||||||
|
font.insert(
|
||||||
|
':',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b00000,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00000,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00000,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'/',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b00001,
|
||||||
|
0b00010,
|
||||||
|
0b00010,
|
||||||
|
0b00100,
|
||||||
|
0b01000,
|
||||||
|
0b01000,
|
||||||
|
0b10000,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'0',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10101,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'1',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b00001,
|
||||||
|
0b00011,
|
||||||
|
0b00101,
|
||||||
|
0b00001,
|
||||||
|
0b00001,
|
||||||
|
0b00001,
|
||||||
|
0b00001,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'2',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b00001,
|
||||||
|
0b00010,
|
||||||
|
0b00100,
|
||||||
|
0b01000,
|
||||||
|
0b11111,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'3',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b00001,
|
||||||
|
0b01110,
|
||||||
|
0b00001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'4',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b11111,
|
||||||
|
0b00001,
|
||||||
|
0b00001,
|
||||||
|
0b00001,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'5',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11111,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b11111,
|
||||||
|
0b00001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'6',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10000,
|
||||||
|
0b11110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'7',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11111,
|
||||||
|
0b00001,
|
||||||
|
0b00010,
|
||||||
|
0b00110,
|
||||||
|
0b00100,
|
||||||
|
0b01000,
|
||||||
|
0b01000,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'8',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'9',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01111,
|
||||||
|
0b00001,
|
||||||
|
0b00001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'A',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b00100,
|
||||||
|
0b01010,
|
||||||
|
0b01010,
|
||||||
|
0b10001,
|
||||||
|
0b11111,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'B',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b1111 ,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b11110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'C',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'D',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b11110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'E',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11111,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b11111,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b11111,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'F',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11111,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b11110,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'G',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10000,
|
||||||
|
0b10011,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'H',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b11111,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'I',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11111,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b11111,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'J',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b00111,
|
||||||
|
0b00001,
|
||||||
|
0b00001,
|
||||||
|
0b00001,
|
||||||
|
0b00001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'K',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b10010,
|
||||||
|
0b10100,
|
||||||
|
0b11000,
|
||||||
|
0b10100,
|
||||||
|
0b10010,
|
||||||
|
0b10001,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'L',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b11111,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'M',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b11011,
|
||||||
|
0b10101,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'N',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b11001,
|
||||||
|
0b11001,
|
||||||
|
0b10101,
|
||||||
|
0b10011,
|
||||||
|
0b10011,
|
||||||
|
0b10001,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'O',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'P',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b11110,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
0b10000,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'Q',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10101,
|
||||||
|
0b10011,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'R',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11110,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b11110,
|
||||||
|
0b10100,
|
||||||
|
0b10010,
|
||||||
|
0b10001,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'S',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b01110,
|
||||||
|
0b10001,
|
||||||
|
0b10000,
|
||||||
|
0b01110,
|
||||||
|
0b00001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'T',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11111,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'U',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01110,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'V',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01010,
|
||||||
|
0b01010,
|
||||||
|
0b00100,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'W',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b10101,
|
||||||
|
0b10101,
|
||||||
|
0b01010,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'X',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01010,
|
||||||
|
0b00100,
|
||||||
|
0b01010,
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'Y',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b10001,
|
||||||
|
0b10001,
|
||||||
|
0b01010,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
0b00100,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'Z',
|
||||||
|
BitmapGlyph([
|
||||||
|
0b11111,
|
||||||
|
0b00001,
|
||||||
|
0b00010,
|
||||||
|
0b00100,
|
||||||
|
0b01000,
|
||||||
|
0b10000,
|
||||||
|
0b11111,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
Self(font)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Font<BitmapGlyph> for BitmapFont {
|
||||||
|
fn glyph(&self, c: char) -> &BitmapGlyph {
|
||||||
|
self.0.get(&c).unwrap_or(self.0.get(&' ').unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Glyph for BitmapGlyph {
|
||||||
|
fn draw(&self, canvas: &mut impl Canvas, x: usize, y: usize, color: &RGB) {
|
||||||
|
for row in 0..7 {
|
||||||
|
if self.0[row] & (1 << 4) > 0 {
|
||||||
|
canvas.set_pixel(x, y + row, color);
|
||||||
|
}
|
||||||
|
if self.0[row] & (1 << 3) > 0 {
|
||||||
|
canvas.set_pixel(x + 1, y + row, color);
|
||||||
|
}
|
||||||
|
if self.0[row] & (1 << 2) > 0 {
|
||||||
|
canvas.set_pixel(x + 2, y + row, color);
|
||||||
|
}
|
||||||
|
if self.0[row] & (1 << 1) > 0 {
|
||||||
|
canvas.set_pixel(x + 3, y + row, color);
|
||||||
|
}
|
||||||
|
if self.0[row] & 1 > 0 {
|
||||||
|
canvas.set_pixel(x + 4, y + row, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extents(&self) -> (usize, usize) {
|
||||||
|
(5, 7)
|
||||||
|
}
|
||||||
|
}
|
20
pico-st7789/src/font/mod.rs
Normal file
20
pico-st7789/src/font/mod.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use crate::canvas::{Canvas, RGB};
|
||||||
|
|
||||||
|
mod bits_5_8;
|
||||||
|
pub use bits_5_8::BitmapFont;
|
||||||
|
|
||||||
|
mod seven_segment;
|
||||||
|
pub use seven_segment::SevenSegmentFont;
|
||||||
|
|
||||||
|
mod sixteen_segment;
|
||||||
|
pub use sixteen_segment::SixteenSegmentFont;
|
||||||
|
|
||||||
|
pub trait Font<A> {
|
||||||
|
fn glyph(&self, c: char) -> &A;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Glyph {
|
||||||
|
fn draw(&self, canvas: &mut impl Canvas, x: usize, y: usize, color: &RGB);
|
||||||
|
fn extents(&self) -> (usize, usize);
|
||||||
|
}
|
||||||
|
|
211
pico-st7789/src/font/seven_segment.rs
Normal file
211
pico-st7789/src/font/seven_segment.rs
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
use alloc::collections::btree_map::BTreeMap;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
use crate::canvas::{Canvas, RGB};
|
||||||
|
|
||||||
|
use super::{Font, Glyph};
|
||||||
|
|
||||||
|
// Seven Segments
|
||||||
|
//
|
||||||
|
// a a a
|
||||||
|
// f b
|
||||||
|
// f b
|
||||||
|
// f b
|
||||||
|
// g g g
|
||||||
|
// e c
|
||||||
|
// e c
|
||||||
|
// e c
|
||||||
|
// d d d
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct SevenSegmentGlyph: u8 {
|
||||||
|
const NONE = 0;
|
||||||
|
const A = 0x01;
|
||||||
|
const B = 0x01 << 1;
|
||||||
|
const C = 0x01 << 2;
|
||||||
|
const D = 0x01 << 3;
|
||||||
|
const E = 0x01 << 4;
|
||||||
|
const F = 0x01 << 5;
|
||||||
|
const G = 0x01 << 6;
|
||||||
|
const DOT = 0x01 << 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SevenSegmentFont(BTreeMap<char, SevenSegmentGlyph>);
|
||||||
|
|
||||||
|
impl SevenSegmentFont {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut font = BTreeMap::new();
|
||||||
|
font.insert(' ', SevenSegmentGlyph::NONE);
|
||||||
|
font.insert(
|
||||||
|
'0',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::B
|
||||||
|
| SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::D
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::F,
|
||||||
|
);
|
||||||
|
font.insert('1', SevenSegmentGlyph::B | SevenSegmentGlyph::C);
|
||||||
|
font.insert(
|
||||||
|
'2',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::B
|
||||||
|
| SevenSegmentGlyph::D
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'3',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::B
|
||||||
|
| SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::G
|
||||||
|
| SevenSegmentGlyph::D,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'4',
|
||||||
|
SevenSegmentGlyph::B
|
||||||
|
| SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::F
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'5',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::D
|
||||||
|
| SevenSegmentGlyph::F
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'6',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::F
|
||||||
|
| SevenSegmentGlyph::G
|
||||||
|
| SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::D,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'7',
|
||||||
|
SevenSegmentGlyph::A | SevenSegmentGlyph::B | SevenSegmentGlyph::C,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'8',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::B
|
||||||
|
| SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::D
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::F
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'9',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::B
|
||||||
|
| SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::F
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'A',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::B
|
||||||
|
| SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::F
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'B',
|
||||||
|
SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::D
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::F
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'C',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::D
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::F,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'D',
|
||||||
|
SevenSegmentGlyph::B
|
||||||
|
| SevenSegmentGlyph::C
|
||||||
|
| SevenSegmentGlyph::D
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'E',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::D
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::F
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'F',
|
||||||
|
SevenSegmentGlyph::A
|
||||||
|
| SevenSegmentGlyph::E
|
||||||
|
| SevenSegmentGlyph::F
|
||||||
|
| SevenSegmentGlyph::G,
|
||||||
|
);
|
||||||
|
Self(font)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Font<SevenSegmentGlyph> for SevenSegmentFont {
|
||||||
|
fn glyph(&self, c: char) -> &SevenSegmentGlyph {
|
||||||
|
self.0.get(&c).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Glyph for SevenSegmentGlyph {
|
||||||
|
fn draw(&self, canvas: &mut impl Canvas, x: usize, y: usize, color: &RGB) {
|
||||||
|
if self.contains(SevenSegmentGlyph::A) {
|
||||||
|
canvas.set_pixel(x + 1, y, color);
|
||||||
|
canvas.set_pixel(x + 2, y, color);
|
||||||
|
canvas.set_pixel(x + 3, y, color);
|
||||||
|
}
|
||||||
|
if self.contains(SevenSegmentGlyph::B) {
|
||||||
|
canvas.set_pixel(x + 4, y + 1, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 2, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 3, color);
|
||||||
|
}
|
||||||
|
if self.contains(SevenSegmentGlyph::C) {
|
||||||
|
canvas.set_pixel(x + 4, y + 5, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 6, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 7, color);
|
||||||
|
}
|
||||||
|
if self.contains(SevenSegmentGlyph::D) {
|
||||||
|
canvas.set_pixel(x + 1, y + 8, color);
|
||||||
|
canvas.set_pixel(x + 2, y + 8, color);
|
||||||
|
canvas.set_pixel(x + 3, y + 8, color);
|
||||||
|
}
|
||||||
|
if self.contains(SevenSegmentGlyph::E) {
|
||||||
|
canvas.set_pixel(x, y + 5, color);
|
||||||
|
canvas.set_pixel(x, y + 6, color);
|
||||||
|
canvas.set_pixel(x, y + 7, color);
|
||||||
|
}
|
||||||
|
if self.contains(SevenSegmentGlyph::F) {
|
||||||
|
canvas.set_pixel(x, y + 1, color);
|
||||||
|
canvas.set_pixel(x, y + 2, color);
|
||||||
|
canvas.set_pixel(x, y + 3, color);
|
||||||
|
}
|
||||||
|
if self.contains(SevenSegmentGlyph::G) {
|
||||||
|
canvas.set_pixel(x + 1, y + 4, color);
|
||||||
|
canvas.set_pixel(x + 2, y + 4, color);
|
||||||
|
canvas.set_pixel(x + 3, y + 4, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extents(&self) -> (usize, usize) {
|
||||||
|
(5, 8)
|
||||||
|
}
|
||||||
|
}
|
689
pico-st7789/src/font/sixteen_segment.rs
Normal file
689
pico-st7789/src/font/sixteen_segment.rs
Normal file
@ -0,0 +1,689 @@
|
|||||||
|
use alloc::collections::btree_map::BTreeMap;
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
use crate::canvas::{Canvas, RGB};
|
||||||
|
|
||||||
|
use super::{Font, Glyph};
|
||||||
|
|
||||||
|
// Sixteen Segments
|
||||||
|
// https://www.partsnotincluded.com/segmented-led-display-ascii-library/
|
||||||
|
//
|
||||||
|
// a1 a2
|
||||||
|
// f h i jb
|
||||||
|
// f i j b
|
||||||
|
// f hij b
|
||||||
|
// g1 g2
|
||||||
|
// e klm c
|
||||||
|
// e k l m c
|
||||||
|
// ek l mc
|
||||||
|
// d1 d2
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct SixteenSegmentGlyph: u32 {
|
||||||
|
const NONE = 0;
|
||||||
|
const A1 = 0x0001;
|
||||||
|
const A2 = 0x0001 << 1;
|
||||||
|
const B = 0x0001 << 2;
|
||||||
|
const C = 0x0001 << 3;
|
||||||
|
const D1 = 0x0001 << 4;
|
||||||
|
const D2 = 0x0001 << 5;
|
||||||
|
const E = 0x0001 << 6;
|
||||||
|
const F = 0x0001 << 7;
|
||||||
|
const G1 = 0x0001 << 8;
|
||||||
|
const G2 = 0x0001 << 9;
|
||||||
|
const H = 0x0001 << 10;
|
||||||
|
const I = 0x0001 << 11;
|
||||||
|
const J = 0x0001 << 12;
|
||||||
|
const K = 0x0001 << 13;
|
||||||
|
const L = 0x0001 << 14;
|
||||||
|
const M = 0x0001 << 15;
|
||||||
|
const DOT = 0x0001 << 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SixteenSegmentFont(BTreeMap<char, SixteenSegmentGlyph>);
|
||||||
|
|
||||||
|
impl SixteenSegmentFont {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut font = BTreeMap::new();
|
||||||
|
font.insert(' ', SixteenSegmentGlyph::NONE);
|
||||||
|
font.insert(
|
||||||
|
'!',
|
||||||
|
SixteenSegmentGlyph::B | SixteenSegmentGlyph::C | SixteenSegmentGlyph::DOT,
|
||||||
|
);
|
||||||
|
font.insert('"', SixteenSegmentGlyph::B | SixteenSegmentGlyph::I);
|
||||||
|
font.insert(
|
||||||
|
'0',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::J
|
||||||
|
| SixteenSegmentGlyph::K,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'1',
|
||||||
|
SixteenSegmentGlyph::B | SixteenSegmentGlyph::C | SixteenSegmentGlyph::J,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'2',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'3',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'4',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'5',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'6',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'7',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'8',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'9',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'A',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'B',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::G2
|
||||||
|
| SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'C',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'D',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'E',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'F',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'G',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'H',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'I',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'J',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'K',
|
||||||
|
SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::J
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'L',
|
||||||
|
SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'M',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::H
|
||||||
|
| SixteenSegmentGlyph::J,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'N',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::H
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'O',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'P',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'Q',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'R',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'S',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'T',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'U',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'V',
|
||||||
|
SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::J
|
||||||
|
| SixteenSegmentGlyph::K,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'W',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::K
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'X',
|
||||||
|
SixteenSegmentGlyph::H
|
||||||
|
| SixteenSegmentGlyph::J
|
||||||
|
| SixteenSegmentGlyph::K
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'Y',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'Z',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::J
|
||||||
|
| SixteenSegmentGlyph::K,
|
||||||
|
);
|
||||||
|
|
||||||
|
font.insert(
|
||||||
|
'a',
|
||||||
|
SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'b',
|
||||||
|
SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'c',
|
||||||
|
SixteenSegmentGlyph::D1 | SixteenSegmentGlyph::E | SixteenSegmentGlyph::G1,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'd',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::G2
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'e',
|
||||||
|
SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::K,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'f',
|
||||||
|
SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2
|
||||||
|
| SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'g',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'h',
|
||||||
|
SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert('i', SixteenSegmentGlyph::L);
|
||||||
|
font.insert(
|
||||||
|
'j',
|
||||||
|
SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'k',
|
||||||
|
SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::J
|
||||||
|
| SixteenSegmentGlyph::L
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert('l', SixteenSegmentGlyph::E | SixteenSegmentGlyph::F);
|
||||||
|
font.insert(
|
||||||
|
'm',
|
||||||
|
SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::L
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'n',
|
||||||
|
SixteenSegmentGlyph::E | SixteenSegmentGlyph::L | SixteenSegmentGlyph::G1,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'o',
|
||||||
|
SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'p',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::I,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'q',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert('r', SixteenSegmentGlyph::E | SixteenSegmentGlyph::G1);
|
||||||
|
font.insert(
|
||||||
|
's',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1
|
||||||
|
| SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
't',
|
||||||
|
SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G1,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'u',
|
||||||
|
SixteenSegmentGlyph::D1 | SixteenSegmentGlyph::E | SixteenSegmentGlyph::L,
|
||||||
|
);
|
||||||
|
font.insert('v', SixteenSegmentGlyph::E | SixteenSegmentGlyph::K);
|
||||||
|
font.insert(
|
||||||
|
'w',
|
||||||
|
SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::K
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'x',
|
||||||
|
SixteenSegmentGlyph::H
|
||||||
|
| SixteenSegmentGlyph::J
|
||||||
|
| SixteenSegmentGlyph::K
|
||||||
|
| SixteenSegmentGlyph::M,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'y',
|
||||||
|
SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::C
|
||||||
|
| SixteenSegmentGlyph::I
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'z',
|
||||||
|
SixteenSegmentGlyph::D1 | SixteenSegmentGlyph::G1 | SixteenSegmentGlyph::K,
|
||||||
|
);
|
||||||
|
font.insert(
|
||||||
|
'@',
|
||||||
|
SixteenSegmentGlyph::A1
|
||||||
|
| SixteenSegmentGlyph::A2
|
||||||
|
| SixteenSegmentGlyph::B
|
||||||
|
| SixteenSegmentGlyph::D1
|
||||||
|
| SixteenSegmentGlyph::D2
|
||||||
|
| SixteenSegmentGlyph::E
|
||||||
|
| SixteenSegmentGlyph::F
|
||||||
|
| SixteenSegmentGlyph::G2,
|
||||||
|
);
|
||||||
|
Self(font)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Font<SixteenSegmentGlyph> for SixteenSegmentFont {
|
||||||
|
fn glyph(&self, c: char) -> &SixteenSegmentGlyph {
|
||||||
|
self.0.get(&c).unwrap_or(&SixteenSegmentGlyph::NONE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Glyph for SixteenSegmentGlyph {
|
||||||
|
fn draw(&self, canvas: &mut impl Canvas, x: usize, y: usize, color: &RGB) {
|
||||||
|
if self.contains(SixteenSegmentGlyph::A1) {
|
||||||
|
canvas.set_pixel(x + 1, y, color);
|
||||||
|
canvas.set_pixel(x + 2, y, color);
|
||||||
|
canvas.set_pixel(x + 3, y, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::A2) {
|
||||||
|
canvas.set_pixel(x + 5, y, color);
|
||||||
|
canvas.set_pixel(x + 6, y, color);
|
||||||
|
canvas.set_pixel(x + 7, y, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::B) {
|
||||||
|
canvas.set_pixel(x + 8, y + 1, color);
|
||||||
|
canvas.set_pixel(x + 8, y + 2, color);
|
||||||
|
canvas.set_pixel(x + 8, y + 3, color);
|
||||||
|
canvas.set_pixel(x + 8, y + 4, color);
|
||||||
|
canvas.set_pixel(x + 8, y + 5, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::C) {
|
||||||
|
canvas.set_pixel(x + 8, y + 7, color);
|
||||||
|
canvas.set_pixel(x + 8, y + 8, color);
|
||||||
|
canvas.set_pixel(x + 8, y + 9, color);
|
||||||
|
canvas.set_pixel(x + 8, y + 10, color);
|
||||||
|
canvas.set_pixel(x + 8, y + 11, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.contains(SixteenSegmentGlyph::D1) {
|
||||||
|
canvas.set_pixel(x + 1, y + 12, color);
|
||||||
|
canvas.set_pixel(x + 2, y + 12, color);
|
||||||
|
canvas.set_pixel(x + 3, y + 12, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::D2) {
|
||||||
|
canvas.set_pixel(x + 5, y + 12, color);
|
||||||
|
canvas.set_pixel(x + 6, y + 12, color);
|
||||||
|
canvas.set_pixel(x + 7, y + 12, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.contains(SixteenSegmentGlyph::E) {
|
||||||
|
canvas.set_pixel(x, y + 7, color);
|
||||||
|
canvas.set_pixel(x, y + 8, color);
|
||||||
|
canvas.set_pixel(x, y + 9, color);
|
||||||
|
canvas.set_pixel(x, y + 10, color);
|
||||||
|
canvas.set_pixel(x, y + 11, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::F) {
|
||||||
|
canvas.set_pixel(x, y + 1, color);
|
||||||
|
canvas.set_pixel(x, y + 2, color);
|
||||||
|
canvas.set_pixel(x, y + 3, color);
|
||||||
|
canvas.set_pixel(x, y + 4, color);
|
||||||
|
canvas.set_pixel(x, y + 5, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.contains(SixteenSegmentGlyph::G1) {
|
||||||
|
canvas.set_pixel(x + 1, y + 6, color);
|
||||||
|
canvas.set_pixel(x + 2, y + 6, color);
|
||||||
|
canvas.set_pixel(x + 3, y + 6, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::G2) {
|
||||||
|
canvas.set_pixel(x + 5, y + 6, color);
|
||||||
|
canvas.set_pixel(x + 6, y + 6, color);
|
||||||
|
canvas.set_pixel(x + 7, y + 6, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.contains(SixteenSegmentGlyph::H) {
|
||||||
|
canvas.set_pixel(x + 1, y + 1, color);
|
||||||
|
canvas.set_pixel(x + 1, y + 2, color);
|
||||||
|
canvas.set_pixel(x + 2, y + 3, color);
|
||||||
|
canvas.set_pixel(x + 3, y + 4, color);
|
||||||
|
canvas.set_pixel(x + 3, y + 5, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::I) {
|
||||||
|
canvas.set_pixel(x + 4, y + 1, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 2, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 3, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 4, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 5, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::J) {
|
||||||
|
canvas.set_pixel(x + 7, y + 1, color);
|
||||||
|
canvas.set_pixel(x + 7, y + 2, color);
|
||||||
|
canvas.set_pixel(x + 6, y + 3, color);
|
||||||
|
canvas.set_pixel(x + 5, y + 4, color);
|
||||||
|
canvas.set_pixel(x + 5, y + 5, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::K) {
|
||||||
|
canvas.set_pixel(x + 3, y + 7, color);
|
||||||
|
canvas.set_pixel(x + 3, y + 8, color);
|
||||||
|
canvas.set_pixel(x + 2, y + 9, color);
|
||||||
|
canvas.set_pixel(x + 1, y + 10, color);
|
||||||
|
canvas.set_pixel(x + 1, y + 11, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::L) {
|
||||||
|
canvas.set_pixel(x + 4, y + 7, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 8, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 9, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 10, color);
|
||||||
|
canvas.set_pixel(x + 4, y + 11, color);
|
||||||
|
}
|
||||||
|
if self.contains(SixteenSegmentGlyph::M) {
|
||||||
|
canvas.set_pixel(x + 5, y + 7, color);
|
||||||
|
canvas.set_pixel(x + 5, y + 8, color);
|
||||||
|
canvas.set_pixel(x + 6, y + 9, color);
|
||||||
|
canvas.set_pixel(x + 7, y + 10, color);
|
||||||
|
canvas.set_pixel(x + 7, y + 11, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extents(&self) -> (usize, usize) {
|
||||||
|
(9, 13)
|
||||||
|
}
|
||||||
|
}
|
184
pico-st7789/src/main.rs
Normal file
184
pico-st7789/src/main.rs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::fmt::format;
|
||||||
|
use embedded_alloc::LlffHeap as Heap;
|
||||||
|
use embedded_hal::{delay::DelayNs, digital::OutputPin};
|
||||||
|
use fugit::RateExtU32;
|
||||||
|
use panic_halt as _;
|
||||||
|
use rp_pico::{
|
||||||
|
entry,
|
||||||
|
hal::{clocks::init_clocks_and_plls, spi::Spi, Clock, Sio, Timer, Watchdog},
|
||||||
|
pac, Pins,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod canvas;
|
||||||
|
use canvas::{Canvas, RGB, print};
|
||||||
|
|
||||||
|
mod font;
|
||||||
|
use font::{BitmapFont, Font, Glyph, SevenSegmentFont, SixteenSegmentFont};
|
||||||
|
|
||||||
|
mod st7789;
|
||||||
|
use st7789::{ST7789Display, SETUP_PROGRAM};
|
||||||
|
|
||||||
|
const XOSC_CRYSTAL_FREQ: u32 = 12_000_000; // MHz, https://forums.raspberrypi.com/viewtopic.php?t=356764
|
||||||
|
|
||||||
|
const ROWS: usize = 320;
|
||||||
|
const COLUMNS: usize = 170;
|
||||||
|
const FRAMEBUF: usize = ROWS * COLUMNS * 3;
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static HEAP: Heap = Heap::empty();
|
||||||
|
|
||||||
|
static mut BUF: [u8; 163200] = [0; 163200];
|
||||||
|
|
||||||
|
pub struct FrameBuf {
|
||||||
|
pub buf: &'static mut [u8; 163200],
|
||||||
|
pub width: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FrameBuf {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
buf: unsafe { &mut BUF },
|
||||||
|
width: 170,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Canvas for FrameBuf {
|
||||||
|
fn set_pixel(&mut self, x: usize, y: usize, color: &RGB) {
|
||||||
|
self.buf[(y * self.width + x) * 3 + 0] = color.r << 2;
|
||||||
|
self.buf[(y * self.width + x) * 3 + 1] = color.g << 2;
|
||||||
|
self.buf[(y * self.width + x) * 3 + 2] = color.b << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
unsafe fn main() -> ! {
|
||||||
|
{
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
const HEAP_SIZE: usize = 64 * 1024;
|
||||||
|
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
|
||||||
|
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
|
||||||
|
}
|
||||||
|
|
||||||
|
let font_bitmap = BitmapFont::new();
|
||||||
|
|
||||||
|
// rp_pico::pac::Peripherals is a reference to physical hardware defined on the Pico.
|
||||||
|
let mut peripherals = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// SIO inidcates "Single Cycle IO". I don't know what this means, but it could mean that this
|
||||||
|
// is a class of IO operations that can be run in a single clock cycle, such as switching a
|
||||||
|
// GPIO pin on or off.
|
||||||
|
let sio = Sio::new(peripherals.SIO);
|
||||||
|
|
||||||
|
// Many of the following systems require a watchdog. I do not know what this does, either, but
|
||||||
|
// it may be some failsafe software that will reset operations if the watchdog detects a lack
|
||||||
|
// of activity.
|
||||||
|
let mut watchdog = Watchdog::new(peripherals.WATCHDOG);
|
||||||
|
|
||||||
|
// Here we grab the GPIO pins in bank 0.
|
||||||
|
let pins = Pins::new(
|
||||||
|
peripherals.IO_BANK0,
|
||||||
|
peripherals.PADS_BANK0,
|
||||||
|
sio.gpio_bank0,
|
||||||
|
&mut peripherals.RESETS,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initialize an abstraction of the clock system with a batch of standard hardware clocks.
|
||||||
|
let clocks = init_clocks_and_plls(
|
||||||
|
XOSC_CRYSTAL_FREQ,
|
||||||
|
peripherals.XOSC,
|
||||||
|
peripherals.CLOCKS,
|
||||||
|
peripherals.PLL_SYS,
|
||||||
|
peripherals.PLL_USB,
|
||||||
|
&mut peripherals.RESETS,
|
||||||
|
&mut watchdog,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// An abstraction for a timer which we can use to delay the code.
|
||||||
|
let mut timer = Timer::new(peripherals.TIMER, &mut peripherals.RESETS, &clocks);
|
||||||
|
|
||||||
|
let mut led = pins.led.into_function();
|
||||||
|
|
||||||
|
// Grab the clock and data pins for SPI1. For Clock pins and for Data pins, there are only two
|
||||||
|
// pins each on the Pico which can function for SPI1.
|
||||||
|
let spi_clk = pins.gpio2.into_function();
|
||||||
|
let spi_sdo = pins.gpio3.into_function();
|
||||||
|
// let spi_sdi = pins.gpio4.into_function();
|
||||||
|
// Chip select 1 means the chip is not enabled
|
||||||
|
let mut board_select = pins.gpio13.into_function();
|
||||||
|
let mut data_command = pins.gpio15.into_function();
|
||||||
|
let mut reset = pins.gpio14.into_function();
|
||||||
|
|
||||||
|
let _ = reset.set_low();
|
||||||
|
let _ = board_select.set_high();
|
||||||
|
let _ = data_command.set_high();
|
||||||
|
|
||||||
|
// Now, create the SPI function abstraction for SPI1 with spi_clk and spi_sdo.
|
||||||
|
let mut spi = Spi::<_, _, _, 8>::new(peripherals.SPI0, (spi_sdo, spi_clk)).init(
|
||||||
|
&mut peripherals.RESETS,
|
||||||
|
// The SPI system uses the peripheral clock
|
||||||
|
clocks.peripheral_clock.freq(),
|
||||||
|
// Transmit data at a rate of 32Mbit.
|
||||||
|
32_u32.MHz(),
|
||||||
|
// Run with SPI Mode 1. This means that the clock line should start high and that data will
|
||||||
|
// be sampled starting at the first falling edge.
|
||||||
|
embedded_hal::spi::MODE_3,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut display = ST7789Display::new(board_select, data_command, spi);
|
||||||
|
|
||||||
|
let _ = reset.set_high();
|
||||||
|
timer.delay_ms(10);
|
||||||
|
for step in SETUP_PROGRAM {
|
||||||
|
let mut display = display.acquire();
|
||||||
|
display.send_command(&step, &mut timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.delay_ms(1000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
let mut framebuf = [0; FRAMEBUF];
|
||||||
|
let mut canvas = Canvas::new(&mut framebuf, COLUMNS);
|
||||||
|
*/
|
||||||
|
let mut canvas = FrameBuf::new();
|
||||||
|
let white = RGB { r: 63, g: 63, b: 63 };
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
canvas.fill(0, 10, 170, 20, &RGB{r: 0, g: 0, b: 0 });
|
||||||
|
print(&mut canvas, &font_bitmap, 1, 10, &format(format_args!("COUNT: {:03}", count)), &RGB{ r: 32, g: 32, b: 63 });
|
||||||
|
print(&mut canvas, &font_bitmap, 1, 200, " !\"#$%&'<>*+,-./", &RGB{ r: 63, g: 63, b: 63 });
|
||||||
|
print(&mut canvas, &font_bitmap, 1, 220, "0123456789|: = ?", &RGB{ r: 63, g: 63, b: 63 });
|
||||||
|
print(&mut canvas, &font_bitmap, 1, 240, "@ABCDEFGHIJKLMNO", &RGB{ r: 63, g: 63, b: 63 });
|
||||||
|
print(&mut canvas, &font_bitmap, 1, 260, "PQRSTUVWXYZ[\\]^_", &RGB{ r: 63, g: 63, b: 63 });
|
||||||
|
print(&mut canvas, &font_bitmap, 1, 280, "`abcdefghijklmno", &RGB{ r: 63, g: 63, b: 63 });
|
||||||
|
print(&mut canvas, &font_bitmap, 1, 300, "pqrstuvwxyz{|}", &RGB{ r: 63, g: 63, b: 63 });
|
||||||
|
|
||||||
|
// canvas.square(10, 70, 160, 310, &white);
|
||||||
|
|
||||||
|
{
|
||||||
|
let display = display.acquire();
|
||||||
|
let _ = led.set_high();
|
||||||
|
timer.delay_ms(100);
|
||||||
|
display.send_buf(canvas.buf);
|
||||||
|
let _ = led.set_low();
|
||||||
|
}
|
||||||
|
count = count + 1;
|
||||||
|
/*
|
||||||
|
for x in 80..90 {
|
||||||
|
for y in 155..165 {
|
||||||
|
draw_pixel(&mut frame, x, y, (0, 0, 63));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
timer.delay_ms(1000);
|
||||||
|
}
|
||||||
|
}
|
204
pico-st7789/src/st7789.rs
Normal file
204
pico-st7789/src/st7789.rs
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
use embedded_hal::{delay::DelayNs, digital::OutputPin, spi::SpiBus};
|
||||||
|
use rp_pico::hal::{
|
||||||
|
gpio::{FunctionSio, Pin, PinId, PullDown, SioOutput},
|
||||||
|
spi::{Enabled, SpiDevice, ValidSpiPinout},
|
||||||
|
Spi, Timer,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Step {
|
||||||
|
param_cnt: usize,
|
||||||
|
command: u8,
|
||||||
|
params: [u8; 4],
|
||||||
|
delay: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Step {
|
||||||
|
pub fn send_command<D, Pinout, P>(
|
||||||
|
&self,
|
||||||
|
spi: &mut Spi<Enabled, D, Pinout, 8>,
|
||||||
|
data_command: &mut Pin<P, FunctionSio<SioOutput>, PullDown>,
|
||||||
|
) where
|
||||||
|
D: SpiDevice,
|
||||||
|
Pinout: ValidSpiPinout<D>,
|
||||||
|
P: PinId,
|
||||||
|
{
|
||||||
|
let _ = data_command.set_low();
|
||||||
|
let _ = spi.write(&[self.command]);
|
||||||
|
if self.param_cnt > 0 {
|
||||||
|
let _ = data_command.set_high();
|
||||||
|
let _ = spi.write(&self.params[0..self.param_cnt]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NOP: u8 = 0x00;
|
||||||
|
|
||||||
|
const SWRESET: Step = Step {
|
||||||
|
param_cnt: 0,
|
||||||
|
command: 0x01,
|
||||||
|
params: [0, 0, 0, 0],
|
||||||
|
delay: Some(150),
|
||||||
|
};
|
||||||
|
const SLPOUT: Step = Step {
|
||||||
|
param_cnt: 0,
|
||||||
|
command: 0x11,
|
||||||
|
params: [0, 0, 0, 0],
|
||||||
|
delay: Some(10),
|
||||||
|
};
|
||||||
|
const COLMOD: u8 = 0x3a;
|
||||||
|
const MADCTL: Step = Step {
|
||||||
|
param_cnt: 1,
|
||||||
|
command: 0x36,
|
||||||
|
params: [0x00, 0, 0, 0],
|
||||||
|
delay: None,
|
||||||
|
};
|
||||||
|
const CASET: u8 = 0x2a;
|
||||||
|
const RASET: u8 = 0x2b;
|
||||||
|
const INVON: Step = Step {
|
||||||
|
param_cnt: 0,
|
||||||
|
command: 0x21,
|
||||||
|
params: [0, 0, 0, 0],
|
||||||
|
delay: Some(10),
|
||||||
|
};
|
||||||
|
const NORON: Step = Step {
|
||||||
|
param_cnt: 0,
|
||||||
|
command: 0x13,
|
||||||
|
params: [0, 0, 0, 0],
|
||||||
|
delay: Some(10),
|
||||||
|
};
|
||||||
|
const DISPOFF: Step = Step {
|
||||||
|
param_cnt: 0,
|
||||||
|
command: 0x28,
|
||||||
|
params: [0, 0, 0, 0],
|
||||||
|
delay: Some(10),
|
||||||
|
};
|
||||||
|
const DISPON: Step = Step {
|
||||||
|
param_cnt: 0,
|
||||||
|
command: 0x29,
|
||||||
|
params: [0, 0, 0, 0],
|
||||||
|
delay: Some(10),
|
||||||
|
};
|
||||||
|
const RAMWR: u8 = 0x2c;
|
||||||
|
|
||||||
|
// Adafruit setup instructions
|
||||||
|
// SWRESET (0x01), 150ms delay
|
||||||
|
// SLPOUT (0x11), 10ms delay
|
||||||
|
// COLMOD (0x3a) 0x55 (65K RGB, 16bit/pixel), 10ms delay
|
||||||
|
// MADCTL (0x36) 0x00,
|
||||||
|
// memory data access control, RGB
|
||||||
|
// CASET 0x00, 0, 0, 170,
|
||||||
|
// column address set, 4 parameters
|
||||||
|
// 0x00, 0x00 indicates xstart is 0
|
||||||
|
// 0x00, 170 indicates xend is 170
|
||||||
|
// RASET 0x00, 0, 320 >> 8, 320 & 0xFF,
|
||||||
|
// row address set, 4 parameters
|
||||||
|
// 0x00, 0x00 indicates ystart is 0
|
||||||
|
// 3230 >> 8, 320 & 0xff indicates that 320 is the last y address
|
||||||
|
// INVON, 10ms delay
|
||||||
|
// invert the display
|
||||||
|
// NORON, 10ms delay
|
||||||
|
// normal display mode
|
||||||
|
// DISPON, 10ms delay
|
||||||
|
// turn the display on
|
||||||
|
|
||||||
|
pub const SETUP_PROGRAM: [Step; 8] = [
|
||||||
|
SWRESET,
|
||||||
|
SLPOUT,
|
||||||
|
Step {
|
||||||
|
param_cnt: 1,
|
||||||
|
command: COLMOD,
|
||||||
|
params: [0x66, 0, 0, 0],
|
||||||
|
delay: Some(10),
|
||||||
|
},
|
||||||
|
MADCTL,
|
||||||
|
Step {
|
||||||
|
param_cnt: 4,
|
||||||
|
command: CASET,
|
||||||
|
params: [0, 35, 0, 204],
|
||||||
|
delay: None,
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
Step {
|
||||||
|
param_cnt: 4,
|
||||||
|
command: RASET,
|
||||||
|
params: [0, 0, (320 >> 8) as u8, (320 & 0xff) as u8],
|
||||||
|
delay: None,
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
INVON,
|
||||||
|
NORON,
|
||||||
|
DISPON,
|
||||||
|
];
|
||||||
|
|
||||||
|
pub struct ST7789Display<
|
||||||
|
BoardSelectId: PinId,
|
||||||
|
DataCommandId: PinId,
|
||||||
|
D: SpiDevice,
|
||||||
|
Pinout: ValidSpiPinout<D>,
|
||||||
|
> {
|
||||||
|
inner: ST7789DisplayEnabled<BoardSelectId, DataCommandId, D, Pinout>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<BoardSelectId: PinId, DataCommandId: PinId, D: SpiDevice, Pinout: ValidSpiPinout<D>>
|
||||||
|
ST7789Display<BoardSelectId, DataCommandId, D, Pinout>
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
board_select: Pin<BoardSelectId, FunctionSio<SioOutput>, PullDown>,
|
||||||
|
data_command: Pin<DataCommandId, FunctionSio<SioOutput>, PullDown>,
|
||||||
|
spi: Spi<Enabled, D, Pinout, 8>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: ST7789DisplayEnabled {
|
||||||
|
board_select,
|
||||||
|
data_command,
|
||||||
|
spi,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acquire(
|
||||||
|
&mut self,
|
||||||
|
) -> &mut ST7789DisplayEnabled<BoardSelectId, DataCommandId, D, Pinout> {
|
||||||
|
self.inner.board_select.set_low();
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ST7789DisplayEnabled<
|
||||||
|
BoardSelectId: PinId,
|
||||||
|
DataCommandId: PinId,
|
||||||
|
D: SpiDevice,
|
||||||
|
Pinout: ValidSpiPinout<D>,
|
||||||
|
> {
|
||||||
|
board_select: Pin<BoardSelectId, FunctionSio<SioOutput>, PullDown>,
|
||||||
|
data_command: Pin<DataCommandId, FunctionSio<SioOutput>, PullDown>,
|
||||||
|
spi: Spi<Enabled, D, Pinout, 8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<BoardSelectId: PinId, DataCommandId: PinId, D: SpiDevice, Pinout: ValidSpiPinout<D>>
|
||||||
|
ST7789DisplayEnabled<BoardSelectId, DataCommandId, D, Pinout>
|
||||||
|
{
|
||||||
|
pub fn send_command(&mut self, step: &Step, timer: &mut Timer) {
|
||||||
|
step.send_command(&mut self.spi, &mut self.data_command);
|
||||||
|
if let Some(delay) = step.delay {
|
||||||
|
timer.delay_ms(delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_buf(&mut self, frame: &[u8]) {
|
||||||
|
// let _ = DISPOFF.send_command(&mut self.spi, &mut self.data_command);
|
||||||
|
let _ = self.data_command.set_low();
|
||||||
|
let _ = self.spi.write(&[RAMWR]);
|
||||||
|
let _ = self.data_command.set_high();
|
||||||
|
let _ = self.spi.write(&frame);
|
||||||
|
// let _ = DISPON.send_command(&mut self.spi, &mut self.data_command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<BoardSelectId: PinId, DataCommandId: PinId, D: SpiDevice, Pinout: ValidSpiPinout<D>> Drop
|
||||||
|
for ST7789DisplayEnabled<BoardSelectId, DataCommandId, D, Pinout>
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.board_select.set_high();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.81.0"
|
channel = "1.85.0"
|
||||||
targets = [ "wasm32-unknown-unknown", "thumbv6m-none-eabi" ]
|
targets = [ "wasm32-unknown-unknown", "thumbv6m-none-eabi" ]
|
||||||
components = [ "rustfmt", "rust-analyzer", "clippy" ]
|
components = [ "rustfmt", "rust-analyzer", "clippy" ]
|
||||||
|
@ -4,12 +4,3 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = { version = "0.8.1", features = ["macros"] }
|
|
||||||
visions-types = { path = "../types" }
|
|
||||||
serde = { version = "1.0.217", features = ["derive", "serde_derive"] }
|
|
||||||
tokio = { version = "1.43.0", features = ["full", "rt"] }
|
|
||||||
tower-http = { version = "0.6.2", features = ["cors"] }
|
|
||||||
typeshare = "1.0.4"
|
|
||||||
uuid = { version = "1.13.1", features = ["v4"] }
|
|
||||||
result-extended = { path = "../../result-extended" }
|
|
||||||
thiserror = "2.0.11"
|
|
||||||
|
@ -1,155 +1,3 @@
|
|||||||
use std::future::Future;
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
use axum::{
|
|
||||||
http::{
|
|
||||||
header::{AUTHORIZATION, CONTENT_TYPE},
|
|
||||||
HeaderMap, Method, StatusCode,
|
|
||||||
},
|
|
||||||
routing::{get, post},
|
|
||||||
Json, Router,
|
|
||||||
};
|
|
||||||
use visions_types::{AccountStatus, AuthRequest, AuthResponse, SessionId, UserOverview};
|
|
||||||
use result_extended::{error, ok, ResultExt};
|
|
||||||
use thiserror::Error;
|
|
||||||
use tower_http::cors::{Any, CorsLayer};
|
|
||||||
|
|
||||||
#[axum::debug_handler]
|
|
||||||
async fn check_password(
|
|
||||||
request: Json<AuthRequest>,
|
|
||||||
) -> (StatusCode, Json<Option<AuthResponse>>) {
|
|
||||||
let Json(request) = request;
|
|
||||||
if request.username == "vakarian" && request.password == "aoeu" {
|
|
||||||
(
|
|
||||||
StatusCode::OK,
|
|
||||||
Json(Some(AuthResponse::Success("vakarian-session-id".into()))),
|
|
||||||
)
|
|
||||||
} else if request.username == "shephard" && request.password == "aoeu" {
|
|
||||||
(
|
|
||||||
StatusCode::OK,
|
|
||||||
Json(Some(AuthResponse::PasswordReset(
|
|
||||||
"shephard-session-id".into(),
|
|
||||||
))),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(StatusCode::UNAUTHORIZED, Json(None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
enum AppError {
|
|
||||||
#[error("no user authorized")]
|
|
||||||
Unauthorized,
|
|
||||||
#[error("bad request")]
|
|
||||||
BadRequest,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
enum FatalError {
|
|
||||||
#[error("on unknown fatal error occurred")]
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl result_extended::FatalError for FatalError {}
|
|
||||||
|
|
||||||
fn parse_session_header(headers: HeaderMap) -> ResultExt<Option<SessionId>, AppError, FatalError> {
|
|
||||||
match headers.get("Authorization") {
|
|
||||||
Some(token) => {
|
|
||||||
match token
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.split(" ")
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.as_slice()
|
|
||||||
{
|
|
||||||
[_schema, token] => ok(Some(SessionId::from(*token))),
|
|
||||||
_ => error(AppError::BadRequest),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn auth_required<B, F, Fut>(headers: HeaderMap, f: F) -> (StatusCode, Json<Option<B>>)
|
|
||||||
where
|
|
||||||
F: Fn() -> Fut,
|
|
||||||
Fut: Future<Output = (StatusCode, B)>,
|
|
||||||
{
|
|
||||||
match parse_session_header(headers) {
|
|
||||||
ResultExt::Ok(Some(session_id)) => {
|
|
||||||
if session_id == "vakarian-session-id".into() {
|
|
||||||
let (code, result) = f().await;
|
|
||||||
(code, Json(Some(result)))
|
|
||||||
} else {
|
|
||||||
(StatusCode::UNAUTHORIZED, Json(None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ResultExt::Ok(None) => (StatusCode::UNAUTHORIZED, Json(None)),
|
|
||||||
ResultExt::Err(AppError::Unauthorized) => (StatusCode::UNAUTHORIZED, Json(None)),
|
|
||||||
ResultExt::Err(AppError::BadRequest) => (StatusCode::BAD_REQUEST, Json(None)),
|
|
||||||
ResultExt::Fatal(err) => {
|
|
||||||
panic!("{}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
let app = Router::new()
|
|
||||||
.route(
|
|
||||||
"/api/test/health",
|
|
||||||
get(|| async { (StatusCode::OK, Json(None::<String>)) }).layer(
|
|
||||||
CorsLayer::new()
|
|
||||||
.allow_methods([Method::GET])
|
|
||||||
.allow_origin(Any),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/test/auth",
|
|
||||||
post(|req: Json<AuthRequest>| check_password(req)).layer(
|
|
||||||
CorsLayer::new()
|
|
||||||
.allow_methods([Method::POST])
|
|
||||||
.allow_headers([CONTENT_TYPE])
|
|
||||||
.allow_origin(Any),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/api/test/list-users",
|
|
||||||
get(|headers: HeaderMap| {
|
|
||||||
auth_required(headers, || async {
|
|
||||||
(
|
|
||||||
StatusCode::OK,
|
|
||||||
Some(vec![
|
|
||||||
UserOverview {
|
|
||||||
id: "vakarian-id".into(),
|
|
||||||
name: "vakarian".to_owned(),
|
|
||||||
status: AccountStatus::Ok,
|
|
||||||
},
|
|
||||||
UserOverview {
|
|
||||||
id: "shephard-id".into(),
|
|
||||||
name: "shephard".to_owned(),
|
|
||||||
status: AccountStatus::PasswordReset(
|
|
||||||
"2050-01-01 00:00:00".to_owned(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
UserOverview {
|
|
||||||
id: "tali-id".into(),
|
|
||||||
name: "tali".to_owned(),
|
|
||||||
status: AccountStatus::Locked,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.layer(
|
|
||||||
CorsLayer::new()
|
|
||||||
.allow_headers([AUTHORIZATION])
|
|
||||||
.allow_methods([Method::GET])
|
|
||||||
.allow_origin(Any),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:8001")
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
axum::serve(listener, app).await.unwrap();
|
|
||||||
}
|
}
|
||||||
|
2
visions/types/.gitignore
vendored
2
visions/types/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
gen/
|
|
||||||
dist/
|
|
@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "visions-types"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
serde = { version = "1.0.218", features = ["derive"] }
|
|
||||||
uuid = { version = "1.13.2", features = ["v4", "js"] }
|
|
8
visions/types/Taskfile.yml
Normal file
8
visions/types/Taskfile.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build:
|
||||||
|
cmds:
|
||||||
|
- npm install typescript
|
||||||
|
- typeshare --lang typescript --output-file visions.ts ../server/src
|
||||||
|
- npx tsc
|
14
visions/types/package.json
Normal file
14
visions/types/package.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "visions-types",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Shared data types for Visions",
|
||||||
|
"main": "visions.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
}
|
||||||
|
}
|
@ -1,81 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
#[serde(tag = "type", content = "content", rename_all = "kebab-case")]
|
|
||||||
pub enum AccountStatus {
|
|
||||||
Ok,
|
|
||||||
PasswordReset(String),
|
|
||||||
Locked,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct AuthRequest {
|
|
||||||
pub username: String,
|
|
||||||
pub password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
|
||||||
pub struct SessionId(String);
|
|
||||||
|
|
||||||
impl SessionId {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(format!("{}", Uuid::new_v4().hyphenated()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for SessionId {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
Self(s.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for SessionId {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
Self(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
|
||||||
pub struct UserId(String);
|
|
||||||
|
|
||||||
impl UserId {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(format!("{}", Uuid::new_v4().hyphenated()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_str(&self) -> &str {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for UserId {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
Self(s.to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for UserId {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
Self(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct UserOverview {
|
|
||||||
pub id: UserId,
|
|
||||||
pub name: String,
|
|
||||||
pub status: AccountStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
#[serde(tag = "type", content = "content", rename_all = "kebab-case")]
|
|
||||||
pub enum AuthResponse {
|
|
||||||
Success(SessionId),
|
|
||||||
PasswordReset(SessionId),
|
|
||||||
}
|
|
||||||
|
|
15
visions/types/tsconfig.json
Normal file
15
visions/types/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2016",
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["./visions.ts"]
|
||||||
|
}
|
23
visions/ui/.gitignore
vendored
Normal file
23
visions/ui/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
@ -1,17 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "visions-client"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
visions-types = { path = "../types" }
|
|
||||||
gloo-console = "0.3.0"
|
|
||||||
gloo-net = "0.6.0"
|
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
|
||||||
serde-wasm-bindgen = "0.6.5"
|
|
||||||
serde_json = "1.0.138"
|
|
||||||
wasm-bindgen = "0.2.100"
|
|
||||||
wasm-bindgen-futures = "0.4.50"
|
|
||||||
web-sys = "0.3.77"
|
|
||||||
yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] }
|
|
||||||
|
|
14
visions/ui/Taskfile.yml
Normal file
14
visions/ui/Taskfile.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
dev:
|
||||||
|
cmds:
|
||||||
|
- cd ../visions-types && task build
|
||||||
|
- npm install
|
||||||
|
- npm run start
|
||||||
|
|
||||||
|
test:
|
||||||
|
cmds:
|
||||||
|
- cd ../visions-types && task build
|
||||||
|
- npm install
|
||||||
|
- npm run test
|
@ -1,3 +0,0 @@
|
|||||||
[[proxy]]
|
|
||||||
backend = "http://localhost:8001/api"
|
|
||||||
insecure = true
|
|
@ -1,36 +0,0 @@
|
|||||||
:root {
|
|
||||||
--spacing-s: 4px;
|
|
||||||
--spacing-m: 8px;
|
|
||||||
--shadow-shallow: 2px 2px 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: hsl(0, 0%, 95%);
|
|
||||||
font-family: Ariel, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: space-between;
|
|
||||||
border: 1px solid black;
|
|
||||||
box-shadow: var(--shadow-shallow);
|
|
||||||
border-radius: var(--spacing-s);
|
|
||||||
padding: var(--spacing-m);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card > h1 {
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card > * {
|
|
||||||
margin-top: var(--spacing-s);
|
|
||||||
margin-bottom: var(--spacing-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Visions Client Demo</title>
|
|
||||||
<link data-trunk rel="css" href="design.css" />
|
|
||||||
</head>
|
|
||||||
<body></body>
|
|
||||||
</html>
|
|
51
visions/ui/package.json
Normal file
51
visions/ui/package.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "ui",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
|
"@testing-library/react": "^16.2.0",
|
||||||
|
"@testing-library/user-event": "^14.6.1",
|
||||||
|
"@types/jest": "^27.5.2",
|
||||||
|
"@types/node": "^16.18.119",
|
||||||
|
"@types/react": "^18.3.12",
|
||||||
|
"@types/react-dom": "^18.3.1",
|
||||||
|
"@types/react-router": "^5.1.20",
|
||||||
|
"@types/react-router-dom": "^5.3.3",
|
||||||
|
"classnames": "^2.5.1",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"react-router": "^6.28.0",
|
||||||
|
"react-router-dom": "^6.28.0",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
|
"react-use-websocket": "^4.11.1",
|
||||||
|
"typescript": "^4.9.5",
|
||||||
|
"visions-types": "../visions-types",
|
||||||
|
"web-vitals": "^2.1.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
use std::future::Future;
|
|
||||||
|
|
||||||
use gloo_console::log;
|
|
||||||
use gloo_net::http::{Request, Response};
|
|
||||||
use visions_types::{AuthRequest, AuthResponse, SessionId, UserOverview};
|
|
||||||
|
|
||||||
pub enum ClientError {
|
|
||||||
Unauthorized,
|
|
||||||
Err(u16),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Client {
|
|
||||||
fn auth(&self, username: String, password: String) -> impl Future<Output = Result<AuthResponse, ClientError>>;
|
|
||||||
fn list_users(&self, session_id: SessionId) -> impl Future<Output = Result<Vec<UserOverview>, ClientError>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Connection;
|
|
||||||
|
|
||||||
impl Connection {
|
|
||||||
pub fn new() -> Self { Self }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Client for Connection {
|
|
||||||
async fn auth(&self, username: String, password: String) -> Result<AuthResponse, ClientError> {
|
|
||||||
log!("authenticating: ", &username, &password);
|
|
||||||
let response: Response = Request::post("/api/test/auth")
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.body(serde_wasm_bindgen::to_value(&serde_json::to_string(&AuthRequest{ username, password }).unwrap()).unwrap())
|
|
||||||
.unwrap()
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if response.ok() {
|
|
||||||
Ok(serde_json::from_slice(&response.binary().await.unwrap()).unwrap())
|
|
||||||
} else {
|
|
||||||
Err(ClientError::Err(response.status()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn list_users(&self, session_id: SessionId) -> Result<Vec<UserOverview>, ClientError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use gloo_console::log;
|
|
||||||
use visions_types::AuthResponse;
|
|
||||||
use wasm_bindgen::JsCast;
|
|
||||||
use web_sys::HtmlInputElement;
|
|
||||||
use yew::prelude::*;
|
|
||||||
|
|
||||||
mod client;
|
|
||||||
use client::*;
|
|
||||||
|
|
||||||
struct AuthInfo {
|
|
||||||
session_id: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AuthInfo {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { session_id: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AuthAction {
|
|
||||||
Auth(String),
|
|
||||||
Unauth,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Reducible for AuthInfo {
|
|
||||||
type Action = AuthAction;
|
|
||||||
|
|
||||||
fn reduce(self: Rc<Self>, action: Self::Action) -> Rc<Self> {
|
|
||||||
// log!("reduce", action);
|
|
||||||
match action {
|
|
||||||
AuthAction::Auth(session_id) => Self {
|
|
||||||
session_id: Some(session_id),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
AuthAction::Unauth => Self { session_id: None }.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
|
||||||
struct LoginProps {
|
|
||||||
on_login: Callback<(String, String)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component]
|
|
||||||
fn Login(LoginProps { on_login }: &LoginProps) -> Html {
|
|
||||||
let username = use_state(|| "".to_owned());
|
|
||||||
let password = use_state(|| "".to_owned());
|
|
||||||
|
|
||||||
let on_click = {
|
|
||||||
let on_login = on_login.clone();
|
|
||||||
let username = username.clone();
|
|
||||||
let password = password.clone();
|
|
||||||
Callback::from(move |_| on_login.emit((username.to_string(), password.to_string())))
|
|
||||||
};
|
|
||||||
|
|
||||||
let on_username_changed = {
|
|
||||||
let username = username.clone();
|
|
||||||
Callback::from(move |event: Event| {
|
|
||||||
let input = event
|
|
||||||
.target()
|
|
||||||
.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
|
|
||||||
if let Some(input) = input {
|
|
||||||
username.set(input.value());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let on_password_changed = {
|
|
||||||
let password = password.clone();
|
|
||||||
Callback::from(move |event: Event| {
|
|
||||||
let input = event
|
|
||||||
.target()
|
|
||||||
.and_then(|t| t.dyn_into::<HtmlInputElement>().ok());
|
|
||||||
if let Some(input) = input {
|
|
||||||
password.set(input.value());
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
html! {
|
|
||||||
<div class="login-form">
|
|
||||||
<div class="card">
|
|
||||||
<h1>{"Welcome to Visions VTT"}</h1>
|
|
||||||
<input type="text" name="username" placeholder="username" onchange={on_username_changed} />
|
|
||||||
<input type="password" name="password" placeholder="password" onchange={on_password_changed} />
|
|
||||||
<button onclick={on_click}>{"Login"}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component]
|
|
||||||
fn App() -> Html {
|
|
||||||
let auth_info = use_reducer(AuthInfo::default);
|
|
||||||
|
|
||||||
let on_login = {
|
|
||||||
let auth_info = auth_info.clone();
|
|
||||||
Callback::from(move |(username, password)| {
|
|
||||||
let auth_info = auth_info.clone();
|
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
|
||||||
let client = Connection::new();
|
|
||||||
match client.auth(username, password).await {
|
|
||||||
Ok(AuthResponse::Success(session_id)) => {
|
|
||||||
auth_info.dispatch(AuthAction::Auth(session_id.as_str().to_owned()))
|
|
||||||
}
|
|
||||||
Ok(AuthResponse::PasswordReset(session_id)) => {
|
|
||||||
auth_info.dispatch(AuthAction::Auth(session_id.as_str().to_owned()))
|
|
||||||
}
|
|
||||||
Err(ClientError::Unauthorized) => todo!(),
|
|
||||||
Err(ClientError::Err(status)) => todo!(),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
if auth_info.session_id.is_some() {
|
|
||||||
html! { <p>{ "this is just a thing" }</p> }
|
|
||||||
} else {
|
|
||||||
html! { <Login on_login={on_login.clone()} /> }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
yew::Renderer::<App>::new().render();
|
|
||||||
}
|
|
27
visions/ui/tsconfig.json
Normal file
27
visions/ui/tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src",
|
||||||
|
"gen"
|
||||||
|
]
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "yew-app"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
gloo-net = "0.6.0"
|
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
|
||||||
wasm-bindgen-futures = "0.4.50"
|
|
||||||
yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] }
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
body {
|
|
||||||
background-color: hsl(0, 0%, 50%);
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head></head>
|
|
||||||
<body></body>
|
|
||||||
</html>
|
|
@ -1,126 +0,0 @@
|
|||||||
use yew::prelude::*;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use gloo_net::http::Request;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Deserialize)]
|
|
||||||
struct Video {
|
|
||||||
id: usize,
|
|
||||||
title: String,
|
|
||||||
speaker: String,
|
|
||||||
url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
|
||||||
struct VideosListProps {
|
|
||||||
videos: Vec<Video>,
|
|
||||||
on_click: Callback<Video>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
fn videos() -> Vec<Video> {
|
|
||||||
vec![
|
|
||||||
Video {
|
|
||||||
id: 1,
|
|
||||||
title: "Building and breaking things".to_string(),
|
|
||||||
speaker: "John Doe".to_string(),
|
|
||||||
url: "https://youtu.be/PsaFVLr8t4E".to_string(),
|
|
||||||
},
|
|
||||||
Video {
|
|
||||||
id: 2,
|
|
||||||
title: "The development process".to_string(),
|
|
||||||
speaker: "Jane Smith".to_string(),
|
|
||||||
url: "https://youtu.be/PsaFVLr8t4E".to_string(),
|
|
||||||
},
|
|
||||||
Video {
|
|
||||||
id: 3,
|
|
||||||
title: "The Web 7.0".to_string(),
|
|
||||||
speaker: "Matt Miller".to_string(),
|
|
||||||
url: "https://youtu.be/PsaFVLr8t4E".to_string(),
|
|
||||||
},
|
|
||||||
Video {
|
|
||||||
id: 4,
|
|
||||||
title: "Mouseless development".to_string(),
|
|
||||||
speaker: "Tom Jerry".to_string(),
|
|
||||||
url: "https://youtu.be/PsaFVLr8t4E".to_string(),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[function_component(VideosList)]
|
|
||||||
fn videos_list(VideosListProps { videos, on_click }: &VideosListProps) -> Html {
|
|
||||||
let on_click = on_click.clone();
|
|
||||||
videos
|
|
||||||
.iter()
|
|
||||||
.map(|video| {
|
|
||||||
let on_video_select = {
|
|
||||||
let on_click = on_click.clone();
|
|
||||||
let video = video.clone();
|
|
||||||
Callback::from(move |_| on_click.emit(video.clone()))
|
|
||||||
};
|
|
||||||
html! {
|
|
||||||
<p key={video.id} onclick={on_video_select}>{format!("{}: {}", video.speaker, video.title)}</p>
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
|
||||||
struct VideosDetailsProps {
|
|
||||||
video: Video,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component(VideoDetails)]
|
|
||||||
fn video_details(VideosDetailsProps { video }: &VideosDetailsProps) -> Html {
|
|
||||||
html! {
|
|
||||||
<div>
|
|
||||||
<h3>{video.title.clone()}</h3>
|
|
||||||
<img src="https://via.placeholder.com/640x360.png?text=Video+Player+Placeholder" alt="video thumbnail" />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[function_component(App)]
|
|
||||||
fn app() -> Html {
|
|
||||||
let videos = use_state(|| vec![]);
|
|
||||||
{
|
|
||||||
let videos = videos.clone();
|
|
||||||
use_effect_with((), move |_| {
|
|
||||||
let videos = videos.clone();
|
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
|
||||||
let response = Request::get("/tutorial/data.json").send().await;
|
|
||||||
println!("response: {:?}", response);
|
|
||||||
let response = response.unwrap();
|
|
||||||
let fetched_videos: Vec<Video> = response.json().await.unwrap();
|
|
||||||
videos.set(fetched_videos);
|
|
||||||
});
|
|
||||||
|| ()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let selected_video = use_state(|| None);
|
|
||||||
|
|
||||||
let on_video_select = {
|
|
||||||
let selected_video = selected_video.clone();
|
|
||||||
Callback::from(move |video: Video| selected_video.set(Some(video)))
|
|
||||||
};
|
|
||||||
|
|
||||||
let details = selected_video.as_ref().map(|video| html! {
|
|
||||||
<VideoDetails video={video.clone()} />
|
|
||||||
});
|
|
||||||
|
|
||||||
html! {
|
|
||||||
<>
|
|
||||||
<h1>{ "RustConf Explorer" }</h1>
|
|
||||||
<div>
|
|
||||||
<h3>{"Videos to watch"}</h3>
|
|
||||||
<VideosList videos={(*videos).clone()} on_click={on_video_select.clone()} />
|
|
||||||
</div>
|
|
||||||
{ for details }
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
yew::Renderer::<App>::new().render();
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user