├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.sh ├── sarge.yml ├── src ├── battery.rs ├── config.rs ├── lib.rs └── main.rs └── test_data └── confs ├── auto_none.yaml ├── garbage_value.yaml └── simple.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "arrayref" 5 | version = "0.3.6" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 8 | 9 | [[package]] 10 | name = "arrayvec" 11 | version = "0.5.2" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 14 | 15 | [[package]] 16 | name = "autocfg" 17 | version = "1.0.1" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 20 | 21 | [[package]] 22 | name = "base64" 23 | version = "0.13.0" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 26 | 27 | [[package]] 28 | name = "bitflags" 29 | version = "0.9.1" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" 32 | 33 | [[package]] 34 | name = "bitflags" 35 | version = "1.2.1" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 38 | 39 | [[package]] 40 | name = "blake2b_simd" 41 | version = "0.5.11" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" 44 | dependencies = [ 45 | "arrayref", 46 | "arrayvec", 47 | "constant_time_eq", 48 | ] 49 | 50 | [[package]] 51 | name = "block" 52 | version = "0.1.6" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 55 | 56 | [[package]] 57 | name = "cc" 58 | version = "1.0.66" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" 61 | 62 | [[package]] 63 | name = "cfg-if" 64 | version = "0.1.10" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 67 | 68 | [[package]] 69 | name = "cfg-if" 70 | version = "1.0.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 73 | 74 | [[package]] 75 | name = "chrono" 76 | version = "0.4.19" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 79 | dependencies = [ 80 | "libc", 81 | "num-integer", 82 | "num-traits", 83 | "time", 84 | "winapi 0.3.9", 85 | ] 86 | 87 | [[package]] 88 | name = "constant_time_eq" 89 | version = "0.1.5" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 92 | 93 | [[package]] 94 | name = "crossbeam-utils" 95 | version = "0.8.1" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" 98 | dependencies = [ 99 | "autocfg", 100 | "cfg-if 1.0.0", 101 | "lazy_static", 102 | ] 103 | 104 | [[package]] 105 | name = "dbus" 106 | version = "0.8.4" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "5cd9e78c210146a1860f897db03412fd5091fd73100778e43ee255cca252cf32" 109 | dependencies = [ 110 | "libc", 111 | "libdbus-sys", 112 | ] 113 | 114 | [[package]] 115 | name = "dirs" 116 | version = "1.0.5" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" 119 | dependencies = [ 120 | "libc", 121 | "redox_users", 122 | "winapi 0.3.9", 123 | ] 124 | 125 | [[package]] 126 | name = "dtoa" 127 | version = "0.4.6" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" 130 | 131 | [[package]] 132 | name = "error-chain" 133 | version = "0.12.4" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" 136 | dependencies = [ 137 | "version_check", 138 | ] 139 | 140 | [[package]] 141 | name = "filetime" 142 | version = "0.2.13" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" 145 | dependencies = [ 146 | "cfg-if 1.0.0", 147 | "libc", 148 | "redox_syscall", 149 | "winapi 0.3.9", 150 | ] 151 | 152 | [[package]] 153 | name = "fsevent" 154 | version = "0.4.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" 157 | dependencies = [ 158 | "bitflags 1.2.1", 159 | "fsevent-sys", 160 | ] 161 | 162 | [[package]] 163 | name = "fsevent-sys" 164 | version = "2.0.1" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" 167 | dependencies = [ 168 | "libc", 169 | ] 170 | 171 | [[package]] 172 | name = "fuchsia-zircon" 173 | version = "0.3.3" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 176 | dependencies = [ 177 | "bitflags 1.2.1", 178 | "fuchsia-zircon-sys", 179 | ] 180 | 181 | [[package]] 182 | name = "fuchsia-zircon-sys" 183 | version = "0.3.3" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 186 | 187 | [[package]] 188 | name = "getrandom" 189 | version = "0.1.15" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" 192 | dependencies = [ 193 | "cfg-if 0.1.10", 194 | "libc", 195 | "wasi 0.9.0+wasi-snapshot-preview1", 196 | ] 197 | 198 | [[package]] 199 | name = "inotify" 200 | version = "0.7.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" 203 | dependencies = [ 204 | "bitflags 1.2.1", 205 | "inotify-sys", 206 | "libc", 207 | ] 208 | 209 | [[package]] 210 | name = "inotify-sys" 211 | version = "0.1.4" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55" 214 | dependencies = [ 215 | "libc", 216 | ] 217 | 218 | [[package]] 219 | name = "iovec" 220 | version = "0.1.4" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 223 | dependencies = [ 224 | "libc", 225 | ] 226 | 227 | [[package]] 228 | name = "kernel32-sys" 229 | version = "0.2.2" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 232 | dependencies = [ 233 | "winapi 0.2.8", 234 | "winapi-build", 235 | ] 236 | 237 | [[package]] 238 | name = "lazy_static" 239 | version = "1.4.0" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 242 | 243 | [[package]] 244 | name = "lazycell" 245 | version = "1.3.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 248 | 249 | [[package]] 250 | name = "libc" 251 | version = "0.2.81" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" 254 | 255 | [[package]] 256 | name = "libdbus-sys" 257 | version = "0.2.1" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0" 260 | dependencies = [ 261 | "pkg-config", 262 | ] 263 | 264 | [[package]] 265 | name = "linked-hash-map" 266 | version = "0.5.3" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" 269 | 270 | [[package]] 271 | name = "log" 272 | version = "0.4.11" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 275 | dependencies = [ 276 | "cfg-if 0.1.10", 277 | ] 278 | 279 | [[package]] 280 | name = "mac-notification-sys" 281 | version = "0.3.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "3dfb6b71a9a89cd38b395d994214297447e8e63b1ba5708a9a2b0b1048ceda76" 284 | dependencies = [ 285 | "cc", 286 | "chrono", 287 | "dirs", 288 | "objc-foundation", 289 | ] 290 | 291 | [[package]] 292 | name = "malloc_buf" 293 | version = "0.0.6" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 296 | dependencies = [ 297 | "libc", 298 | ] 299 | 300 | [[package]] 301 | name = "mio" 302 | version = "0.6.23" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" 305 | dependencies = [ 306 | "cfg-if 0.1.10", 307 | "fuchsia-zircon", 308 | "fuchsia-zircon-sys", 309 | "iovec", 310 | "kernel32-sys", 311 | "libc", 312 | "log", 313 | "miow", 314 | "net2", 315 | "slab", 316 | "winapi 0.2.8", 317 | ] 318 | 319 | [[package]] 320 | name = "mio-extras" 321 | version = "2.0.6" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" 324 | dependencies = [ 325 | "lazycell", 326 | "log", 327 | "mio", 328 | "slab", 329 | ] 330 | 331 | [[package]] 332 | name = "miow" 333 | version = "0.2.2" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" 336 | dependencies = [ 337 | "kernel32-sys", 338 | "net2", 339 | "winapi 0.2.8", 340 | "ws2_32-sys", 341 | ] 342 | 343 | [[package]] 344 | name = "net2" 345 | version = "0.2.37" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" 348 | dependencies = [ 349 | "cfg-if 0.1.10", 350 | "libc", 351 | "winapi 0.3.9", 352 | ] 353 | 354 | [[package]] 355 | name = "notify" 356 | version = "4.0.15" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd" 359 | dependencies = [ 360 | "bitflags 1.2.1", 361 | "filetime", 362 | "fsevent", 363 | "fsevent-sys", 364 | "inotify", 365 | "libc", 366 | "mio", 367 | "mio-extras", 368 | "walkdir", 369 | "winapi 0.3.9", 370 | ] 371 | 372 | [[package]] 373 | name = "notify-rust" 374 | version = "4.0.0" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "144acee6a0543dc74893e4b8a33936b5b0a94cc2d4ab024afd0c6daff7afc3c0" 377 | dependencies = [ 378 | "dbus", 379 | "mac-notification-sys", 380 | "winrt-notification", 381 | ] 382 | 383 | [[package]] 384 | name = "num-integer" 385 | version = "0.1.44" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 388 | dependencies = [ 389 | "autocfg", 390 | "num-traits", 391 | ] 392 | 393 | [[package]] 394 | name = "num-traits" 395 | version = "0.2.14" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 398 | dependencies = [ 399 | "autocfg", 400 | ] 401 | 402 | [[package]] 403 | name = "objc" 404 | version = "0.2.7" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 407 | dependencies = [ 408 | "malloc_buf", 409 | ] 410 | 411 | [[package]] 412 | name = "objc-foundation" 413 | version = "0.1.1" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" 416 | dependencies = [ 417 | "block", 418 | "objc", 419 | "objc_id", 420 | ] 421 | 422 | [[package]] 423 | name = "objc_id" 424 | version = "0.1.1" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" 427 | dependencies = [ 428 | "objc", 429 | ] 430 | 431 | [[package]] 432 | name = "pkg-config" 433 | version = "0.3.19" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 436 | 437 | [[package]] 438 | name = "proc-macro2" 439 | version = "1.0.24" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 442 | dependencies = [ 443 | "unicode-xid 0.2.1", 444 | ] 445 | 446 | [[package]] 447 | name = "quote" 448 | version = "0.3.15" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" 451 | 452 | [[package]] 453 | name = "quote" 454 | version = "1.0.7" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 457 | dependencies = [ 458 | "proc-macro2", 459 | ] 460 | 461 | [[package]] 462 | name = "redox_syscall" 463 | version = "0.1.57" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 466 | 467 | [[package]] 468 | name = "redox_users" 469 | version = "0.3.5" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" 472 | dependencies = [ 473 | "getrandom", 474 | "redox_syscall", 475 | "rust-argon2", 476 | ] 477 | 478 | [[package]] 479 | name = "rust-argon2" 480 | version = "0.8.3" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" 483 | dependencies = [ 484 | "base64", 485 | "blake2b_simd", 486 | "constant_time_eq", 487 | "crossbeam-utils", 488 | ] 489 | 490 | [[package]] 491 | name = "same-file" 492 | version = "1.0.6" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 495 | dependencies = [ 496 | "winapi-util", 497 | ] 498 | 499 | [[package]] 500 | name = "sarge" 501 | version = "0.1.0" 502 | dependencies = [ 503 | "log", 504 | "notify", 505 | "notify-rust", 506 | "serde", 507 | "serde_yaml", 508 | "syslog", 509 | "yaml-rust", 510 | ] 511 | 512 | [[package]] 513 | name = "serde" 514 | version = "1.0.118" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" 517 | dependencies = [ 518 | "serde_derive", 519 | ] 520 | 521 | [[package]] 522 | name = "serde_derive" 523 | version = "1.0.118" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" 526 | dependencies = [ 527 | "proc-macro2", 528 | "quote 1.0.7", 529 | "syn 1.0.54", 530 | ] 531 | 532 | [[package]] 533 | name = "serde_yaml" 534 | version = "0.8.14" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7" 537 | dependencies = [ 538 | "dtoa", 539 | "linked-hash-map", 540 | "serde", 541 | "yaml-rust", 542 | ] 543 | 544 | [[package]] 545 | name = "slab" 546 | version = "0.4.2" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 549 | 550 | [[package]] 551 | name = "strum" 552 | version = "0.8.0" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "4ca6e4730f517e041e547ffe23d29daab8de6b73af4b6ae2a002108169f5e7da" 555 | 556 | [[package]] 557 | name = "strum_macros" 558 | version = "0.8.0" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "3384590878eb0cab3b128e844412e2d010821e7e091211b9d87324173ada7db8" 561 | dependencies = [ 562 | "quote 0.3.15", 563 | "syn 0.11.11", 564 | ] 565 | 566 | [[package]] 567 | name = "syn" 568 | version = "0.11.11" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" 571 | dependencies = [ 572 | "quote 0.3.15", 573 | "synom", 574 | "unicode-xid 0.0.4", 575 | ] 576 | 577 | [[package]] 578 | name = "syn" 579 | version = "1.0.54" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44" 582 | dependencies = [ 583 | "proc-macro2", 584 | "quote 1.0.7", 585 | "unicode-xid 0.2.1", 586 | ] 587 | 588 | [[package]] 589 | name = "synom" 590 | version = "0.11.3" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" 593 | dependencies = [ 594 | "unicode-xid 0.0.4", 595 | ] 596 | 597 | [[package]] 598 | name = "syslog" 599 | version = "5.0.0" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "9a5d8ef1b679c07976f3ee336a436453760c470f54b5e7237556728b8589515d" 602 | dependencies = [ 603 | "error-chain", 604 | "libc", 605 | "log", 606 | "time", 607 | ] 608 | 609 | [[package]] 610 | name = "time" 611 | version = "0.1.44" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 614 | dependencies = [ 615 | "libc", 616 | "wasi 0.10.0+wasi-snapshot-preview1", 617 | "winapi 0.3.9", 618 | ] 619 | 620 | [[package]] 621 | name = "unicode-xid" 622 | version = "0.0.4" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" 625 | 626 | [[package]] 627 | name = "unicode-xid" 628 | version = "0.2.1" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 631 | 632 | [[package]] 633 | name = "version_check" 634 | version = "0.9.2" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 637 | 638 | [[package]] 639 | name = "walkdir" 640 | version = "2.3.1" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" 643 | dependencies = [ 644 | "same-file", 645 | "winapi 0.3.9", 646 | "winapi-util", 647 | ] 648 | 649 | [[package]] 650 | name = "wasi" 651 | version = "0.9.0+wasi-snapshot-preview1" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 654 | 655 | [[package]] 656 | name = "wasi" 657 | version = "0.10.0+wasi-snapshot-preview1" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 660 | 661 | [[package]] 662 | name = "winapi" 663 | version = "0.2.8" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 666 | 667 | [[package]] 668 | name = "winapi" 669 | version = "0.3.9" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 672 | dependencies = [ 673 | "winapi-i686-pc-windows-gnu", 674 | "winapi-x86_64-pc-windows-gnu", 675 | ] 676 | 677 | [[package]] 678 | name = "winapi-build" 679 | version = "0.1.1" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 682 | 683 | [[package]] 684 | name = "winapi-i686-pc-windows-gnu" 685 | version = "0.4.0" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 688 | 689 | [[package]] 690 | name = "winapi-util" 691 | version = "0.1.5" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 694 | dependencies = [ 695 | "winapi 0.3.9", 696 | ] 697 | 698 | [[package]] 699 | name = "winapi-x86_64-pc-windows-gnu" 700 | version = "0.4.0" 701 | source = "registry+https://github.com/rust-lang/crates.io-index" 702 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 703 | 704 | [[package]] 705 | name = "winrt" 706 | version = "0.4.0" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "7e30cba82e22b083dc5a422c2ee77e20dc7927271a0dc981360c57c1453cb48d" 709 | dependencies = [ 710 | "winapi 0.3.9", 711 | ] 712 | 713 | [[package]] 714 | name = "winrt-notification" 715 | version = "0.2.2" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "6c31a65da50d792c6f9bd2e3216249566c4fb1d2d34f9b7d2d66d2e93f62a242" 718 | dependencies = [ 719 | "strum", 720 | "strum_macros", 721 | "winapi 0.3.9", 722 | "winrt", 723 | "xml-rs", 724 | ] 725 | 726 | [[package]] 727 | name = "ws2_32-sys" 728 | version = "0.2.1" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 731 | dependencies = [ 732 | "winapi 0.2.8", 733 | "winapi-build", 734 | ] 735 | 736 | [[package]] 737 | name = "xml-rs" 738 | version = "0.6.1" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "e1945e12e16b951721d7976520b0832496ef79c31602c7a29d950de79ba74621" 741 | dependencies = [ 742 | "bitflags 0.9.1", 743 | ] 744 | 745 | [[package]] 746 | name = "yaml-rust" 747 | version = "0.4.4" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" 750 | dependencies = [ 751 | "linked-hash-map", 752 | ] 753 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sarge" 3 | version = "1.0.0" 4 | authors = ["Deep Majumder "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | yaml-rust = "0.4.4" 11 | serde = { version = "1.0", features = ["derive"] } 12 | serde_yaml = "0.8" 13 | log = "0.4" 14 | syslog = "5.0.0" 15 | notify-rust = "4.0.0" 16 | notify = "4.0.15" 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Deep Majumder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sarge 2 | A laptop battery-status notification daemon written in Rust. 3 | 4 | ## How it works 5 | *Currently only supports Linux* 6 | 7 | Sarge reads the files in the `/sys/class/power_supply` directory, which contains information regarding the battery and power adapter. In its config file are defined a set of *triggers* which dictate when a notification will be shown. A trigger maybe defined for when a certain battery percentage is reached or for when the ac power is connected or disconnected. 8 | 9 | ## Dependencies 10 | Sarge uses D-Bus notifications to send its notifications. Desktop environments typically have this out of the box. If you use a discrete window-manager, then you might need to install a notification serer. I personally use [Dunst](https://github.com/dunst-project/dunst). For further information, please refer to [this](https://wiki.archlinux.org/index.php/Desktop_notifications) Arch Wiki page. 11 | 12 | Also, to build and install (or to use the script for that), you must have `git` and `cargo` installed and in your `$PATH`. For installing `cargo`, please look at the Rust [install](https://www.rust-lang.org/tools/install) documentation. 13 | 14 | ## Installation 15 | 16 | ### Build and install Script 17 | Sarge can be built and installed by the following command: 18 | ``` 19 | bash <(wget -qO- https://raw.githubusercontent.com/RedDocMD/Sarge/master/build.sh) 20 | ``` 21 | 22 | Alternatively, you can download the script from [here](https://raw.githubusercontent.com/RedDocMD/Sarge/master/build.sh) and run it. 23 | 24 | ### Manual build and install 25 | Suppose you are in `$HOME` when you perform the install. 26 | ``` 27 | git clone https://github.com/RedDocMD/Sarge 28 | cd Sarge 29 | cargo build --release 30 | ``` 31 | The binary will be formed in `target/release/sarge`. 32 | 33 | To automatically start this on login, make a file called `sarge.sh` in `/etc/profile.d` and make it executable. Add the line `$HOME/Sarge/target/release/sarge`, save and re-login. 34 | ``` 35 | cd /tmp 36 | touch sarge.sh 37 | echo "$HOME/Sarge/target/release/sarge &" > sarge.sh 38 | sudo bash 39 | cp sarge.sh /etc/profile.d/ 40 | chmod +x /etc/profle.d/sarge.sh 41 | exit 42 | ``` 43 | 44 | ## Configuration 45 | 46 | ### File location 47 | Sarge looks in the following files, in order, for its config: 48 | 49 | 1. `$XDG_CONFIG_HOME/sarge/sarge.yml` 50 | 2. `$XDG_CONFIG_HOME/sarge.yml` 51 | 3. `$HOME/.config/sarge/sarge.yml` 52 | 4. `$HOME/.sarge.yml` 53 | 54 | If a config file is not found, Sarge uses its own default config. The default config is provided [here](https://github.com/RedDocMD/Sarge/blob/master/sarge.yml). 55 | 56 | ### File format 57 | The config file is to be written in YAML. The keys are as follows: 58 | 59 | 1. `update_interval`: The time-interval, in milisecond, at which Sarge will update the battery info. 60 | 2. `triggers`: Array for the triggers, which is a set of the following keys: 61 | - `percentage`: The battery percentage at which the trigger takes place. 62 | - `when`: It is one of the following alternatives (**case matters**): 63 | * `Equal` - when the battery percentage becomes equal to `percentage` 64 | * `Above` - when the battery percentage goes above the `percentage` 65 | * `Below` - when the battery percentage goes below the `percentage` 66 | * `Charging` - when the AC adapter is plugged in. `percentage` key is ignored 67 | * `Discharging` - when the AC adapter is unplugged. `percentage` key is ignored 68 | 69 | Sarge supports *hot-reloading* of the config file, which means that it watches for changes in its config file. 70 | 71 | ## License 72 | Sarge is released under the [MIT](https://github.com/RedDocMD/Sarge/blob/master/LICENSE) license. 73 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Downloads Sarge from GitHub, builds it with Cargo and adds auto-start script to /etc/profile.d/ 4 | # Requires sudo for the last step 5 | 6 | URL=https://github.com/RedDocMD/Sarge # Git repo URL 7 | DEF_LOC=${HOME}/sarge # Default install location 8 | 9 | # Check for git 10 | if ! command -v git &> /dev/null; then 11 | echo "Please install git and run this script again" 12 | exit 1 13 | fi 14 | 15 | # Check for cargo 16 | if ! command -v cargo &> /dev/null; then 17 | echo "Please install Cargo and run this script again" 18 | exit 1 19 | fi 20 | 21 | # Get install location 22 | echo -n "Enter location to download Sarge [${DEF_LOC}]: " 23 | read LOC 24 | if [[ $LOC == "" ]]; then 25 | LOC=${DEF_LOC} 26 | fi 27 | 28 | # Clone repo 29 | echo "" 30 | echo "Cloning into repo ..." 31 | git clone "$URL" ${LOC} 32 | 33 | # Now to build Sarge 34 | echo "" 35 | echo "Building Sarge ...." 36 | cd $LOC 37 | cargo build --release 38 | 39 | # Go to /tmp and make the auto-start file 40 | echo "" 41 | echo "Creating auto-start script in /tmp" 42 | cd /tmp 43 | if ! command -v mktemp &> /dev/null; then 44 | TMP_NAME="sarge.sh" 45 | else 46 | TMP_NAME=$(mktemp "sarge-XXXX.sh") 47 | fi 48 | echo "$LOC/target/release/sarge &" >> ${TMP_NAME} 49 | chmod +x $TMP_NAME 50 | 51 | # Copying auto-start script to /etc/profile.d (requires sudo) 52 | echo "Copying start script to /etc/profile.d" 53 | sudo cp -pn $TMP_NAME /etc/profile.d/$TMP_NAME 54 | 55 | exit 0 56 | -------------------------------------------------------------------------------- /sarge.yml: -------------------------------------------------------------------------------- 1 | update_interval: 1000 2 | 3 | triggers: 4 | - percentage: 100 5 | when: Equal 6 | message: Battery full 7 | 8 | - percentage: 20 9 | when: Below 10 | message: Battery low - less than 20% remaining 11 | 12 | - percentage: ~ 13 | when: Charging 14 | message: Battery charging 15 | 16 | - percentage: ~ 17 | when: Discharging 18 | message: Battery discharging 19 | -------------------------------------------------------------------------------- /src/battery.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fs; 3 | use std::fs::File; 4 | use std::io; 5 | use std::io::prelude::*; 6 | use std::path::{Path, PathBuf}; 7 | 8 | #[derive(Debug)] 9 | pub struct BatteryInfo { 10 | pub charging: bool, 11 | pub percentage: i32, 12 | pub charge_now: i32, 13 | pub charge_full: i32, 14 | } 15 | 16 | pub struct InfoDirectories { 17 | battery: PathBuf, 18 | ac: PathBuf, 19 | } 20 | 21 | impl InfoDirectories { 22 | pub fn read() -> io::Result> { 23 | let power_supply = Path::new("/sys/class/power_supply"); 24 | let mut battery_path = None; 25 | let mut ac_path = None; 26 | for entry in fs::read_dir(power_supply)? { 27 | let entry = entry?; 28 | let path = entry.path(); 29 | if path.is_dir() { 30 | let name = path.to_str().unwrap(); 31 | if name.contains("BAT") { 32 | battery_path = Some(path); 33 | } else if name.contains("AC") { 34 | ac_path = Some(path); 35 | } 36 | } 37 | } 38 | if let (Some(bat), Some(ac)) = (battery_path, ac_path) { 39 | Ok(Some(Self { 40 | battery: bat, 41 | ac: ac, 42 | })) 43 | } else { 44 | Ok(None) 45 | } 46 | } 47 | } 48 | 49 | impl BatteryInfo { 50 | pub fn from(loc: &InfoDirectories) -> Result> { 51 | let charge_now_path = loc.battery.join(Path::new("charge_now")); 52 | let charge_full_path = loc.battery.join(Path::new("charge_full")); 53 | let percentage_path = loc.battery.join(Path::new("capacity")); 54 | let charging_path = loc.ac.join(Path::new("online")); 55 | 56 | let mut charge_now = String::new(); 57 | let mut charge_full = String::new(); 58 | let mut percentage = String::new(); 59 | let mut charging = String::new(); 60 | 61 | File::open(charge_now_path)?.read_to_string(&mut charge_now)?; 62 | File::open(charge_full_path)?.read_to_string(&mut charge_full)?; 63 | File::open(percentage_path)?.read_to_string(&mut percentage)?; 64 | File::open(charging_path)?.read_to_string(&mut charging)?; 65 | 66 | Ok(Self { 67 | charge_now: charge_now.trim().parse()?, 68 | charge_full: charge_full.trim().parse()?, 69 | percentage: percentage.trim().parse()?, 70 | charging: charging.trim() == String::from("1"), 71 | }) 72 | } 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | 79 | #[test] 80 | fn test_info_dir() -> io::Result<()> { 81 | let dirs = InfoDirectories::read()?; 82 | assert_eq!(dirs.is_some(), true); 83 | if let Some(dirs) = dirs { 84 | assert_eq!(dirs.battery.is_dir(), true); 85 | assert_eq!(dirs.ac.is_dir(), true); 86 | } 87 | Ok(()) 88 | } 89 | 90 | #[test] 91 | fn test_info() -> Result<(), Box> { 92 | let dirs = InfoDirectories::read()?.unwrap(); 93 | let info = BatteryInfo::from(&dirs)?; 94 | println!("{:?}", info); 95 | Ok(()) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use super::battery::*; 2 | use serde::{Deserialize, Serialize}; 3 | use std::error::Error; 4 | use std::fs::File; 5 | use std::io::prelude::*; 6 | use std::path::Path; 7 | use std::time::Duration; 8 | 9 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 10 | enum TriggerType { 11 | Above, 12 | Below, 13 | Equal, 14 | Charging, 15 | Discharging, 16 | } 17 | 18 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 19 | struct Trigger { 20 | percentage: Option, 21 | when: TriggerType, 22 | message: String, 23 | } 24 | 25 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 26 | pub struct Config { 27 | update_interval: i32, // In milisecond 28 | triggers: Vec, 29 | } 30 | 31 | impl Config { 32 | pub fn default() -> Self { 33 | let triggers = vec![ 34 | Trigger { 35 | percentage: Some(100), 36 | when: TriggerType::Equal, 37 | message: String::from("Battery full"), 38 | }, 39 | Trigger { 40 | percentage: Some(20), 41 | when: TriggerType::Below, 42 | message: String::from("Battery low - less than 20% remaining"), 43 | }, 44 | Trigger { 45 | percentage: None, 46 | when: TriggerType::Charging, 47 | message: String::from("Battery charging"), 48 | }, 49 | Trigger { 50 | percentage: None, 51 | when: TriggerType::Discharging, 52 | message: String::from("Battery discharging"), 53 | }, 54 | ]; 55 | Self { 56 | triggers, 57 | update_interval: 1000, 58 | } 59 | } 60 | 61 | pub fn from_file(path: &Path) -> Result> { 62 | let mut file = File::open(path)?; 63 | let mut content = String::new(); 64 | file.read_to_string(&mut content)?; 65 | let mut config: Config = serde_yaml::from_str(&content)?; 66 | for trigger in &mut config.triggers { 67 | match trigger.when { 68 | TriggerType::Charging | TriggerType::Discharging => trigger.percentage = None, 69 | _ => {} 70 | }; 71 | } 72 | Ok(config) 73 | } 74 | 75 | pub fn messages(&self, old: &BatteryInfo, new: &BatteryInfo) -> Vec { 76 | let mut msgs = Vec::new(); 77 | for trigger in &self.triggers { 78 | match trigger.when { 79 | TriggerType::Above => { 80 | if old.percentage <= trigger.percentage.unwrap() 81 | && new.percentage > trigger.percentage.unwrap() 82 | { 83 | msgs.push(trigger.message.clone()); 84 | } 85 | } 86 | TriggerType::Below => { 87 | if old.percentage >= trigger.percentage.unwrap() 88 | && new.percentage < trigger.percentage.unwrap() 89 | { 90 | msgs.push(trigger.message.clone()); 91 | } 92 | } 93 | TriggerType::Equal => { 94 | if old.percentage != trigger.percentage.unwrap() 95 | && new.percentage == trigger.percentage.unwrap() 96 | { 97 | msgs.push(trigger.message.clone()); 98 | } 99 | } 100 | TriggerType::Charging => { 101 | if !old.charging && new.charging { 102 | msgs.push(trigger.message.clone()); 103 | } 104 | } 105 | TriggerType::Discharging => { 106 | if old.charging && !new.charging { 107 | msgs.push(trigger.message.clone()); 108 | } 109 | } 110 | }; 111 | } 112 | msgs 113 | } 114 | 115 | pub fn intv(&self) -> Duration { 116 | Duration::from_millis(self.update_interval as u64) 117 | } 118 | 119 | pub fn update(&mut self, new: &mut Self) { 120 | self.update_interval = new.update_interval; 121 | self.triggers.clear(); 122 | self.triggers.append(&mut new.triggers); 123 | } 124 | } 125 | 126 | #[cfg(test)] 127 | mod test { 128 | use super::*; 129 | use std::path::PathBuf; 130 | 131 | #[test] 132 | fn simple_conf() -> Result<(), Box> { 133 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 134 | path.push("test_data/confs/simple.yaml"); 135 | let config = Config::from_file(&path)?; 136 | 137 | let expected_triggers = vec![ 138 | Trigger { 139 | percentage: Some(20), 140 | when: TriggerType::Below, 141 | message: String::from("Battery low"), 142 | }, 143 | Trigger { 144 | percentage: Some(100), 145 | when: TriggerType::Equal, 146 | message: String::from("Fully charged"), 147 | }, 148 | Trigger { 149 | percentage: None, 150 | when: TriggerType::Discharging, 151 | message: String::from("Battery discharging"), 152 | }, 153 | ]; 154 | 155 | assert_eq!(expected_triggers, config.triggers); 156 | assert_eq!(config.update_interval, 500); 157 | Ok(()) 158 | } 159 | 160 | #[test] 161 | fn auto_none_conf() -> Result<(), Box> { 162 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 163 | path.push("test_data/confs/auto_none.yaml"); 164 | let config = Config::from_file(&path)?; 165 | 166 | let expected_triggers = vec![Trigger { 167 | percentage: None, 168 | when: TriggerType::Charging, 169 | message: String::from("Battery charging"), 170 | }]; 171 | 172 | assert_eq!(expected_triggers, config.triggers); 173 | Ok(()) 174 | } 175 | 176 | #[test] 177 | fn garbage_value_conf() -> Result<(), Box> { 178 | let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 179 | path.push("test_data/confs/garbage_value.yaml"); 180 | assert!( 181 | Config::from_file(&path).is_err(), 182 | "shouldn't accept wrong key for when" 183 | ); 184 | Ok(()) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod battery; 2 | pub mod config; 3 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use log::{error, warn, LevelFilter}; 2 | use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; 3 | use notify_rust::Notification; 4 | use sarge::battery::*; 5 | use sarge::config::*; 6 | use std::env; 7 | use std::path::PathBuf; 8 | use std::process; 9 | use std::sync::mpsc::channel; 10 | use std::sync::{Arc, Mutex}; 11 | use std::thread; 12 | use std::time::Duration; 13 | use syslog::{BasicLogger, Facility, Formatter3164}; 14 | 15 | fn main() { 16 | if env::consts::OS != "linux" { 17 | println!("Only supported on Linux"); 18 | process::exit(1); 19 | } 20 | 21 | let formatter = Formatter3164 { 22 | facility: Facility::LOG_USER, 23 | hostname: None, 24 | process: "sarge".into(), 25 | pid: 0, 26 | }; 27 | 28 | let logger = syslog::unix(formatter).expect("could not connect to syslog"); 29 | log::set_boxed_logger(Box::new(BasicLogger::new(logger))) 30 | .map(|()| log::set_max_level(LevelFilter::Warn)) 31 | .expect("logger setup error"); 32 | 33 | let info_dir = match InfoDirectories::read() { 34 | Err(e) => { 35 | error!("{}", e); 36 | process::exit(1); 37 | } 38 | Ok(s) => match s { 39 | None => { 40 | error!("/sys/class/power_supply/BAT* or /sys/class/power_supply/ACAD not found"); 41 | process::exit(1); 42 | } 43 | Some(s) => s, 44 | }, 45 | }; 46 | 47 | let (config_raw, config_path) = get_config(); 48 | let config_rc = Arc::from(Mutex::new(config_raw)); 49 | 50 | let (tx, rx) = channel(); 51 | let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(5)).unwrap(); 52 | if let Some(config_path) = config_path { 53 | watcher 54 | .watch(config_path, RecursiveMode::Recursive) 55 | .unwrap(); 56 | 57 | let config_rc = Arc::clone(&config_rc); 58 | thread::spawn(move || loop { 59 | let event = rx.recv().unwrap(); 60 | match event { 61 | DebouncedEvent::Write(path) => { 62 | if let Ok(conf) = &mut Config::from_file(&path) { 63 | let mut config = config_rc.lock().unwrap(); 64 | config.update(conf); 65 | } 66 | } 67 | _ => {} 68 | } 69 | }); 70 | } else { 71 | warn!("no config file found; choosing to default config"); 72 | } 73 | 74 | let mut old_info = get_info(&info_dir); 75 | let config = config_rc.lock().unwrap(); 76 | thread::sleep(config.intv()); 77 | drop(config); 78 | let mut new_info = get_info(&info_dir); 79 | 80 | loop { 81 | let config = config_rc.lock().unwrap(); 82 | let msgs = config.messages(&old_info, &new_info); 83 | if msgs.len() != 0 { 84 | for msg in &msgs { 85 | match Notification::new().summary("sarge").body(msg).show() { 86 | Err(e) => error!("{}", e), 87 | _ => {} 88 | }; 89 | } 90 | } 91 | thread::sleep(config.intv()); 92 | drop(config); 93 | old_info = new_info; 94 | new_info = get_info(&info_dir); 95 | } 96 | } 97 | 98 | fn get_config() -> (Config, Option) { 99 | if let Some(xdg_config_home) = env::var_os("XDG_CONFIG_HOME") { 100 | let mut path = PathBuf::from(xdg_config_home); 101 | path.push("sarge"); 102 | path.push("sarge.yml"); 103 | if path.exists() { 104 | match Config::from_file(&path) { 105 | Ok(c) => return (c, Some(path)), 106 | Err(e) => warn!("error in {}: {}", path.display(), e), 107 | }; 108 | } else { 109 | path.pop(); 110 | path.pop(); 111 | path.push("sarge.yml"); 112 | if path.exists() { 113 | match Config::from_file(&path) { 114 | Ok(c) => return (c, Some(path)), 115 | Err(e) => warn!("error in {}: {}", path.display(), e), 116 | }; 117 | } 118 | } 119 | } 120 | if let Some(home) = env::var_os("HOME") { 121 | let mut path = PathBuf::from(home); 122 | path.push(".config"); 123 | path.push("sarge"); 124 | path.push("sarge.yml"); 125 | if path.exists() { 126 | match Config::from_file(&path) { 127 | Ok(c) => return (c, Some(path)), 128 | Err(e) => warn!("error in {}: {}", path.display(), e), 129 | }; 130 | } else { 131 | path.pop(); 132 | path.pop(); 133 | path.pop(); 134 | path.push(".sarge.yml"); 135 | if path.exists() { 136 | match Config::from_file(&path) { 137 | Ok(c) => return (c, Some(path)), 138 | Err(e) => warn!("error in {}: {}", path.display(), e), 139 | }; 140 | } 141 | } 142 | } 143 | (Config::default(), None) 144 | } 145 | 146 | fn get_info(info_dir: &InfoDirectories) -> BatteryInfo { 147 | match BatteryInfo::from(info_dir) { 148 | Ok(s) => s, 149 | Err(e) => { 150 | error!("{}", e); 151 | process::exit(1); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /test_data/confs/auto_none.yaml: -------------------------------------------------------------------------------- 1 | triggers: 2 | - percentage: 50 3 | when: Charging 4 | message: Battery charging 5 | 6 | update_interval: 500 7 | -------------------------------------------------------------------------------- /test_data/confs/garbage_value.yaml: -------------------------------------------------------------------------------- 1 | triggers: 2 | - percentage: 50 3 | when: Hellya 4 | message: Battery charging 5 | -------------------------------------------------------------------------------- /test_data/confs/simple.yaml: -------------------------------------------------------------------------------- 1 | triggers: 2 | - percentage: 20 3 | when: Below 4 | message: Battery low 5 | 6 | - percentage: 100 7 | when: Equal 8 | message: Fully charged 9 | 10 | - percentage: ~ 11 | when: Discharging 12 | message: Battery discharging 13 | 14 | update_interval: 500 15 | --------------------------------------------------------------------------------