diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 095d59c..87be035 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,15 @@ on: [push] jobs: build: - runs-on: ubuntu-latest + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + include: + - os: macos-latest + features: sketchybar + + runs-on: ${{ matrix.os }} + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -17,9 +25,14 @@ jobs: - name: Setup Cache uses: Swatinem/rust-cache@v2 - - name: Build crate + - if: ${{ !matrix.features }} + name: Build crate run: cargo build + - if: ${{ matrix.features }} + name: Build crate + run: cargo build --features ${{ matrix.features }} + - name: Run tests run: cargo test diff --git a/Cargo.lock b/Cargo.lock index 809f69a..b3a7faa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,9 +178,12 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -246,6 +249,7 @@ dependencies = [ "log", "rustls", "serde", + "sketchybar-rs", "sqlx", "tempfile", "thiserror", @@ -1300,6 +1304,16 @@ dependencies = [ "rand_core", ] +[[package]] +name = "sketchybar-rs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c26ccb881a9573154fb6d8a59838d0d136581086c5d92f1005b5763d2325e214" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "slab" version = "0.4.9" diff --git a/Cargo.toml b/Cargo.toml index 95a0e72..1e0cec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,10 +20,14 @@ lazy_static = "1.4.0" log = "0.4.20" rustls = "0.21.7" serde = { version = "1", features = ["derive"] } -thiserror = "1.0.49" -ureq = { version = "2.8.0", features = ["json"] } +sketchybar-rs = { version = "0.1.0", optional = true } sqlx = { version = "0.7", features = ["macros", "runtime-tokio", "sqlite"] } +thiserror = "1.0.49" tokio = { version = "1.33", features = ["macros"] } +ureq = { version = "2.8.0", features = ["json"] } [dev-dependencies] tempfile = "3.8.0" + +[features] +sketchybar = ["sketchybar-rs"] diff --git a/README.md b/README.md index b6591be..e4654b8 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,11 @@ conditions location set "10001, usa" ### SketchyBar -Here's how I'm using this with SketchyBar. +[SketchyBar](https://github.com/FelixKratz/SketchyBar) is a very customizable bar app for macOS that is deliberately light on resource usage (written in C vs more common WebView approach). Widgets in SketchyBar are refreshed (if need be) via "plugins" (typically shell scripts). + +#### Manual + +Here's how I'm using `conditions` with SketchyBar in the more traditional plugin approach. ```bash #!/bin/bash @@ -74,6 +78,44 @@ sketchybar -m \ --set weather label="${temp}°F" ``` +#### Direct Integration + +Alternatively `conditions` can directly update the running instance of SketchyBar without making calls from a shell script using `sketchybar -m`. In order to use this experimental version, the `sketchybar` feature must be enabled on install. + +```bash +cargo install conditions --features sketchybar +``` + +Then when setting up a weather widget in `sketchybarrc`, `conditions` can be invoked directly. + +```bash +sketchybar \ + --add item weather right \ + update_freq=1800 \ + icon.drawing=off \ + script="$HOME/.cargo/bin/conditions current --icon=" + +sketchybar \ + --add item weather_logo right \ + icon.font="Hack Nerd Font:Regular:14.0" \ + label.drawing=off \ +``` + +Note, at this time `conditions` is hard-coded with widgets named `weather` and `weather_logo`: + +```rust +let message = format!( + "--set weather_logo icon=\"{}\" --set weather label=\"{}°{}\"", + conditions.icon, + conditions.temp_f as i32, + self.config.unit.as_char().to_ascii_uppercase() +); +``` + +In my bar this results in: + +![example SketchyBar](./assets/sketchybar-example.png) + ## Tasks Run tasks from this directory via: `xc [task-name]` diff --git a/assets/sketchybar-example.png b/assets/sketchybar-example.png new file mode 100644 index 0000000..53bfba2 Binary files /dev/null and b/assets/sketchybar-example.png differ diff --git a/src/conditions.rs b/src/conditions.rs index 4925128..b8a02ce 100644 --- a/src/conditions.rs +++ b/src/conditions.rs @@ -35,6 +35,10 @@ impl Conditions { }; let conditions = CurrentConditions::get(&self.config, &location)?; + + #[cfg(feature = "sketchybar")] + self.notify_sketchybar(&conditions)?; + let output = self.to_output(conditions); Ok(ureq::serde_json::to_string(&output)?) @@ -53,6 +57,26 @@ impl Conditions { Output { temp, icon } } + + #[cfg(feature = "sketchybar")] + fn notify_sketchybar( + &self, + conditions: &CurrentConditions, + ) -> eyre::Result { + #[allow(clippy::cast_possible_truncation)] + // TODO: this shouldn't be hard-code. Consider additional CLI args + let message = format!( + "--set weather_logo icon=\"{}\" --set weather label=\"{}°{}\"", + conditions.icon, + conditions.temp_f as i32, + self.config.unit.as_char().to_ascii_uppercase() + ); + + match sketchybar_rs::message(&message) { + Ok(_) => Ok(String::new()), + Err(err) => Err(eyre::eyre!(err.to_string())), + } + } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 26bc1b3..4573c31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ #![deny(clippy::pedantic)] +#[cfg(feature = "sketchybar")] +extern crate sketchybar_rs; + use clap::Parser; use args::{