Compare commits

...

3 commits

Author SHA1 Message Date
Breadway
aad314c5ca Switch to tag-pinned bread-ecosystem deps; bump version to v0.1.1
All checks were successful
Mirror to GitHub / mirror (push) Successful in 2s
release / build (push) Successful in 25s
2026-07-19 03:52:53 +08:00
Breadway
5e4cbc83c9 Timeout-guard hyprctl JSON queries
hyprctl_json used a bare Command::new("hyprctl").output() with no timeout,
used by every geometry_* helper (region/output/window/active_window/
active_output selection) — an unresponsive hyprctl could block screenshot
capture indefinitely. Switched to bread_utils::proc::run_json (path
dependency for now, see the TODO in Cargo.toml).

grim/slurp/wl-copy calls deliberately left untouched: several pipe binary
image data through stdin/stdout (e.g. grim -> wl-copy), which
bread_utils::proc's current run_with_stdin only accepts as &str — adapting
those safely would need a bytes-flavored variant, out of scope for this
pass to avoid risking a regression in image piping.
2026-07-17 09:53:54 +08:00
Breadway
08c27d1b22 breadshot: expand ~ in save_dir, align Cargo.toml version with tag
- config.rs: expand a leading ~ in the save_dir config value on load.
  The documented example config (save_dir = "~/Pictures/Screenshots")
  was previously taken literally, silently creating ./~/Pictures/... in
  the current working directory. Added unit tests.
- Cargo.toml: 1.0.0 -> 0.1.0, matching the only existing tag (v0.1.0).
  breadshot --version previously reported nine releases ahead of the
  actual release history.
2026-07-17 03:19:50 +08:00
4 changed files with 100 additions and 36 deletions

65
Cargo.lock generated
View file

@ -82,11 +82,22 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bread-utils"
version = "0.3.0"
source = "git+https://git.breadway.dev/Breadway/bread-ecosystem?tag=v0.3.0#8e82d2d833e992ce939a5b836f910ee109f2e939"
dependencies = [
"dirs",
"serde",
"serde_json",
]
[[package]]
name = "breadshot"
version = "1.0.0"
version = "0.1.1"
dependencies = [
"anyhow",
"bread-utils",
"chrono",
"clap",
"dirs",
@ -105,9 +116,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cc"
version = "1.2.65"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8"
dependencies = [
"find-msvc-tools",
"shlex",
@ -134,9 +145,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.6.1"
version = "4.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011"
dependencies = [
"clap_builder",
"clap_derive",
@ -144,9 +155,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.6.0"
version = "4.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b"
dependencies = [
"anstream",
"anstyle",
@ -219,21 +230,21 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "futures-core"
version = "0.3.32"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
[[package]]
name = "futures-task"
version = "0.3.32"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
[[package]]
name = "futures-util"
version = "0.3.32"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
dependencies = [
"futures-core",
"futures-task",
@ -335,9 +346,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libredox"
version = "0.1.17"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
dependencies = [
"libc",
]
@ -359,9 +370,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.8.2"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "nu-ansi-term"
@ -436,9 +447,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.14"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
dependencies = [
"aho-corasick",
"memchr",
@ -453,9 +464,9 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "rustversion"
version = "1.0.22"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "serde"
@ -544,9 +555,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.118"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
@ -575,9 +586,9 @@ dependencies = [
[[package]]
name = "thread_local"
version = "1.1.9"
version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
dependencies = [
"cfg-if",
]
@ -898,6 +909,6 @@ dependencies = [
[[package]]
name = "zmij"
version = "1.0.21"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"

View file

@ -1,6 +1,6 @@
[package]
name = "breadshot"
version = "1.0.0"
version = "0.1.1"
edition = "2021"
license = "MIT"
authors = ["Breadway"]
@ -16,6 +16,7 @@ serde_json = "1"
toml = "0.8"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
bread-utils = { git = "https://git.breadway.dev/Breadway/bread-ecosystem", tag = "v0.3.0" }
[profile.release]
lto = "thin"

View file

@ -303,12 +303,9 @@ fn send_notification(title: &str, msg: &str, timeout: u32, path: &Path) {
// --- helpers ---
fn hyprctl_json(subcmd: &str) -> Result<Value> {
let out = Command::new("hyprctl")
.args(["-j", subcmd])
.output()
.context("running hyprctl")?;
serde_json::from_slice(&out.stdout)
.with_context(|| format!("parsing hyprctl {subcmd} output"))
// Was a bare Command::new("hyprctl").output() with no timeout.
bread_utils::proc::run_json("hyprctl", &["-j", subcmd], std::time::Duration::from_secs(3))
.with_context(|| format!("running/parsing hyprctl {subcmd}"))
}
fn slurp(args: &[&str]) -> Result<String> {

View file

@ -38,8 +38,10 @@ impl Config {
}
let content = std::fs::read_to_string(path)
.with_context(|| format!("reading {}", path.display()))?;
toml::from_str(&content)
.with_context(|| format!("parsing {}", path.display()))
let mut config: Self = toml::from_str(&content)
.with_context(|| format!("parsing {}", path.display()))?;
config.save_dir = expand_tilde(config.save_dir);
Ok(config)
}
}
@ -49,3 +51,56 @@ pub fn default_path() -> PathBuf {
.join("breadshot")
.join("config.toml")
}
/// Expand a leading `~` (or `~/...`) to the user's home directory, the way a
/// shell would. `PathBuf`'s `Deserialize` does no such expansion, so a
/// documented config value like `save_dir = "~/Pictures/Screenshots"` would
/// otherwise be taken literally and create a `./~/Pictures/Screenshots`
/// directory relative to the current working directory.
fn expand_tilde(path: PathBuf) -> PathBuf {
let Some(s) = path.to_str() else {
return path;
};
if s == "~" {
return dirs::home_dir().unwrap_or(path);
}
if let Some(rest) = s.strip_prefix("~/") {
if let Some(home) = dirs::home_dir() {
return home.join(rest);
}
}
path
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expand_tilde_prefix() {
let home = dirs::home_dir().unwrap();
assert_eq!(
expand_tilde(PathBuf::from("~/Pictures/Screenshots")),
home.join("Pictures/Screenshots")
);
}
#[test]
fn expand_tilde_bare() {
let home = dirs::home_dir().unwrap();
assert_eq!(expand_tilde(PathBuf::from("~")), home);
}
#[test]
fn expand_tilde_absolute_untouched() {
let p = PathBuf::from("/var/tmp/shots");
assert_eq!(expand_tilde(p.clone()), p);
}
#[test]
fn expand_tilde_no_expansion_mid_path() {
// Only a leading ~ is special, matching shell behavior.
let p = PathBuf::from("/home/user/~weird");
assert_eq!(expand_tilde(p.clone()), p);
}
}