From aee7ab9066e81361b34c0eb5426a7024b3e4859a Mon Sep 17 00:00:00 2001
From: Sibi Prabakaran
Date: Sat, 7 Feb 2026 14:52:36 +0530
Subject: [PATCH 1/9] refactor: replace chrono with jiff for time handling
Migrate the time-handling logic from chrono to jiff. This updates
the public API and internal state to use Zoned and Span types,
providing better handling of durations and timezones.
The change also includes updates to anyhow and axum dependencies
to keep the project current with recent security and performance
updates.
---
Cargo.lock | 416 +++++++++-------------------------------
Cargo.toml | 14 +-
examples/leaderboard.rs | 6 +-
rust-toolchain.toml | 2 +-
src/lib.rs | 65 ++++---
5 files changed, 136 insertions(+), 367 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 4ff3cef..b218794 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,41 +2,11 @@
# It is not intended for manual editing.
version = 4
-[[package]]
-name = "addr2line"
-version = "0.24.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler2"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
-
-[[package]]
-name = "android-tzdata"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
-
-[[package]]
-name = "android_system_properties"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
-dependencies = [
- "libc",
-]
-
[[package]]
name = "anyhow"
-version = "1.0.99"
+version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
+checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
[[package]]
name = "askama"
@@ -80,17 +50,11 @@ dependencies = [
"winnow",
]
-[[package]]
-name = "autocfg"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
-
[[package]]
name = "axum"
-version = "0.8.4"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5"
+checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
dependencies = [
"axum-core",
"bytes",
@@ -107,8 +71,7 @@ dependencies = [
"mime",
"percent-encoding",
"pin-project-lite",
- "rustversion",
- "serde",
+ "serde_core",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
@@ -122,9 +85,9 @@ dependencies = [
[[package]]
name = "axum-core"
-version = "0.5.2"
+version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
+checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
dependencies = [
"bytes",
"futures-core",
@@ -133,28 +96,12 @@ dependencies = [
"http-body-util",
"mime",
"pin-project-lite",
- "rustversion",
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
-[[package]]
-name = "backtrace"
-version = "0.3.75"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
-dependencies = [
- "addr2line",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
- "windows-targets",
-]
-
[[package]]
name = "basic-toml"
version = "0.1.10"
@@ -170,54 +117,18 @@ version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
-[[package]]
-name = "bumpalo"
-version = "3.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
-
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
-[[package]]
-name = "cc"
-version = "1.2.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e"
-dependencies = [
- "shlex",
-]
-
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
-[[package]]
-name = "chrono"
-version = "0.4.41"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
-dependencies = [
- "android-tzdata",
- "iana-time-zone",
- "js-sys",
- "num-traits",
- "serde",
- "wasm-bindgen",
- "windows-link",
-]
-
-[[package]]
-name = "core-foundation-sys"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
-
[[package]]
name = "fnv"
version = "1.0.7"
@@ -278,12 +189,6 @@ dependencies = [
"wasi 0.14.2+wasi-0.2.4",
]
-[[package]]
-name = "gimli"
-version = "0.31.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
-
[[package]]
name = "http"
version = "1.3.1"
@@ -366,45 +271,51 @@ dependencies = [
]
[[package]]
-name = "iana-time-zone"
-version = "0.1.63"
+name = "itoa"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "jiff"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89a5b5e10d5a9ad6e5d1f4bd58225f655d6fe9767575a5e8ac5a6fe64e04495"
dependencies = [
- "android_system_properties",
- "core-foundation-sys",
- "iana-time-zone-haiku",
- "js-sys",
+ "jiff-static",
+ "jiff-tzdb-platform",
"log",
- "wasm-bindgen",
- "windows-core",
+ "portable-atomic",
+ "portable-atomic-util",
+ "serde_core",
+ "windows-sys 0.59.0",
]
[[package]]
-name = "iana-time-zone-haiku"
-version = "0.1.2"
+name = "jiff-static"
+version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+checksum = "ff7a39c8862fc1369215ccf0a8f12dd4598c7f6484704359f0351bd617034dbf"
dependencies = [
- "cc",
+ "proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "io-uring"
-version = "0.7.9"
+name = "jiff-tzdb"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
-dependencies = [
- "bitflags",
- "cfg-if",
- "libc",
-]
+checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2"
[[package]]
-name = "itoa"
-version = "1.0.15"
+name = "jiff-tzdb-platform"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
+dependencies = [
+ "jiff-tzdb",
+]
[[package]]
name = "job-watcher"
@@ -413,7 +324,7 @@ dependencies = [
"anyhow",
"askama",
"axum",
- "chrono",
+ "jiff",
"rand",
"serde",
"tokio",
@@ -422,16 +333,6 @@ dependencies = [
"tracing",
]
-[[package]]
-name = "js-sys"
-version = "0.3.77"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
-dependencies = [
- "once_cell",
- "wasm-bindgen",
-]
-
[[package]]
name = "libc"
version = "0.2.175"
@@ -462,15 +363,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
-[[package]]
-name = "miniz_oxide"
-version = "0.8.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
-dependencies = [
- "adler2",
-]
-
[[package]]
name = "mio"
version = "1.0.4"
@@ -479,25 +371,7 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
- "windows-sys",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "object"
-version = "0.36.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
-dependencies = [
- "memchr",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -524,6 +398,21 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+[[package]]
+name = "portable-atomic"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
+
+[[package]]
+name = "portable-atomic-util"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5"
+dependencies = [
+ "portable-atomic",
+]
+
[[package]]
name = "ppv-lite86"
version = "0.2.21"
@@ -586,24 +475,12 @@ dependencies = [
"getrandom",
]
-[[package]]
-name = "rustc-demangle"
-version = "0.1.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
-
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
-[[package]]
-name = "rustversion"
-version = "1.0.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
-
[[package]]
name = "ryu"
version = "1.0.20"
@@ -612,18 +489,28 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "serde"
-version = "1.0.219"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.219"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
@@ -664,18 +551,6 @@ dependencies = [
"serde",
]
-[[package]]
-name = "shlex"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
-
-[[package]]
-name = "slab"
-version = "0.4.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
-
[[package]]
name = "smallvec"
version = "1.15.1"
@@ -689,7 +564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
- "windows-sys",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -711,26 +586,23 @@ checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
[[package]]
name = "tokio"
-version = "1.47.1"
+version = "1.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
+checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
dependencies = [
- "backtrace",
- "io-uring",
"libc",
"mio",
"pin-project-lite",
- "slab",
"socket2",
"tokio-macros",
- "windows-sys",
+ "windows-sys 0.61.2",
]
[[package]]
name = "tokio-macros"
-version = "2.5.0"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
+checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
@@ -739,9 +611,9 @@ dependencies = [
[[package]]
name = "tower"
-version = "0.5.2"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
+checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
dependencies = [
"futures-core",
"futures-util",
@@ -755,9 +627,9 @@ dependencies = [
[[package]]
name = "tower-http"
-version = "0.6.6"
+version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
+checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
dependencies = [
"bitflags",
"bytes",
@@ -785,9 +657,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
-version = "0.1.41"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"log",
"pin-project-lite",
@@ -797,9 +669,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.30"
+version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
@@ -808,9 +680,9 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.34"
+version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
@@ -836,130 +708,28 @@ dependencies = [
"wit-bindgen-rt",
]
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
-dependencies = [
- "cfg-if",
- "once_cell",
- "rustversion",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
-dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.100"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "windows-core"
-version = "0.61.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
-dependencies = [
- "windows-implement",
- "windows-interface",
- "windows-link",
- "windows-result",
- "windows-strings",
-]
-
-[[package]]
-name = "windows-implement"
-version = "0.60.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "windows-interface"
-version = "0.59.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "windows-link"
-version = "0.1.3"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
-name = "windows-result"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
-dependencies = [
- "windows-link",
-]
-
-[[package]]
-name = "windows-strings"
-version = "0.4.2"
+name = "windows-sys"
+version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
- "windows-link",
+ "windows-targets",
]
[[package]]
name = "windows-sys"
-version = "0.59.0"
+version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
- "windows-targets",
+ "windows-link",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 431daac..93a1f25 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,18 +4,18 @@ version = "0.1.0"
edition = "2024"
[dependencies]
-anyhow = "1.0.99"
+anyhow = "1.0.101"
askama = "0.14.0"
-axum = { version = "0.8.4", features = ["json"] }
-chrono = { version = "0.4.41", features = ["serde"] }
+axum = { version = "0.8.8", features = ["json"] }
rand = "0.9.2"
serde = { version = "1.0.219", features = ["derive", "rc"] }
-tokio = { version = "1.47.1", features = ["rt-multi-thread"]}
-tower = "0.5.2"
-tower-http = { version = "0.6.6", features = [
+jiff = { version = "0.2.19", features = ["serde"]}
+tokio = { version = "1.49.0", features = ["rt-multi-thread"]}
+tower = "0.5.3"
+tower-http = { version = "0.6.8", features = [
"cors",
"limit",
"timeout",
"trace",
] }
-tracing = "0.1.41"
+tracing = "0.1.44"
diff --git a/examples/leaderboard.rs b/examples/leaderboard.rs
index 864d7b0..6ca424b 100644
--- a/examples/leaderboard.rs
+++ b/examples/leaderboard.rs
@@ -1,5 +1,5 @@
use anyhow::{Result, bail};
-use chrono::{DateTime, Utc};
+use jiff::Zoned;
use job_watcher::{
AppBuilder, Heartbeat, TaskLabel, WatchedTask, WatchedTaskOutput, WatcherAppContext,
config::{Delay, TaskConfig, WatcherConfig},
@@ -39,8 +39,8 @@ impl WatcherAppContext for DummyApp {
Some("test-env".to_string())
}
- fn live_since(&self) -> DateTime {
- Utc::now()
+ fn live_since(&self) -> Zoned {
+ Zoned::now()
}
fn watcher_config(&self) -> WatcherConfig {
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index c1bc0a6..ff100ed 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,2 +1,2 @@
[toolchain]
-channel = "1.85.0"
+channel = "1.90.0"
diff --git a/src/lib.rs b/src/lib.rs
index b36cca0..01f80b5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -30,8 +30,8 @@ use axum::{
http::{self, HeaderValue},
response::IntoResponse,
};
-use chrono::{DateTime, Duration, Utc};
use config::{Delay, WatcherConfig};
+use jiff::{Span, Zoned};
use rand::Rng;
use std::fmt::Write;
use std::{borrow::Cow, collections::HashMap, fmt::Display, pin::Pin, sync::Arc, time::Instant};
@@ -41,7 +41,7 @@ pub trait WatcherAppContext {
fn title(&self) -> String;
fn environment(&self) -> Option;
fn build_version(&self) -> Option;
- fn live_since(&self) -> DateTime;
+ fn live_since(&self) -> Zoned;
fn watcher_config(&self) -> WatcherConfig;
fn triggers_alert(&self, label: &TaskLabel, selected_label: Option<&TaskLabel>) -> bool;
fn show_output(&self, label: &TaskLabel) -> bool;
@@ -52,7 +52,6 @@ pub trait WatcherAppContext {
)]
#[serde(transparent)]
pub struct TaskLabel(String);
-
impl TaskLabel {
pub fn new(s: impl Into) -> Self {
Self(s.into())
@@ -98,7 +97,7 @@ impl TaskResultValue {
#[serde(rename_all = "kebab-case")]
pub struct TaskResult {
pub value: Arc,
- pub updated: DateTime,
+ pub updated: Zoned,
}
#[derive(Clone, serde::Serialize, Debug)]
@@ -106,18 +105,18 @@ pub struct TaskResult {
pub struct TaskError {
#[serde(skip)]
pub value: Arc,
- pub updated: DateTime,
+ pub updated: Zoned,
}
impl TaskResult {
fn since(&self) -> Since {
- Since(self.updated)
+ Since(self.updated.clone())
}
}
impl TaskError {
fn since(&self) -> Since {
- Since(self.updated)
+ Since(self.updated.clone())
}
}
@@ -140,10 +139,10 @@ impl TaskCounts {
pub struct TaskStatus {
pub last_result: TaskResult,
pub last_retry_error: Option,
- pub current_run_started: Option>,
+ pub current_run_started: Option,
/// Is the last_result out of date ?
#[serde(skip)]
- pub out_of_date: Option,
+ pub out_of_date: Option,
/// Should we expire the status of last result ?
#[serde(skip)]
pub expire_last_result: Option<(std::time::Duration, Instant)>,
@@ -237,8 +236,8 @@ struct StatusTemplate<'a> {
statuses: Vec,
env: Cow<'a, str>,
build_version: Cow<'a, str>,
- live_since: DateTime,
- now: DateTime,
+ live_since: Zoned,
+ now: Zoned,
alert: bool,
title: String,
}
@@ -256,7 +255,7 @@ impl TaskStatuses {
statuses,
env: app.environment().unwrap_or("Unknown".to_owned()).into(),
build_version: app.build_version().unwrap_or("Unknown".to_owned()).into(),
- now: Utc::now(),
+ now: Zoned::now(),
alert,
live_since: app.live_since(),
title: app.title(),
@@ -457,11 +456,11 @@ impl TaskStatus {
}
fn is_out_of_date(&self) -> OutOfDateType {
- match self.current_run_started {
+ match &self.current_run_started {
Some(started) => match self.out_of_date {
Some(out_of_date) => {
- let now = Utc::now();
- if started + Duration::seconds(300) <= now {
+ let now = Zoned::now();
+ if started.clone() + Span::new().seconds(300) <= now {
OutOfDateType::Very
} else if started + out_of_date <= now {
OutOfDateType::Slightly
@@ -509,12 +508,12 @@ impl TaskStatus {
}
}
-struct Since(DateTime);
+struct Since(Zoned);
impl Display for Since {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- let duration = Utc::now().signed_duration_since(self.0);
- let secs = duration.num_seconds();
+ let duration = Zoned::now().duration_since(&self.0);
+ let secs = duration.as_secs();
match secs.cmp(&0) {
std::cmp::Ordering::Less => write!(f, "{}", self.0),
@@ -717,11 +716,11 @@ impl AppBuilder {
let config = task_config_for(&self.app.watcher_config(), &label);
let out_of_date = config
.out_of_date
- .map(|item| chrono::Duration::seconds(item.into()));
+ .map(|seconds| Span::new().seconds(seconds));
let task_status = Arc::new(RwLock::new(TaskStatus {
last_result: TaskResult {
value: TaskResultValue::NotYetRun.into(),
- updated: Utc::now(),
+ updated: Zoned::now(),
},
last_retry_error: None,
current_run_started: None,
@@ -750,7 +749,7 @@ impl AppBuilder {
*guard = TaskStatus {
last_result: old.last_result.clone(),
last_retry_error: old.last_retry_error.clone(),
- current_run_started: Some(Utc::now()),
+ current_run_started: Some(Zoned::now()),
out_of_date,
counts: old.counts,
expire_last_result: old.expire_last_result,
@@ -801,9 +800,9 @@ impl AppBuilder {
}
}
let last_run_seconds = {
- if let Some(old_run_started) = old.current_run_started {
- let duration = Utc::now() - old_run_started;
- Some(duration.num_seconds())
+ if let Some(old_run_started) = old.current_run_started.clone() {
+ let duration = Zoned::now() - old_run_started;
+ Some(duration.get_seconds())
} else {
None
}
@@ -817,7 +816,7 @@ impl AppBuilder {
} else {
TaskResultValue::Ok(message).into()
},
- updated: Utc::now(),
+ updated: Zoned::now(),
},
last_retry_error: None,
current_run_started: None,
@@ -875,9 +874,9 @@ impl AppBuilder {
let mut guard = task_status.write().await;
let old = &*guard;
let last_run_seconds = {
- if let Some(old_run_started) = old.current_run_started {
- let duration = Utc::now() - old_run_started;
- Some(duration.num_seconds())
+ if let Some(old_run_started) = old.current_run_started.clone() {
+ let duration = Zoned::now() - old_run_started;
+ Some(duration.get_seconds())
} else {
None
}
@@ -905,7 +904,7 @@ impl AppBuilder {
*guard = TaskStatus {
last_result: TaskResult {
value: TaskResultValue::Err(new_error_message).into(),
- updated: Utc::now(),
+ updated: Zoned::now(),
},
last_retry_error: None,
current_run_started: None,
@@ -922,9 +921,9 @@ impl AppBuilder {
let mut guard = task_status.write().await;
let old = &*guard;
let last_run_seconds = {
- if let Some(old_run_started) = old.current_run_started {
- let duration = Utc::now() - old_run_started;
- Some(duration.num_seconds())
+ if let Some(old_run_started) = old.current_run_started.clone() {
+ let duration = Zoned::now() - old_run_started;
+ Some(duration.get_seconds())
} else {
None
}
@@ -933,7 +932,7 @@ impl AppBuilder {
last_result: old.last_result.clone(),
last_retry_error: Some(TaskError {
value: format!("{err:?}").into(),
- updated: Utc::now(),
+ updated: Zoned::now(),
}),
current_run_started: None,
out_of_date,
From 603afb2642fce84d2583d2f76745b6ba8674705c Mon Sep 17 00:00:00 2001
From: Sibi Prabakaran
Date: Sat, 7 Feb 2026 16:00:22 +0530
Subject: [PATCH 2/9] refactor: use Infallible for long-running tasks
The task management system now uses `std::convert::Infallible` to
explicitly signal that background tasks and the REST API server are
intended to run indefinitely. This removes the need for the internal
`StoppedTask` enum and simplifies error handling when tasks
unexpectedly terminate.
---
Cargo.lock | 16 +++++++--------
Cargo.toml | 2 +-
src/lib.rs | 52 ++++++++++++++++---------------------------------
src/rest_api.rs | 9 +++++----
4 files changed, 31 insertions(+), 48 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index b218794..684e0eb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -424,18 +424,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.97"
+version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.40"
+version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
@@ -569,9 +569,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.105"
+version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619"
+checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
@@ -689,9 +689,9 @@ dependencies = [
[[package]]
name = "unicode-ident"
-version = "1.0.18"
+version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "wasi"
diff --git a/Cargo.toml b/Cargo.toml
index 93a1f25..bbb09ef 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@ anyhow = "1.0.101"
askama = "0.14.0"
axum = { version = "0.8.8", features = ["json"] }
rand = "0.9.2"
-serde = { version = "1.0.219", features = ["derive", "rc"] }
+serde = { version = "1.0.228", features = ["derive", "rc"] }
jiff = { version = "0.2.19", features = ["serde"]}
tokio = { version = "1.49.0", features = ["rt-multi-thread"]}
tower = "0.5.3"
diff --git a/src/lib.rs b/src/lib.rs
index 01f80b5..5c757c4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -33,8 +33,8 @@ use axum::{
use config::{Delay, WatcherConfig};
use jiff::{Span, Zoned};
use rand::Rng;
-use std::fmt::Write;
use std::{borrow::Cow, collections::HashMap, fmt::Display, pin::Pin, sync::Arc, time::Instant};
+use std::{convert::Infallible, fmt::Write};
use tokio::{net::TcpListener, sync::RwLock, task::JoinSet};
pub trait WatcherAppContext {
@@ -69,7 +69,8 @@ impl Display for TaskLabel {
}
struct ToSpawn {
- future: Pin> + Send>>,
+ /// This future is expected to be an infinite loop and never return Ok.
+ future: Pin> + Send>>,
label: TaskLabel,
}
@@ -152,16 +153,10 @@ pub struct TaskStatus {
pub(crate) type StatusMap = HashMap>>;
-enum StoppedTask {
- Background,
- Periodic(TaskLabel),
- RestApi,
-}
-
#[derive(Default)]
pub(crate) struct Watcher {
to_spawn: Vec,
- set: JoinSet>,
+ set: JoinSet>,
statuses: StatusMap,
}
@@ -555,23 +550,18 @@ impl Watcher {
app: Arc,
listener: TcpListener,
) -> Result<()> {
- self.set.spawn(async {
- rest_api::start_rest_api(
- app,
- TaskStatuses {
- statuses: Arc::new(self.statuses),
- },
- listener,
- )
- .await?;
- Ok(StoppedTask::RestApi)
- });
+ self.set.spawn(rest_api::start_rest_api(
+ app,
+ TaskStatuses {
+ statuses: Arc::new(self.statuses),
+ },
+ listener,
+ ));
for ToSpawn { future, label } in self.to_spawn {
self.set.spawn(async move {
future
.await
- .with_context(|| format!("Failure while running: {label}"))?;
- Ok(StoppedTask::Periodic(label))
+ .with_context(|| format!("Task failed: {}", label))
});
}
if let Some(res) = self.set.join_next().await {
@@ -580,14 +570,9 @@ impl Watcher {
self.set.abort_all();
return Err(e);
}
- Ok(task) => {
- self.set.abort_all();
- let task_label = match task {
- StoppedTask::Background => "background task".into(),
- StoppedTask::Periodic(task_label) => format!("task tracking {task_label}"),
- StoppedTask::RestApi => "REST API".into(),
- };
- anyhow::bail!("Unexpected finish of {task_label}")
+ Ok(_task) => {
+ // This branch is impossible, as Infallible can't be created.
+ anyhow::bail!("Impossible: Infallible witnessed!")
}
}
}
@@ -691,12 +676,9 @@ impl AppBuilder {
/// Watch a background job that runs continuously, launched immediately
pub fn watch_background(&mut self, task: Fut)
where
- Fut: std::future::Future
@@ -186,7 +186,7 @@
Task Currently Running
-
Started at {{ started }}
+
Started at {{ started.strftime("%Y-%m-%d %H:%M:%S") }}
@@ -214,7 +214,7 @@ Task Currently Running
Retry in progress
-
Last error occurred {{ retrying.since() }}
+
Last error occurred {{ retrying.updated.strftime("%Y-%m-%d %H:%M:%S") }}
From 9f198faff4690f5e86227dd503cfe73bc65809b2 Mon Sep 17 00:00:00 2001
From: Sibi Prabakaran
Date: Sat, 7 Feb 2026 16:44:40 +0530
Subject: [PATCH 7/9] Update
---
templates/status.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/templates/status.html b/templates/status.html
index cb7a4db..2a1bc3b 100644
--- a/templates/status.html
+++ b/templates/status.html
@@ -92,7 +92,7 @@
Version
- {{build_version}}
+ {{build_version}}
Live since
From c76d993798b8f9b9e60507aac360f986e28c71e9 Mon Sep 17 00:00:00 2001
From: Sibi Prabakaran
Date: Sat, 7 Feb 2026 16:46:15 +0530
Subject: [PATCH 8/9] Ability to copy via clipboard
---
templates/status.html | 29 ++++++++++++++++++++++++++---
1 file changed, 26 insertions(+), 3 deletions(-)
diff --git a/templates/status.html b/templates/status.html
index 2a1bc3b..51b2e8c 100644
--- a/templates/status.html
+++ b/templates/status.html
@@ -195,10 +195,10 @@ Task Currently Running
-
-
Output
+
+
-
{{ status.status.last_result.value.as_ref().as_str() }}
+
{{ status.status.last_result.value.as_ref().as_str() }}
@@ -236,5 +236,28 @@
Retry in progress
+
From 67d809a14f11b53817f1fad1c02b5fe864ef4a7e Mon Sep 17 00:00:00 2001
From: Sibi Prabakaran
Date: Mon, 9 Feb 2026 08:22:02 +0530
Subject: [PATCH 9/9] Do clippy fix
---
examples/leaderboard.rs | 6 +--
src/lib.rs | 93 ++++++++++++++++++++---------------------
templates/status.html | 4 +-
3 files changed, 50 insertions(+), 53 deletions(-)
diff --git a/examples/leaderboard.rs b/examples/leaderboard.rs
index 3bf40db..1019c92 100644
--- a/examples/leaderboard.rs
+++ b/examples/leaderboard.rs
@@ -16,7 +16,7 @@ struct LeaderBoard;
struct TaskTwo;
#[derive(Clone)]
-struct DummyApp;
+struct DummyApp(Zoned);
impl DummyApp {
async fn start() -> Result<()> {
@@ -24,7 +24,7 @@ impl DummyApp {
println!("Starting leaderboard watcher example.");
println!("The watcher will run, but the status page is not served in this example.");
- let app = Arc::new(DummyApp);
+ let app = Arc::new(DummyApp(Zoned::now()));
let mut builder = AppBuilder::new(app.clone());
builder.watch_periodic(TaskLabel::new("leaderboard"), LeaderBoard)?;
@@ -40,7 +40,7 @@ impl WatcherAppContext for DummyApp {
}
fn live_since(&self) -> Zoned {
- Zoned::now()
+ self.0.clone()
}
fn watcher_config(&self) -> WatcherConfig {
diff --git a/src/lib.rs b/src/lib.rs
index f18f7e7..2a22c48 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -109,18 +109,6 @@ pub struct TaskError {
pub updated: Zoned,
}
-impl TaskResult {
- fn since(&self) -> Since {
- Since(self.updated.clone())
- }
-}
-
-impl TaskError {
- fn since(&self) -> Since {
- Since(self.updated.clone())
- }
-}
-
#[derive(Clone, Copy, Default, serde::Serialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct TaskCounts {
@@ -237,6 +225,51 @@ struct StatusTemplate<'a> {
title: String,
}
+impl<'a> StatusTemplate<'a> {
+ fn live_since_human(&self) -> String {
+ let duration = self.now.duration_since(&self.live_since);
+ let secs = duration.as_secs();
+
+ if secs < 0 {
+ return "In the future".to_string();
+ }
+ if secs == 0 {
+ return "just now".to_string();
+ }
+
+ let minutes = secs / 60;
+ let secs_rem = secs % 60;
+ let hours = minutes / 60;
+ let minutes_rem = minutes % 60;
+ let days = hours / 24;
+ let hours_rem = hours % 24;
+
+ let mut result = String::new();
+ let mut need_space = false;
+
+ for (number, letter) in [
+ (days, "d"),
+ (hours_rem, "h"),
+ (minutes_rem, "m"),
+ (secs_rem, "s"),
+ ] {
+ if number > 0 {
+ if need_space {
+ result.push(' ');
+ }
+ result.push_str(&format!("{}{}", number, letter));
+ need_space = true;
+ }
+ }
+
+ if result.is_empty() {
+ "0s".to_string()
+ } else {
+ result
+ }
+ }
+}
+
impl TaskStatuses {
async fn to_template<'a, C: WatcherAppContext>(
&'a self,
@@ -504,42 +537,6 @@ impl TaskStatus {
}
}
-struct Since(Zoned);
-
-impl Display for Since {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- let duration = Zoned::now().duration_since(&self.0);
- let secs = duration.as_secs();
-
- match secs.cmp(&0) {
- std::cmp::Ordering::Less => write!(f, "{}", self.0),
- std::cmp::Ordering::Equal => write!(f, "just now ({})", self.0),
- std::cmp::Ordering::Greater => {
- let minutes = secs / 60;
- let secs = secs % 60;
- let hours = minutes / 60;
- let minutes = minutes % 60;
- let days = hours / 24;
- let hours = hours % 24;
-
- let mut need_space = false;
- for (number, letter) in [(days, 'd'), (hours, 'h'), (minutes, 'm'), (secs, 's')] {
- if number > 0 {
- if need_space {
- write!(f, " {number}{letter}")?;
- } else {
- need_space = true;
- write!(f, "{number}{letter}")?;
- }
- }
- }
-
- write!(f, " ({})", self.0)
- }
- }
- }
-}
-
pub struct AppBuilder {
pub app: Arc,
pub(crate) watcher: Watcher,
diff --git a/templates/status.html b/templates/status.html
index 51b2e8c..84b61f5 100644
--- a/templates/status.html
+++ b/templates/status.html
@@ -96,7 +96,7 @@
Live since
- {{live_since.strftime("%Y-%m-%d %H:%M:%S")}}
+ {{live_since_human()}}
@@ -231,7 +231,7 @@ Retry in progress