From a100c9be524419b35cbf6f419c7094411b6deddd Mon Sep 17 00:00:00 2001 From: mtgmonkey Date: Tue, 8 Jul 2025 07:53:14 -0400 Subject: [PATCH] cli arguments, clippy passes --- .gitignore | 2 + Cargo.lock | 821 ++++------------------------------------------------ Cargo.toml | 21 +- flake.lock | 6 +- flake.nix | 11 +- package.nix | 3 + src/lib.rs | 574 ++++++++++++++++++++++++++---------- src/main.rs | 48 ++- 8 files changed, 528 insertions(+), 958 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4075fb2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +result diff --git a/Cargo.lock b/Cargo.lock index f1aab20..3b16745 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "ab_glyph" -version = "0.2.29" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +checksum = "1e0f4f6fbdc5ee39f2ede9f5f3ec79477271a6d6a2baff22310d51736bda6cea" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -14,26 +14,9 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.16", - "once_cell", - "version_check", -] +checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" [[package]] name = "ahash" @@ -42,7 +25,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.3", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -126,18 +109,6 @@ dependencies = [ "libloading 0.7.4", ] -[[package]] -name = "async-broadcast" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" -dependencies = [ - "event-listener 5.4.0", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - [[package]] name = "async-channel" version = "1.9.0" @@ -151,9 +122,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -187,17 +158,6 @@ dependencies = [ "futures-lite 1.13.0", ] -[[package]] -name = "async-fs" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" -dependencies = [ - "async-lock 3.4.0", - "blocking", - "futures-lite 2.6.0", -] - [[package]] name = "async-io" version = "1.13.0" @@ -285,36 +245,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "async-process" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" -dependencies = [ - "async-channel 2.3.1", - "async-io 2.4.1", - "async-lock 3.4.0", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener 5.4.0", - "futures-lite 2.6.0", - "rustix 1.0.7", - "tracing", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "async-signal" version = "0.2.11" @@ -339,17 +269,6 @@ version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" -[[package]] -name = "async-trait" -version = "0.1.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "atomic-waker" version = "1.1.2" @@ -395,15 +314,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "block2" version = "0.5.1" @@ -415,17 +325,37 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ - "async-channel 2.3.1", + "async-channel 2.5.0", "async-task", "futures-io", "futures-lite 2.6.0", "piper", ] +[[package]] +name = "bpaf" +version = "0.9.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473976d7a8620bb1e06dcdd184407c2363fe4fec8e983ee03ed9197222634a31" +dependencies = [ + "bpaf_derive", +] + +[[package]] +name = "bpaf_derive" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fefb4feeec9a091705938922f26081aad77c64cd2e76cd1c4a9ece8e42e1618a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "bumpalo" version = "3.19.0" @@ -492,9 +422,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.27" +version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" dependencies = [ "jobserver", "libc", @@ -634,16 +564,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -657,21 +577,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.9.1", - "core-foundation 0.10.1", - "core-graphics-types 0.2.0", + "core-foundation", + "core-graphics-types", "foreign-types", "libc", ] @@ -683,18 +590,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.9.1", - "core-foundation 0.10.1", + "core-foundation", "libc", ] @@ -721,24 +617,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -770,22 +648,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctor-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" - [[package]] name = "cursor-icon" version = "1.2.0" @@ -803,64 +665,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dark-light" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a76fa97167fa740dcdbfe18e8895601e1bc36525f09b044e00916e717c03a3c" -dependencies = [ - "dconf_rs", - "detect-desktop-environment", - "dirs", - "objc", - "rust-ini", - "web-sys", - "winreg", - "zbus", -] - -[[package]] -name = "dconf_rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" - -[[package]] -name = "detect-desktop-environment" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d8ad60dd5b13a4ee6bd8fa2d5d88965c597c67bce32b5fc49c94f55cb50810" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dispatch" version = "0.2.0" @@ -876,12 +680,6 @@ dependencies = [ "libloading 0.8.8", ] -[[package]] -name = "dlv-list" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" - [[package]] name = "downcast-rs" version = "1.2.1" @@ -894,78 +692,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" -[[package]] -name = "drm" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" -dependencies = [ - "bitflags 2.9.1", - "bytemuck", - "drm-ffi", - "drm-fourcc", - "rustix 0.38.44", -] - -[[package]] -name = "drm-ffi" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" -dependencies = [ - "drm-sys", - "rustix 0.38.44", -] - -[[package]] -name = "drm-fourcc" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" - -[[package]] -name = "drm-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986" -dependencies = [ - "libc", - "linux-raw-sys 0.6.5", -] - [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "endi" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" - -[[package]] -name = "enumflags2" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -1066,25 +798,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "flate2" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "font-types" version = "0.7.3" @@ -1262,16 +975,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "gethostname" version = "0.4.3" @@ -1282,17 +985,6 @@ dependencies = [ "windows-targets 0.48.5", ] -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.3.3" @@ -1302,7 +994,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi", ] [[package]] @@ -1415,22 +1107,13 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.12", + "ahash", "allocator-api2", ] @@ -1467,12 +1150,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hexf-parse" version = "0.2.1" @@ -1501,7 +1178,6 @@ checksum = "0013a238275494641bf8f1732a23a808196540dc67b22ff97099c044ae4c8a1c" dependencies = [ "bitflags 2.9.1", "bytes", - "dark-light", "glam", "log", "num-traits", @@ -1568,7 +1244,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73558208059f9e622df2bf434e044ee2f838ce75201a023cf0ca3e1244f46c2a" dependencies = [ "iced_graphics", - "iced_tiny_skia", "iced_wgpu", "log", "thiserror", @@ -1587,22 +1262,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "iced_tiny_skia" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c625d368284fcc43b0b36b176f76eff1abebe7959dd58bd8ce6897d641962a50" -dependencies = [ - "bytemuck", - "cosmic-text", - "iced_graphics", - "kurbo", - "log", - "rustc-hash 2.1.1", - "softbuffer", - "tiny-skia", -] - [[package]] name = "iced_wgpu" version = "0.13.5" @@ -1716,7 +1375,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.3", + "getrandom", "libc", ] @@ -1747,16 +1406,6 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" -[[package]] -name = "kurbo" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" -dependencies = [ - "arrayvec", - "smallvec", -] - [[package]] name = "libc" version = "0.2.174" @@ -1812,12 +1461,6 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" -[[package]] -name = "linux-raw-sys" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -1870,15 +1513,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "metal" version = "0.27.0" @@ -1887,23 +1521,13 @@ checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" dependencies = [ "bitflags 2.9.1", "block", - "core-graphics-types 0.1.3", + "core-graphics-types", "foreign-types", "log", "objc", "paste", ] -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - [[package]] name = "naga" version = "0.19.2" @@ -1963,19 +1587,6 @@ dependencies = [ "jni-sys", ] -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "cfg_aliases 0.2.1", - "libc", - "memoffset", -] - [[package]] name = "nix" version = "0.30.1" @@ -2266,26 +1877,6 @@ dependencies = [ "libredox", ] -[[package]] -name = "ordered-multimap" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" -dependencies = [ - "dlv-list", - "hashbrown 0.12.3", -] - -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - [[package]] name = "owned_ttf_parser" version = "0.25.0" @@ -2333,17 +1924,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.11", + "parking_lot_core", ] [[package]] @@ -2360,19 +1941,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "parking_lot_core" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.13", - "smallvec", - "windows-targets 0.52.6", -] - [[package]] name = "paste" version = "1.0.15" @@ -2476,19 +2044,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "polling" version = "2.8.0" @@ -2520,15 +2075,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "presser" version = "0.3.1" @@ -2589,18 +2135,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", "rand_core", ] @@ -2609,9 +2143,6 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] [[package]] name = "range-alloc" @@ -2688,17 +2219,6 @@ dependencies = [ "bitflags 2.9.1", ] -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror", -] - [[package]] name = "renderdoc-sys" version = "1.1.0" @@ -2711,22 +2231,13 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" -[[package]] -name = "rust-ini" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rust_term" version = "0.1.0" dependencies = [ + "bpaf", "iced", - "nix 0.30.1", + "nix", ] [[package]] @@ -2864,28 +2375,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -2901,12 +2390,6 @@ dependencies = [ "libc", ] -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - [[package]] name = "siphasher" version = "1.0.1" @@ -2988,11 +2471,11 @@ checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" dependencies = [ "async-channel 1.9.0", "async-executor", - "async-fs 1.6.0", + "async-fs", "async-io 1.13.0", "async-lock 2.8.0", "async-net", - "async-process 1.8.1", + "async-process", "blocking", "futures-lite 1.13.0", ] @@ -3016,38 +2499,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "softbuffer" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" -dependencies = [ - "as-raw-xcb-connection", - "bytemuck", - "cfg_aliases 0.2.1", - "core-graphics 0.24.0", - "drm", - "fastrand 2.3.0", - "foreign-types", - "js-sys", - "log", - "memmap2", - "objc2", - "objc2-foundation", - "objc2-quartz-core", - "raw-window-handle", - "redox_syscall 0.5.13", - "rustix 0.38.44", - "tiny-xlib", - "wasm-bindgen", - "wayland-backend", - "wayland-client", - "wayland-sys", - "web-sys", - "windows-sys 0.59.0", - "x11rb", -] - [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -3117,19 +2568,6 @@ dependencies = [ "libc", ] -[[package]] -name = "tempfile" -version = "3.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" -dependencies = [ - "fastrand 2.3.0", - "getrandom 0.3.3", - "once_cell", - "rustix 1.0.7", - "windows-sys 0.59.0", -] - [[package]] name = "termcolor" version = "1.4.1" @@ -3170,7 +2608,6 @@ dependencies = [ "bytemuck", "cfg-if", "log", - "png", "tiny-skia-path", ] @@ -3185,19 +2622,6 @@ dependencies = [ "strict-num", ] -[[package]] -name = "tiny-xlib" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" -dependencies = [ - "as-raw-xcb-connection", - "ctor-lite", - "libloading 0.8.8", - "pkg-config", - "tracing", -] - [[package]] name = "tinyvec" version = "1.9.0" @@ -3279,23 +2703,6 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -[[package]] -name = "typenum" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" - -[[package]] -name = "uds_windows" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" -dependencies = [ - "memoffset", - "tempfile", - "winapi", -] - [[package]] name = "unicode-bidi" version = "0.3.18" @@ -3378,12 +2785,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" @@ -3472,7 +2873,7 @@ checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ "futures", "js-sys", - "parking_lot 0.11.2", + "parking_lot", "pin-utils", "wasm-bindgen", "wasm-bindgen-futures", @@ -3620,7 +3021,7 @@ dependencies = [ "js-sys", "log", "naga", - "parking_lot 0.12.4", + "parking_lot", "profiling", "raw-window-handle", "smallvec", @@ -3648,7 +3049,7 @@ dependencies = [ "log", "naga", "once_cell", - "parking_lot 0.12.4", + "parking_lot", "profiling", "raw-window-handle", "rustc-hash 1.1.0", @@ -3672,7 +3073,7 @@ dependencies = [ "bitflags 2.9.1", "block", "cfg_aliases 0.1.1", - "core-graphics-types 0.1.3", + "core-graphics-types", "d3d12", "glow", "glutin_wgl_sys", @@ -3690,7 +3091,7 @@ dependencies = [ "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", - "parking_lot 0.12.4", + "parking_lot", "profiling", "range-alloc", "raw-window-handle", @@ -4078,7 +3479,7 @@ version = "0.30.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4409c10174df8779dc29a4788cac85ed84024ccbc1743b776b21a520ee1aaf4" dependencies = [ - "ahash 0.8.12", + "ahash", "android-activity", "atomic-waker", "bitflags 2.9.1", @@ -4087,8 +3488,8 @@ dependencies = [ "calloop", "cfg_aliases 0.2.1", "concurrent-queue", - "core-foundation 0.9.4", - "core-graphics 0.23.2", + "core-foundation", + "core-graphics", "cursor-icon", "dpi", "js-sys", @@ -4133,15 +3534,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -4189,16 +3581,6 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -4230,68 +3612,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" -[[package]] -name = "zbus" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs 2.1.2", - "async-io 2.4.1", - "async-lock 3.4.0", - "async-process 2.3.1", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener 5.4.0", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix 0.29.0", - "ordered-stream", - "rand", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.52.0", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.104", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" -dependencies = [ - "serde", - "static_assertions", - "zvariant", -] - [[package]] name = "zeno" version = "0.2.3" @@ -4317,40 +3637,3 @@ dependencies = [ "quote", "syn 2.0.104", ] - -[[package]] -name = "zvariant" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" -dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "zvariant_derive", -] - -[[package]] -name = "zvariant_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.104", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.104", -] diff --git a/Cargo.toml b/Cargo.toml index 1637e50..a4df731 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,23 @@ edition = "2024" [dependencies] nix = { version = "0.30.1", features = ["term", "process", "fs"], default-features = false } -iced = { version = "0.13.1", features = ["advanced", "smol"] } +iced = { version = "0.13.1", features = ["advanced", "smol", "wgpu"], default-features = false } +bpaf = { version = "0.9.20", features = ["derive"], default-features = false } + +[lints.clippy] +cargo = "deny" +complexity = "deny" +correctness = "deny" +nursery = "deny" +pedantic = "deny" +perf = "deny" +restriction = "deny" +style = "deny" +suspicious = "deny" + +[profile.release] +strip = true +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" diff --git a/flake.lock b/flake.lock index c8ab69c..f06ce6d 100644 --- a/flake.lock +++ b/flake.lock @@ -22,11 +22,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1751271578, - "narHash": "sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU+tt4YY=", + "lastModified": 1751792365, + "narHash": "sha256-J1kI6oAj25IG4EdVlg2hQz8NZTBNYvIS0l4wpr9KcUo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3016b4b15d13f3089db8a41ef937b13a9e33a8df", + "rev": "1fd8bada0b6117e6c7eb54aad5813023eed37ccb", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 18ee5f3..ac12441 100644 --- a/flake.nix +++ b/flake.nix @@ -13,12 +13,19 @@ }: let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; - in { + in rec { packages.${system} = { default = pkgs.callPackage ./package.nix { naersk = pkgs.callPackage naersk {}; }; }; - # nixosModules.${system}.default = ./module.nix; + devShells.${system}.default = pkgs.mkShell { + nativeBuildInputs = + [ + pkgs.clippy + ] + ++ packages.${system}.default.buildInputs + ++ packages.${system}.default.nativeBuildInputs; + }; }; } diff --git a/package.nix b/package.nix index 47babf6..cd01beb 100644 --- a/package.nix +++ b/package.nix @@ -9,6 +9,7 @@ makeWrapper, naersk, pkg-config, + upx, wayland, xorg, ... @@ -33,8 +34,10 @@ naersk.buildPackage rec { nativeBuildInputs = [ pkg-config makeWrapper + upx ]; postInstall = '' + upx --lzma $out/bin/${meta.mainProgram} wrapProgram "$out/bin/${meta.mainProgram}" --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath buildInputs}" ''; meta = { diff --git a/src/lib.rs b/src/lib.rs index c5f76c4..1bf6e93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,191 +1,455 @@ -use iced::widget::text_input::Id; -use iced::widget::{column, row, scrollable, text, text_input}; -use iced::{Element, Font, Task, keyboard}; +//! this crate runs a terminal +#![expect( + clippy::needless_return, + clippy::shadow_reuse, + clippy::blanket_clippy_restriction_lints, + clippy::must_use_candidate, + clippy::missing_trait_methods, + clippy::pattern_type_mismatch, + clippy::std_instead_of_alloc, + clippy::cargo_common_metadata, + clippy::multiple_crate_versions, + clippy::semicolon_outside_block, + static_mut_refs, + unused_doc_comments, + reason = "" +)] + +use bpaf::Bpaf; + +use iced::widget::{column, rich_text, row, scrollable, span, text}; +use iced::{Element, Task, keyboard, time, window}; + +use nix::errno::Errno; +use nix::fcntl; use nix::pty::{ForkptyResult, forkpty}; use nix::unistd::write; use std::fs::File; -use std::io::{Read, Write}; -use std::os::unix::io::{AsFd, OwnedFd}; +use std::io::{self, Read as _}; +use std::os::unix::io::{AsFd as _, OwnedFd}; use std::process::Command; +use std::{error, fmt, thread, time as core_time}; -fn spawn_pty_with_shell(default_shell: String) -> OwnedFd { - match unsafe { forkpty(None, None) } { - Ok(fork_pty_res) => match fork_pty_res { - ForkptyResult::Parent { master, .. } => { - set_nonblock(&master); - master - } - ForkptyResult::Child => { - Command::new(&default_shell).spawn().unwrap(); - std::thread::sleep(std::time::Duration::MAX); - std::process::exit(0); - } - }, - Err(e) => panic!("failed to fork {:?}", e), - } -} +/// whether to enable verbose logging; see `Flags::verbose` +static mut VERBOSE: bool = false; -fn read_from_fd(fd: &OwnedFd) -> Option> { - let mut read_buffer = [0; 65536]; - let mut file = File::from(fd.try_clone().unwrap()); - file.flush(); - let file = file.read(&mut read_buffer); - match file { - Ok(file) => Some(read_buffer[..file].to_vec()), - Err(_) => None, - } -} - -fn set_nonblock(fd: &OwnedFd) { - let flags = nix::fcntl::fcntl(fd, nix::fcntl::FcntlArg::F_GETFL).unwrap(); - let mut flags = - nix::fcntl::OFlag::from_bits(flags & nix::fcntl::OFlag::O_ACCMODE.bits()).unwrap(); - flags.set(nix::fcntl::OFlag::O_NONBLOCK, true); - nix::fcntl::fcntl(fd, nix::fcntl::FcntlArg::F_SETFL(flags)).unwrap(); -} +/// shell path; see `Flags::shell` +static mut SHELL: Option = None; +/// events to be passed to `Model::update` +#[non_exhaustive] #[derive(Debug, Clone)] pub enum Msg { + Exit, + KeyPressed(keyboard::Key), Tick, - KeyPressed(iced::keyboard::Key), } +/// errors for this program +#[non_exhaustive] +#[derive(Debug)] +enum Error { + /// out of bounds err while accessing a slice + IndexOutOfBounds, + /// io error + Io(io::Error), + /// nix crate error + Nix(NixError), + /// try to access a `File::from::()` without an `OwnedFd` + NoFileDescriptor, + /// impossible error + Unreachable, +} + +impl fmt::Display for Error { + #[expect(clippy::min_ident_chars, reason = "it's in the docs like that")] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Nix(nix_error) => return write!(f, "{nix_error}"), + Self::Io(io_error) => return write!(f, "{io_error}"), + Self::NoFileDescriptor => return write!(f, "no file descriptor specified"), + Self::IndexOutOfBounds => return write!(f, "index out of bounds"), + Self::Unreachable => return write!(f, "unreachable error, panic"), + } + } +} + +impl error::Error for Error {} + +/// error wrapper for the `nix` crate +#[non_exhaustive] +#[derive(Debug)] +enum NixError { + /// an OS error + Errno(Errno), + /// the error when `OFlags::from_bits(..)` returns `None` + UnrecognisedFlag, +} + +impl fmt::Display for NixError { + #[expect(clippy::min_ident_chars, reason = "it's in the docs like that")] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::UnrecognisedFlag => return write!(f, "unrecognised flag"), + Self::Errno(errno) => return write!(f, "bad fcntl argument. errno: {errno}"), + } + } +} + +/// cli flags +#[derive(Debug, Clone, Bpaf)] +#[bpaf(options)] +pub struct Flags { + /// path to shell + #[bpaf(short('S'), long)] + shell: Option, + + /// whether to debug log + #[bpaf(short, long)] + verbose: bool, + + /// whether to display version: TODO + #[expect(dead_code, reason = "TODO")] + #[bpaf(short, long)] + version: bool, +} + +/// represents the terminal emulator\ +/// example usage: +/// ```rust +/// iced::application("window title", Model::update, Model::view) +/// .theme(Model::theme) +/// .default_font(iced::Font::MONOSPACE) +/// .decorations(false) +/// .subscription(Model::subscription) +/// .run() +/// ``` pub struct Model { - screen_buffer: [u8; 65536], - screen_buffer_index: usize, + /// location of cursor in user input line cursor_index: usize, - fd: OwnedFd, - stdin: OwnedFd, + /// fd of pty + fd: Option, + /// user input line input: String, + /// all chars on screen + screen_buffer: [u8; 0x4000], + /// length of `screen_buffer`'s filled area + screen_buffer_index: usize, + /// path to shell + shell: String, } impl Model { - fn new( - screen_buffer: [u8; 65536], - screen_buffer_index: usize, - cursor_index: usize, - fd: OwnedFd, - stdin: OwnedFd, - input: String, - ) -> Self { - Model { - screen_buffer, - screen_buffer_index, - cursor_index, - fd, - stdin, - input, - } - } - - pub fn update(&mut self, msg: Msg) -> Task { - match msg { - Msg::Tick => match read_from_fd(&self.fd) { - Some(red) => self.update_screen_buffer(&red), - None => (), - }, - Msg::KeyPressed(key) => match key { - keyboard::Key::Character(c) => { - self.input_char(c.chars().nth(0).unwrap()); - } - keyboard::Key::Named(keyboard::key::Named::Enter) => { - self.input.push('\n'); - let mut write_buffer = self.input.as_bytes().to_vec(); - write(self.fd.as_fd(), &mut write_buffer); - self.input = String::new(); - self.cursor_index = 0; - } - keyboard::Key::Named(keyboard::key::Named::Space) => { - self.input_char(' '); - } - keyboard::Key::Named(keyboard::key::Named::ArrowLeft) => { - if self.cursor_index <= 0 { - self.cursor_index = 0; - } else { - self.cursor_index -= 1; - } - } - keyboard::Key::Named(keyboard::key::Named::ArrowRight) => { - if self.cursor_index >= self.input.len() - 1 { - self.cursor_index = self.input.len() - 1; - } else { - self.cursor_index += 1; - } - } - _ => (), - }, - }; - iced::Task::::none() - } - - pub fn view(&self) -> Element<'_, Msg> { - let (left, right) = - match String::from_utf8(self.screen_buffer[..self.screen_buffer_index].to_vec()) - .unwrap() - .rsplit_once('\n') - { - Some(tup) => (tup.0.to_string(), tup.1.to_string()), - None => ( - String::new(), - String::from_utf8(self.screen_buffer[..self.screen_buffer_index].to_vec()) - .unwrap() - .to_string(), - ), - }; - scrollable(column![text(left), row![text(right), text(&self.input),]]).into() - } - - pub fn theme(&self) -> iced::Theme { - iced::Theme::GruvboxDark - } - - pub fn subscription(&self) -> iced::Subscription { - let tick = iced::time::every(iced::time::Duration::new(0, 1)).map(|_| Msg::Tick); - let key = keyboard::on_key_press(|key, _| Some(Msg::KeyPressed(key))); - iced::Subscription::batch(vec![tick, key]) - } - - fn update_screen_buffer(&mut self, vec: &Vec) { - let offset = self.screen_buffer_index; - for (i, chr) in vec.iter().enumerate() { - self.screen_buffer[i + offset] = chr.clone(); - self.screen_buffer_index += 1; - } - } - - fn input_char(&mut self, c: char) { + /// applies needed side effects when taking an input char + #[expect( + clippy::arithmetic_side_effects, + reason = "cursor_index is bound checked" + )] + fn input_char(&mut self, chr: char) { if self.cursor_index == self.input.len() { - self.input.push_str(c.to_string().as_str()); + self.input.push_str(chr.to_string().as_str()); } else { - self.input.insert(self.cursor_index, c); + self.input.insert(self.cursor_index, chr); } self.cursor_index += 1; } + + /// subscription logic for model + #[inline] + pub fn subscription(&self) -> iced::Subscription { + let tick = time::every(time::Duration::new(0, 1)).map(|_| { + return Msg::Tick; + }); + let key = keyboard::on_key_press(|key, _| { + return Some(Msg::KeyPressed(key)); + }); + return iced::Subscription::batch(vec![tick, key]); + } + + /// theme logic for model + #[inline] + pub const fn theme(&self) -> iced::Theme { + return iced::Theme::GruvboxDark; + } + /// update logic for model + /// TODO fix pattern type mismatch + /// TODO add more keys + #[inline] + #[expect( + clippy::arithmetic_side_effects, + clippy::wildcard_enum_match_arm, + reason = "bounds checked" + )] + pub fn update(&mut self, msg: Msg) -> Task { + match msg { + Msg::Exit => return window::get_latest().and_then(window::close), + Msg::KeyPressed(key) => { + match key { + keyboard::Key::Character(chr) => match chr.chars().nth(0) { + Some(chr) => self.input_char(chr), + None => return window::get_latest().and_then(window::close), + }, + keyboard::Key::Named(keyboard::key::Named::Enter) => { + self.input.push('\n'); + let write_buffer = self.input.as_bytes().to_vec(); + if let Some(fd) = &self.fd { + match write(fd.as_fd(), &write_buffer) { + Ok(_) => (), + Err(error) => print_err(&Error::Nix(NixError::Errno(error))), + } + } + self.input = String::new(); + self.cursor_index = 0; + } + keyboard::Key::Named(keyboard::key::Named::Space) => { + self.input_char(' '); + } + keyboard::Key::Named(keyboard::key::Named::ArrowLeft) => { + if self.cursor_index == 0 { + self.cursor_index = 0; + } else { + self.cursor_index -= 1; + } + } + keyboard::Key::Named(keyboard::key::Named::ArrowRight) => { + if self.cursor_index >= self.input.len() { + self.cursor_index = self.input.len(); + } else { + self.cursor_index += 1; + } + } + keyboard::Key::Named(keyboard::key::Named::ArrowUp) => { + self.cursor_index = 0; + } + keyboard::Key::Named(keyboard::key::Named::ArrowDown) => { + self.cursor_index = self.input.len(); + } + _ => (), + } + return iced::Task::none(); + } + Msg::Tick => { + let red = read_from_option_fd(self.fd.as_ref()); + match red { + Ok(red) => { + if let Err(error) = self.update_screen_buffer(&red) { + print_err(&error); + } + } + Err(error) => print_err(&error), + } + return iced::Task::none(); + } + } + } + + /// reads from the pty and adds it to the buffer + #[expect( + clippy::arithmetic_side_effects, + clippy::indexing_slicing, + reason = "all is bound checked" + )] + fn update_screen_buffer(&mut self, vec: &[u8]) -> Result<(), Error> { + let offset = self.screen_buffer_index; + for (i, chr) in vec.iter().enumerate() { + let index = i + offset; + if index < self.screen_buffer.len() { + self.screen_buffer[index] = *chr; + } else { + return Err(Error::IndexOutOfBounds); + } + self.screen_buffer_index += 1; + } + return Ok(()); + } + + /// view logic for model\ + /// TODO add wide char support\ + /// TODO bound check + #[inline] + #[expect( + clippy::arithmetic_side_effects, + clippy::indexing_slicing, + clippy::string_slice, + reason = "TODO" + )] + pub fn view(&self) -> Element<'_, Msg> { + let (left, right) = + String::from_utf8_lossy(&self.screen_buffer[..self.screen_buffer_index]) + .rsplit_once('\n') + .map_or_else( + || { + return ( + String::new(), + String::from_utf8_lossy( + &self.screen_buffer[..self.screen_buffer_index], + ) + .to_string(), + ); + }, + |tup| { + return (tup.0.to_owned(), tup.1.to_owned()); + }, + ); + return scrollable(column![ + text(left), + row![ + text(right), + text(&self.input[..(self.cursor_index)]), + if self.cursor_index < self.input.len() { + row![ + if self.input[self.cursor_index..=self.cursor_index] == *" " { + text("_").color(iced::Color::from_rgb(f32::MAX, 0.0, 0.0)) + } else { + text(&self.input[self.cursor_index..=self.cursor_index]) + .color(iced::Color::from_rgb(f32::MAX, 0.0, 0.0)) + }, + text(&self.input[(self.cursor_index + 1)..]) + ] + } else { + row![ + text(&self.input[self.cursor_index..]), + rich_text![ + span("_") + .color(iced::Color::from_rgb(f32::MAX, 0.0, 0.0)) + .background(iced::Color::from_rgb(f32::MAX, f32::MAX, 0.0)) + ] + ] + } + ] + ]) + .into(); + } } impl Default for Model { + #[inline] + #[expect(clippy::undocumented_unsafe_blocks, reason = "clippy be trippin")] fn default() -> Self { - let mut me = Self::new( - [0; 65536], - 0, - 0, - spawn_pty_with_shell("/home/mtgmonkey/.nix-profile/bin/dash".to_string()), - std::io::stdin().as_fd().try_clone_to_owned().unwrap(), - String::new(), - ); + let mut me = Self { + screen_buffer: [0; 0x4000], + screen_buffer_index: 0, + cursor_index: 0, + fd: None, + input: String::new(), + /// SAFETY call *after* `init()` + shell: unsafe { SHELL.clone() }.map_or_else( + || return String::from("/home/mtgmonkey/.nix-profile/bin/dash"), + |shell| return shell, + ), + }; + me.fd = spawn_pty_with_shell(&me.shell).ok(); let mut nored = true; while nored { - let red = read_from_fd(&me.fd); - match red { - Some(red) => { - nored = false; - me.update_screen_buffer(&red); + let red = read_from_option_fd(me.fd.as_ref()); + if let Ok(red) = red { + nored = false; + if let Err(error) = me.update_screen_buffer(&red) { + print_err(&error); } - None => (), } } - me + return me; + } +} + +/// # Safety +/// call *before* creating a `Model` because `Model::default()` relies on `SHELL` +/// call *before* `print_err()` because `print_err()` relies on `VERBOSE` +#[inline] +#[expect(clippy::undocumented_unsafe_blocks, reason = "clippy be trippin")] +pub unsafe fn init(flags: Flags) { + unsafe { + VERBOSE = flags.verbose; + } + unsafe { + SHELL = flags.shell; + } +} + +/// spawns a pty with the specified shell program +#[expect(clippy::single_call_fn, reason = "abstraction")] +fn spawn_pty_with_shell(default_shell: &str) -> Result { + // SAFETY: always safe unless the OS is out of ptys + // so it is always safe + match unsafe { forkpty(None, None) } { + Ok(fork_pty_res) => match fork_pty_res { + ForkptyResult::Parent { master, .. } => { + if let Err(error) = set_nonblock(&master) { + return Err(error); + } + return Ok(master); + } + ForkptyResult::Child => { + if let Err(error) = Command::new(default_shell).spawn() { + return Err(Error::Io(error)); + } + thread::sleep(core_time::Duration::MAX); + return Err(Error::Unreachable); + } + }, + Err(error) => return Err(Error::Nix(NixError::Errno(error))), + } +} + +/// reads from an `&OwnedFd` +/// TODO check bounds +#[expect( + clippy::single_call_fn, + clippy::indexing_slicing, + reason = "abstraction" +)] +fn read_from_fd(fd: &OwnedFd) -> Result, Error> { + let mut read_buffer = [0; 0x4000]; + #[expect(clippy::unwrap_used, reason = "platform-specific but fine")] + let mut file = File::from(fd.try_clone().unwrap()); + let file = file.read(&mut read_buffer); + match file { + Ok(file) => return Ok(read_buffer[..file].to_vec()), + Err(error) => return Err(Error::Io(error)), + } +} + +/// reads from an `Option<&OwnedFd>` if it's there +fn read_from_option_fd(maybe_fd: Option<&OwnedFd>) -> Result, Error> { + return maybe_fd.map_or(Err(Error::NoFileDescriptor), |fd| { + return read_from_fd(fd); + }); +} + +/// sets a `OwnedFd` as nonblocking. +#[expect(clippy::single_call_fn, reason = "abstraction")] +fn set_nonblock(fd: &OwnedFd) -> Result<(), Error> { + let flags = match fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFL) { + Ok(flags) => flags, + Err(errno) => return Err(Error::Nix(NixError::Errno(errno))), + }; + let flags = fcntl::OFlag::from_bits(flags & fcntl::OFlag::O_ACCMODE.bits()); + match flags { + Some(mut flags) => { + flags.set(fcntl::OFlag::O_NONBLOCK, true); + if let Err(errno) = fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFL(flags)) { + return Err(Error::Nix(NixError::Errno(errno))); + } + } + None => return Err(Error::Nix(NixError::UnrecognisedFlag)), + } + return Ok(()); +} + +/// if `VERBOSE` is `true`, logs errors +#[inline] +#[expect( + clippy::print_stdout, + clippy::undocumented_unsafe_blocks, + reason = "toggleable with VERBOSE option\n + clippy be buggin" +)] +fn print_err(error: &Error) { + /// SAFETY the only time VERBOSE is written to should be `init()` + if unsafe { VERBOSE } { + println!("[ERROR] {error}"); } } diff --git a/src/main.rs b/src/main.rs index abd02b5..a9241f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,27 @@ -use rust_term::*; +//! this crate runs a terminal +#![expect( + clippy::needless_return, + clippy::cargo_common_metadata, + clippy::blanket_clippy_restriction_lints, + clippy::multiple_crate_versions, + unused_doc_comments, + reason = "" +)] + +use rust_term::{Model, flags, init}; + +#[expect(clippy::undocumented_unsafe_blocks, reason = "clippy be trippin")] fn main() -> iced::Result { - iced::application("test", Model::update, Model::view) + /// SAFETY call does occur *before* the initialization of a Model + /// SAFETY call does occur *before* any opportunity to call `print_err()` + unsafe { + init(flags().run()); + }; + return iced::application("test", Model::update, Model::view) .theme(Model::theme) .default_font(iced::Font::MONOSPACE) .decorations(false) .subscription(Model::subscription) - .run() - /* - let default_shell = "/home/mtgmonkey/.nix-profile/bin/dash".to_string(); - let fd = spawn_pty_with_shell(default_shell); - let mut write_buffer = "tty\n".as_bytes().to_vec(); - write(fd.as_fd(), &mut write_buffer); - loop { - let red = read_from_fd(&fd); - match red { - Some(red) => print!("{}", String::from_utf8(red).unwrap()), - None => { - let mut read_buffer = [0; 65536]; - let mut file = File::from(std::io::stdin().as_fd().try_clone_to_owned().unwrap()); - file.flush(); - let file = file.read(&mut read_buffer); - println!( - "{}", - match file { - Ok(file) => write(fd.as_fd(), &read_buffer[..file]).unwrap(), - Err(_) => 0, - } - ); - } - }; - } - */ + .run(); }