deps+fix: upgrade zbus 5 / rusqlite 0.40 / dirs 6; daemon hardening

Dependency major upgrades (zbus 4->5, rusqlite 0.31->0.40, dirs 5->6) and the
migration + hardening that came with the review:

- zbus 5: SignalContext -> SignalEmitter, signal_context() -> signal_emitter().
- Single-instance D-Bus name: request with DoNotQueue instead of
  ReplaceExisting|AllowReplacement. If the name is already owned the daemon now
  exits instead of running without owning it (zbus 5 returns InQueue as Ok) or
  stealing the name and orphaning the running instance (zbus does not terminate
  a replaced owner). The extension already skips spawning when the name is
  owned, so replacement was never needed.
- Stop swallowing real SQLite errors: upsert_item's dedup SELECT and
  get_thumbnail/get_raw_item now use .optional().context()? so a missing row is
  Ok(None) and a genuine error propagates instead of masquerading as not-found.
- clippy pedantic/nursery cleanup: uninlined format args, redundant mime clone,
  pass ObjectId/Connection by reference where not consumed, match->if/is_ok,
  doc backticks; #[allow] with rationale for the intentional lints
  (significant_drop_tightening on the single-writer lock, unused_async on zbus
  interface methods).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Eduard Tolosa 2026-05-26 21:32:56 -05:00
parent e2972de37a
commit aa56cac958
7 changed files with 196 additions and 492 deletions

499
strata-daemon/Cargo.lock generated
View file

@ -8,18 +8,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.4"
@ -85,17 +73,6 @@ dependencies = [
"slab",
]
[[package]]
name = "async-fs"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5"
dependencies = [
"async-lock",
"blocking",
"futures-lite",
]
[[package]]
name = "async-io"
version = "2.6.0"
@ -111,7 +88,7 @@ dependencies = [
"polling",
"rustix",
"slab",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@ -169,7 +146,7 @@ dependencies = [
"rustix",
"signal-hook-registry",
"slab",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@ -218,16 +195,7 @@ dependencies = [
"cc",
"cfg-if",
"constant_time_eq",
"cpufeatures 0.3.0",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
"cpufeatures",
]
[[package]]
@ -283,12 +251,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "color_quant"
version = "1.1.0"
@ -310,15 +272,6 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "cpufeatures"
version = "0.3.0"
@ -349,45 +302,25 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[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 = "5.0.1"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
"windows-sys",
]
[[package]]
@ -436,7 +369,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@ -521,6 +454,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "futures-core"
version = "0.3.32"
@ -546,12 +485,6 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "futures-sink"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
[[package]]
name = "futures-task"
version = "0.3.32"
@ -565,24 +498,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-io",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"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 = "getrandom"
version = "0.2.17"
@ -628,22 +548,22 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
"foldhash 0.1.5",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"foldhash 0.2.0",
]
[[package]]
@ -654,11 +574,11 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "hashlink"
version = "0.9.1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230"
dependencies = [
"hashbrown 0.14.5",
"hashbrown 0.16.1",
]
[[package]]
@ -773,9 +693,9 @@ dependencies = [
[[package]]
name = "libsqlite3-sys"
version = "0.28.0"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
checksum = "a76001fb4daed01e5f2b518aac0b4dc592e7c734da63dbffcf0c64fa612a8d0c"
dependencies = [
"cc",
"pkg-config",
@ -845,7 +765,7 @@ checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
dependencies = [
"libc",
"wasi",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@ -858,19 +778,6 @@ dependencies = [
"pxfm",
]
[[package]]
name = "nix"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
"memoffset",
]
[[package]]
name = "nom"
version = "8.0.0"
@ -886,7 +793,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@ -927,7 +834,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@ -1017,16 +924,7 @@ dependencies = [
"hermit-abi",
"pin-project-lite",
"rustix",
"windows-sys 0.61.2",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
"windows-sys",
]
[[package]]
@ -1093,36 +991,6 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
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",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.17",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
@ -1134,13 +1002,13 @@ dependencies = [
[[package]]
name = "redox_users"
version = "0.4.6"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.17",
"libredox",
"thiserror 1.0.69",
"thiserror",
]
[[package]]
@ -1161,10 +1029,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "rusqlite"
version = "0.31.0"
name = "rsqlite-vfs"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c"
dependencies = [
"hashbrown 0.16.1",
"thiserror",
]
[[package]]
name = "rusqlite"
version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b3492ea85308705c3a5cc24fb9b9cf77273d30590349070db42991202b214c4"
dependencies = [
"bitflags",
"fallible-iterator",
@ -1172,6 +1050,7 @@ dependencies = [
"hashlink",
"libsqlite3-sys",
"smallvec",
"sqlite-wasm-rs",
]
[[package]]
@ -1184,7 +1063,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@ -1259,17 +1138,6 @@ dependencies = [
"syn",
]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@ -1320,14 +1188,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [
"libc",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
name = "sqlite-wasm-rs"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75"
dependencies = [
"cc",
"js-sys",
"rsqlite-vfs",
"wasm-bindgen",
]
[[package]]
name = "strata-daemon"
@ -1372,16 +1246,7 @@ dependencies = [
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys 0.61.2",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
"windows-sys",
]
[[package]]
@ -1390,18 +1255,7 @@ version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl 2.0.18",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
"thiserror-impl",
]
[[package]]
@ -1453,7 +1307,7 @@ dependencies = [
"socket2",
"tokio-macros",
"tracing",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@ -1569,12 +1423,6 @@ dependencies = [
"petgraph",
]
[[package]]
name = "typenum"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]]
name = "uds_windows"
version = "1.2.1"
@ -1583,7 +1431,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset",
"tempfile",
"windows-sys 0.61.2",
"windows-sys",
]
[[package]]
@ -1606,6 +1454,7 @@ checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
dependencies = [
"getrandom 0.4.2",
"js-sys",
"serde_core",
"wasm-bindgen",
]
@ -1621,12 +1470,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
@ -1812,33 +1655,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
@ -1848,127 +1664,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "1.0.3"
@ -2082,7 +1777,7 @@ dependencies = [
"log",
"os_pipe",
"rustix",
"thiserror 2.0.18",
"thiserror",
"tree_magic_mini",
"wayland-backend",
"wayland-client",
@ -2090,25 +1785,14 @@ dependencies = [
"wayland-protocols-wlr",
]
[[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 = "zbus"
version = "4.4.0"
version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1"
dependencies = [
"async-broadcast",
"async-executor",
"async-fs",
"async-io",
"async-lock",
"async-process",
@ -2119,21 +1803,19 @@ dependencies = [
"enumflags2",
"event-listener",
"futures-core",
"futures-sink",
"futures-util",
"futures-lite",
"hex",
"nix",
"libc",
"ordered-stream",
"rand",
"rustix",
"serde",
"serde_repr",
"sha1",
"static_assertions",
"tokio",
"tracing",
"uds_windows",
"windows-sys 0.52.0",
"xdg-home",
"uuid",
"windows-sys",
"winnow",
"zbus_macros",
"zbus_names",
"zvariant",
@ -2141,25 +1823,27 @@ dependencies = [
[[package]]
name = "zbus_macros"
version = "4.4.0"
version = "5.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zbus_names",
"zvariant",
"zvariant_utils",
]
[[package]]
name = "zbus_names"
version = "3.0.0"
version = "4.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d"
dependencies = [
"serde",
"static_assertions",
"winnow",
"zvariant",
]
@ -2206,22 +1890,23 @@ dependencies = [
[[package]]
name = "zvariant"
version = "4.2.0"
version = "5.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee"
dependencies = [
"endi",
"enumflags2",
"serde",
"static_assertions",
"winnow",
"zvariant_derive",
"zvariant_utils",
]
[[package]]
name = "zvariant_derive"
version = "4.2.0"
version = "5.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda"
dependencies = [
"proc-macro-crate",
"proc-macro2",
@ -2232,11 +1917,13 @@ dependencies = [
[[package]]
name = "zvariant_utils"
version = "2.1.0"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691"
dependencies = [
"proc-macro2",
"quote",
"serde",
"syn",
"winnow",
]

View file

@ -13,7 +13,7 @@ path = "src/main.rs"
tokio = { version = "1", features = ["full"] }
# D-Bus (async, tokio-native)
zbus = { version = "4", features = ["tokio"] }
zbus = { version = "5", features = ["tokio"] }
# Wayland clipboard monitoring (custom event loop)
wayland-client = "0.31"
@ -25,7 +25,7 @@ wayland-protocols-wlr = { version = "0.3", features = ["client"] }
wl-clipboard-rs = "0.9"
# Storage
rusqlite = { version = "0.31", features = ["bundled"] }
rusqlite = { version = "0.40", features = ["bundled"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", features = ["v4"] }
@ -41,7 +41,7 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
# XDG paths
dirs = "5"
dirs = "6"
# Error handling
anyhow = "1"

View file

@ -1,7 +1,7 @@
/// Wayland clipboard monitor using the ext-data-control-v1 protocol with a
/// zwlr-data-control-v1 fallback for wlroots-based compositors. GNOME's Mutter
/// exposes neither, so on GNOME this monitor does not bind and clipboard
/// content arrives from the extension via Meta.Selection + SubmitItem.
/// content arrives from the extension via Meta.Selection + `SubmitItem`.
///
/// Runs on a dedicated OS thread (NOT on the tokio runtime) to avoid blocking
/// the async executor. Communicates clipboard change events to the tokio world
@ -59,9 +59,9 @@ impl MonitorState {
self.offers.entry(id).or_default().push(mime);
}
fn commit_selection(&mut self, offer_id: ObjectId) {
fn commit_selection(&mut self, offer_id: &ObjectId) {
// Take the mime list for this offer (and discard stale in-flight offers).
let mimes = self.offers.remove(&offer_id).unwrap_or_default();
let mimes = self.offers.remove(offer_id).unwrap_or_default();
self.offers.clear(); // discard any superseded offers
if !mimes.is_empty() {
let _ = self.tx.send(ClipboardChange { mime_types: mimes });
@ -119,7 +119,7 @@ impl Dispatch<ExtDataControlDeviceV1, ()> for MonitorState {
}
ext_data_control_device_v1::Event::Selection { id: Some(offer) } => {
let oid = offer.id();
state.commit_selection(oid);
state.commit_selection(&oid);
// Politely destroy the offer - the compositor will clean up
// the Wayland object.
offer.destroy();
@ -183,7 +183,7 @@ impl Dispatch<ZwlrDataControlDeviceV1, ()> for MonitorState {
}
zwlr_data_control_device_v1::Event::Selection { id: Some(offer) } => {
let oid = offer.id();
state.commit_selection(oid);
state.commit_selection(&oid);
offer.destroy();
}
zwlr_data_control_device_v1::Event::Selection { id: None } => {
@ -233,7 +233,7 @@ pub fn spawn(tx: UnboundedSender<ClipboardChange>) -> Result<()> {
std::thread::Builder::new()
.name("strata-wl-monitor".into())
.spawn(move || {
if let Err(e) = run_loop(conn, protocol, tx) {
if let Err(e) = run_loop(&conn, protocol, tx) {
tracing::error!("Clipboard monitor exited with error: {e:#}");
}
})?;
@ -278,11 +278,11 @@ fn probe_protocol(conn: &Connection) -> Result<Protocol> {
}
fn run_loop(
conn: Connection,
conn: &Connection,
protocol: Protocol,
tx: UnboundedSender<ClipboardChange>,
) -> Result<()> {
let (globals, mut queue) = registry_queue_init::<MonitorState>(&conn)?;
let (globals, mut queue) = registry_queue_init::<MonitorState>(conn)?;
let qh = queue.handle();
let mut state = MonitorState::new(tx);

View file

@ -22,7 +22,7 @@ pub fn write_to_clipboard(req: WriteRequest) -> Result<()> {
vec![
MimeSource {
source: Source::Bytes(bytes.clone().into()),
mime_type: MimeType::Specific(mime.clone()),
mime_type: MimeType::Specific(mime),
},
MimeSource {
source: Source::Bytes(bytes.into()),

View file

@ -1,5 +1,11 @@
// SQLite is single-writer, and `lock_conn`'s guard is intentionally held for
// each function's whole body -- the upsert's SELECT-then-INSERT must be atomic
// under one lock. `significant_drop_tightening` would have us drop the guard
// earlier, which would break that guarantee, so it is allowed for this module.
#![allow(clippy::significant_drop_tightening)]
use anyhow::{Context, Result};
use rusqlite::{params, Connection};
use rusqlite::{params, Connection, OptionalExtension};
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::sync::{Mutex, MutexGuard};
@ -21,11 +27,11 @@ pub struct ItemMeta {
pub content_text: Option<String>,
pub source_app: Option<String>,
pub created_at: i64,
/// True if this item has a stored thumbnail (clients should call get_thumbnail to fetch it).
/// True if this item has a stored thumbnail (clients should call `get_thumbnail` to fetch it).
pub has_thumbnail: bool,
}
/// A row as stored in SQLite (blob is raw bytes, not base64).
/// A row as stored in `SQLite` (blob is raw bytes, not base64).
#[allow(dead_code)]
pub struct RawItem {
pub id: String,
@ -43,12 +49,12 @@ pub struct Db {
/// Acquire the DB lock, transparently recovering from poison.
///
/// A mutex is "poisoned" when a thread panics while holding it. For our SQLite
/// A mutex is "poisoned" when a thread panics while holding it. For our `SQLite`
/// connection this is safe to recover from because rusqlite uses RAII statements
/// and transactions - any in-flight statement or transaction has already been
/// rolled back by the time the panic unwinds past it. Without this recovery,
/// a single panic anywhere in the daemon would permanently break ALL future
/// database access (every subsequent .lock().unwrap() would panic too).
/// database access (every subsequent .`lock().unwrap()` would panic too).
fn lock_conn(m: &Mutex<Connection>) -> MutexGuard<'_, Connection> {
match m.lock() {
Ok(g) => g,
@ -62,7 +68,7 @@ fn lock_conn(m: &Mutex<Connection>) -> MutexGuard<'_, Connection> {
impl Db {
pub fn open(path: &Path) -> Result<Self> {
let conn = Connection::open(path)
.with_context(|| format!("Opening SQLite database at {:?}", path))?;
.with_context(|| format!("Opening SQLite database at {}", path.display()))?;
// Performance pragmas: WAL mode for concurrent reads, synchronous=NORMAL is safe
// for a clipboard history (we can tolerate losing the last item on a crash).
@ -140,14 +146,18 @@ impl Db {
let conn = lock_conn(&self.conn);
let now_ms = chrono_now_ms();
// Check for existing item with this hash.
// Check for existing item with this hash. optional() maps "no match" to
// None; a real read error propagates instead of being swallowed (which
// would make a duplicate look new and then fail the INSERT on the UNIQUE
// index with a confusing error).
let existing: Option<String> = conn
.query_row(
"SELECT id FROM clipboard_history WHERE content_hash = ?1",
params![content_hash],
|row| row.get(0),
)
.ok();
.optional()
.context("Checking for existing item")?;
if let Some(id) = existing {
conn.execute(
@ -206,9 +216,9 @@ impl Db {
Ok(items)
}
/// Full-text search over content_text. Only text items can match - images
/// Full-text search over `content_text`. Only text items can match - images
/// and other non-text content are NOT indexed and never appear in results.
/// Empty query returns []; callers should use get_history_page for that.
/// Empty query returns []; callers should use `get_history_page` for that.
pub fn search_history(&self, query: &str, limit: usize) -> Result<Vec<ItemMeta>> {
let conn = lock_conn(&self.conn);
@ -219,7 +229,7 @@ impl Db {
.map(|t| {
// Escape embedded double-quotes by doubling them per FTS5 syntax.
let escaped = t.replace('"', "\"\"");
format!("\"{}\"*", escaped)
format!("\"{escaped}\"*")
})
.collect();
@ -258,22 +268,26 @@ impl Db {
/// (text item, or doesn't exist).
pub fn get_thumbnail(&self, id: &str) -> Result<Option<Vec<u8>>> {
let conn = lock_conn(&self.conn);
let blob: Option<Option<Vec<u8>>> = conn
// thumbnail_blob is itself nullable, so the row yields Option<Vec<u8>>.
// optional() maps a missing row to Ok(None); real errors propagate.
let blob = conn
.query_row(
"SELECT thumbnail_blob FROM clipboard_history WHERE id = ?1",
params![id],
|row| row.get(0),
|row| row.get::<_, Option<Vec<u8>>>(0),
)
.ok();
.optional()
.context("Fetching thumbnail")?;
Ok(blob.flatten())
}
/// Fetch raw item content for clipboard write-back. Does not load
/// thumbnail_blob -- callers only need the original payload.
/// `thumbnail_blob` -- callers only need the original payload.
pub fn get_raw_item(&self, id: &str) -> Result<Option<RawItem>> {
let conn = lock_conn(&self.conn);
let result = conn
.query_row(
// optional() maps a missing row to Ok(None); real errors propagate
// instead of being silently swallowed as "not found".
conn.query_row(
"SELECT id, mime_type, content_text, content_blob, source_app, created_at
FROM clipboard_history WHERE id = ?1",
params![id],
@ -289,8 +303,8 @@ impl Db {
})
},
)
.ok();
Ok(result)
.optional()
.context("Fetching clipboard item")
}
pub fn delete_item(&self, id: &str) -> Result<bool> {
@ -306,7 +320,7 @@ impl Db {
}
/// Prune history to `max_history` most recent items. Returns the IDs of
/// items that were deleted, so the caller can emit ItemDeleted signals
/// items that were deleted, so the caller can emit `ItemDeleted` signals
/// (lets clients clean up per-item caches like thumbnail files).
pub fn prune(&self, max_history: usize) -> Result<Vec<String>> {
let conn = lock_conn(&self.conn);
@ -331,10 +345,7 @@ impl Db {
}
// Delete by collected IDs so the ORDER BY subquery runs only once.
let placeholders = ids.iter().map(|_| "?").collect::<Vec<_>>().join(", ");
let sql = format!(
"DELETE FROM clipboard_history WHERE id IN ({})",
placeholders
);
let sql = format!("DELETE FROM clipboard_history WHERE id IN ({placeholders})");
conn.execute(&sql, rusqlite::params_from_iter(ids.iter()))?;
Ok(ids)
}

View file

@ -1,22 +1,22 @@
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use zbus::{interface, object_server::SignalContext};
use zbus::{interface, object_server::SignalEmitter};
use crate::db::{Db, RawItem};
/// Maximum thumbnail dimension for images (px). Larger images are resized down.
pub const THUMB_PX: u32 = 200;
/// Default maximum raw image size to accept (bytes). Overridable via SetConfig.
/// Default maximum raw image size to accept (bytes). Overridable via `SetConfig`.
pub const DEFAULT_MAX_IMAGE_BYTES: usize = 5 * 1024 * 1024;
/// Default maximum text size to accept. Overridable via SetConfig.
/// Default maximum text size to accept. Overridable via `SetConfig`.
pub const DEFAULT_MAX_TEXT_BYTES: usize = 1024 * 1024;
/// Default maximum history item count. Overridable via SetConfig.
/// Default maximum history item count. Overridable via `SetConfig`.
pub const DEFAULT_MAX_HISTORY: usize = 200;
/// Runtime-configurable limits shared between the D-Bus service and the
/// clipboard processing tasks. All three are read on every ingest / prune
/// call, so updates via SetConfig take effect immediately.
/// call, so updates via `SetConfig` take effect immediately.
#[derive(Clone)]
pub struct Limits {
pub max_history: Arc<AtomicUsize>,
@ -67,6 +67,8 @@ impl StrataManager {
/// hint is not forwarded, so this method cannot re-check it. Treat callers of
/// `SubmitItem` as trusted (the session bus is per-user). Forwarding the
/// sensitivity flag to enforce it here is left for a future contract change.
// async is required by the zbus #[interface] method signature, not by the body.
#[allow(clippy::unused_async)]
async fn submit_item(&self, mime_type: String, content: Vec<u8>) -> zbus::fdo::Result<()> {
self.submit_tx
.send(SubmitRequest {
@ -76,8 +78,8 @@ impl StrataManager {
.map_err(|e| zbus::fdo::Error::Failed(e.to_string()))
}
/// Return a page of recent items as JSON metadata (no inline thumbnail bytes,
/// content_text truncated to a preview). Clients call GetThumbnail(id) lazily
/// for items with has_thumbnail=true and GetItemContent(id) for full content.
/// `content_text` truncated to a preview). Clients call GetThumbnail(id) lazily
/// for items with `has_thumbnail=true` and GetItemContent(id) for full content.
/// `offset` is from the most recent item (0 = newest).
async fn get_history(&self, offset: u32, limit: u32) -> zbus::fdo::Result<String> {
let db = self.db.clone();
@ -93,7 +95,7 @@ impl StrataManager {
}
/// Full-text search across the entire DB. Empty query returns []; callers
/// should fall back to GetHistory in that case.
/// should fall back to `GetHistory` in that case.
async fn search_history(&self, query: String, limit: u32) -> zbus::fdo::Result<String> {
let db = self.db.clone();
let items = tokio::task::spawn_blocking(move || db.search_history(&query, limit as usize))
@ -173,7 +175,7 @@ impl StrataManager {
async fn delete_item(
&self,
id: String,
#[zbus(signal_context)] ctx: SignalContext<'_>,
#[zbus(signal_context)] ctx: SignalEmitter<'_>,
) -> zbus::fdo::Result<()> {
let db = self.db.clone();
let id_clone = id.clone();
@ -190,7 +192,7 @@ impl StrataManager {
/// Remove all items from history.
async fn clear_history(
&self,
#[zbus(signal_context)] ctx: SignalContext<'_>,
#[zbus(signal_context)] ctx: SignalEmitter<'_>,
) -> zbus::fdo::Result<()> {
let db = self.db.clone();
tokio::task::spawn_blocking(move || db.clear_history())
@ -204,14 +206,14 @@ impl StrataManager {
}
/// Push runtime limits from the front-end. Takes effect immediately.
/// All three values are absolute (max_text and max_image are bytes,
/// All three values are absolute (`max_text` and `max_image` are bytes,
/// not MB). 0 means "leave unchanged" for that field.
async fn set_config(
&self,
max_history: u32,
max_text_bytes: u32,
max_image_bytes: u32,
#[zbus(signal_context)] ctx: SignalContext<'_>,
#[zbus(signal_context)] ctx: SignalEmitter<'_>,
) -> zbus::fdo::Result<()> {
use std::sync::atomic::Ordering;
if max_history > 0 {
@ -256,6 +258,8 @@ impl StrataManager {
}
/// Gracefully shut down the daemon.
// async is required by the zbus #[interface] method signature, not by the body.
#[allow(clippy::unused_async)]
async fn shutdown(&self) {
tracing::info!("Shutdown requested via D-Bus");
// Signal the main loop to exit. The reply is sent before the process exits
@ -268,12 +272,12 @@ impl StrataManager {
// -----------------------------------------------------------------
/// Emitted when a new item is stored (after dedup + thumbnail generation).
/// `preview` is a text excerpt (≤ PREVIEW_CHARS) for text items, or empty for
/// `preview` is a text excerpt (≤ `PREVIEW_CHARS`) for text items, or empty for
/// images and other binary types - clients should call GetThumbnail(id) to
/// fetch image thumbnails lazily.
#[zbus(signal)]
pub async fn item_added(
ctx: &SignalContext<'_>,
ctx: &SignalEmitter<'_>,
id: &str,
mime_type: &str,
preview: &str,
@ -281,9 +285,9 @@ impl StrataManager {
/// Emitted when an item is removed.
#[zbus(signal)]
pub async fn item_deleted(ctx: &SignalContext<'_>, id: &str) -> zbus::Result<()>;
pub async fn item_deleted(ctx: &SignalEmitter<'_>, id: &str) -> zbus::Result<()>;
/// Emitted when the entire history is cleared.
#[zbus(signal)]
pub async fn history_cleared(ctx: &SignalContext<'_>) -> zbus::Result<()>;
pub async fn history_cleared(ctx: &SignalEmitter<'_>) -> zbus::Result<()>;
}

View file

@ -54,19 +54,21 @@ async fn main() -> Result<()> {
.await
.context("Building D-Bus connection")?;
// Request the well-known name with ReplaceExisting (try to take it from any
// outgoing instance) AND AllowReplacement (let the next instance take it
// from us). Both are needed for a clean hand-off on extension reload: without
// AllowReplacement on the running instance, a new instance's ReplaceExisting
// would be refused.
// Single-instance daemon: request the name with DoNotQueue. The extension
// spawns exactly one daemon and skips spawning if the name is already owned,
// so if the name IS taken here, another instance is already serving - the
// request returns an error (rather than silently queuing, which would leave
// us running without owning the name while clients talk to the other one)
// and we exit. We deliberately do NOT use ReplaceExisting: stealing the name
// would orphan the running instance (zbus does not terminate a replaced
// owner), so we let the existing one keep it and bow out.
dbus_conn
.request_name_with_flags(
"dev.edu4rdshl.Strata",
zbus::fdo::RequestNameFlags::ReplaceExisting
| zbus::fdo::RequestNameFlags::AllowReplacement,
zbus::fdo::RequestNameFlags::DoNotQueue.into(),
)
.await
.context("Acquiring D-Bus name dev.edu4rdshl.Strata")?;
.context("Acquiring D-Bus name dev.edu4rdshl.Strata (is another instance running?)")?;
tracing::info!("D-Bus service registered as dev.edu4rdshl.Strata");
@ -76,11 +78,12 @@ async fn main() -> Result<()> {
// so this fails to bind there and the extension feeds content via SubmitItem
// instead. On wlroots compositors (Sway, Hyprland) the monitor is the path.
// -----------------------------------------------------------------------
match clipboard::monitor::spawn(clip_tx) {
Ok(()) => tracing::info!("Wayland clipboard monitor started"),
Err(_) => tracing::info!(
if clipboard::monitor::spawn(clip_tx).is_ok() {
tracing::info!("Wayland clipboard monitor started");
} else {
tracing::info!(
"Wayland clipboard monitor unavailable, using GJS Meta.Selection path (expected on GNOME)"
),
);
}
// -----------------------------------------------------------------------
@ -198,12 +201,11 @@ async fn process_change(
tracing::debug!("Skipping clipboard change marked as sensitive");
return Ok(());
}
let mime = match pick_mime(mime_types) {
Some(m) => m.to_string(),
None => {
let mime = if let Some(m) = pick_mime(mime_types) {
m.to_string()
} else {
tracing::debug!("No usable MIME type in {:?}", mime_types);
return Ok(());
}
};
tracing::debug!("Processing Wayland clipboard change, MIME: {}", mime);
@ -316,12 +318,12 @@ async fn process_bytes(
.await
.context("Getting StrataManager interface ref")?;
StrataManager::item_added(iface.signal_context(), &id, &mime, &preview)
StrataManager::item_added(iface.signal_emitter(), &id, &mime, &preview)
.await
.context("Emitting ItemAdded signal")?;
for pid in &pruned_ids {
if let Err(e) = StrataManager::item_deleted(iface.signal_context(), pid).await {
if let Err(e) = StrataManager::item_deleted(iface.signal_emitter(), pid).await {
tracing::warn!("Emitting ItemDeleted for pruned id={}: {}", pid, e);
}
}