Refactor job client into separate crate

- Moved client code from rust/src/client.rs to rust/client/ subdirectory
- Created separate hero-job-client crate for better modularity
- Updated Cargo.toml to include client as workspace member
- Client can now be used independently of the main hero-job crate
This commit is contained in:
Timur Gordon
2025-11-04 17:09:37 +01:00
parent e3d8147eaa
commit 7b9420f3e6
6 changed files with 1262 additions and 685 deletions

613
rust/Cargo.lock generated
View File

@@ -11,17 +11,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "async-trait"
version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.5.0" version = "1.5.0"
@@ -43,12 +32,6 @@ version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.44" version = "1.2.44"
@@ -79,20 +62,6 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "combine"
version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
"bytes",
"futures-core",
"memchr",
"pin-project-lite",
"tokio",
"tokio-util",
]
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.7" version = "0.8.7"
@@ -128,63 +97,12 @@ dependencies = [
"crypto-common", "crypto-common",
] ]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.4" version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-sink",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.9" version = "0.14.9"
@@ -214,14 +132,14 @@ dependencies = [
"chrono", "chrono",
"hex", "hex",
"log", "log",
"redis",
"secp256k1", "secp256k1",
"serde", "serde",
"serde-wasm-bindgen",
"serde_json", "serde_json",
"sha2", "sha2",
"thiserror", "thiserror",
"tokio",
"uuid", "uuid",
"wasm-bindgen",
] ]
[[package]] [[package]]
@@ -254,108 +172,6 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "icu_collections"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
dependencies = [
"displaydoc",
"potential_utf",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
dependencies = [
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
[[package]]
name = "icu_properties"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
dependencies = [
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
[[package]]
name = "icu_provider"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
dependencies = [
"displaydoc",
"icu_locale_core",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.15" version = "1.0.15"
@@ -378,12 +194,6 @@ version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "litemap"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.28" version = "0.4.28"
@@ -396,17 +206,6 @@ version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "mio"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
dependencies = [
"libc",
"wasi",
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -422,33 +221,6 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "potential_utf"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
dependencies = [
"zerovec",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.103" version = "1.0.103"
@@ -473,27 +245,6 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "redis"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec"
dependencies = [
"async-trait",
"bytes",
"combine",
"futures-util",
"itoa",
"percent-encoding",
"pin-project-lite",
"ryu",
"sha1_smol",
"socket2 0.5.10",
"tokio",
"tokio-util",
"url",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.22" version = "1.0.22"
@@ -534,6 +285,17 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-wasm-bindgen"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]] [[package]]
name = "serde_core" name = "serde_core"
version = "1.0.228" version = "1.0.228"
@@ -567,12 +329,6 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "sha1_smol"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.10.9" version = "0.10.9"
@@ -590,38 +346,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "socket2"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
dependencies = [
"libc",
"windows-sys 0.60.2",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.108" version = "2.0.108"
@@ -633,17 +357,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
@@ -664,43 +377,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tinystr"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "tokio"
version = "1.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
dependencies = [
"bytes",
"libc",
"mio",
"pin-project-lite",
"socket2 0.6.1",
"windows-sys 0.61.2",
]
[[package]]
name = "tokio-util"
version = "0.7.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.19.0" version = "1.19.0"
@@ -713,24 +389,6 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "url"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.18.1" version = "1.18.1"
@@ -748,12 +406,6 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "wasip2" name = "wasip2"
version = "1.0.1+wasi-0.2.4" version = "1.0.1+wasi-0.2.4"
@@ -867,247 +519,8 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.46.0" version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "writeable"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]]
name = "yoke"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
dependencies = [
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerotrie"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@@ -10,13 +10,15 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
uuid = { version = "1.0", features = ["v4"] } uuid = { version = "1.0", features = ["v4"] }
thiserror = "1.0" thiserror = "1.0"
redis = { version = "0.25", features = ["aio", "tokio-comp"] }
tokio = { version = "1.0", features = ["rt", "time"] }
log = "0.4" log = "0.4"
secp256k1 = { version = "0.28", features = ["recovery"] } secp256k1 = { version = "0.28", features = ["recovery"] }
sha2 = "0.10" sha2 = "0.10"
hex = "0.4" hex = "0.4"
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
serde-wasm-bindgen = "0.6"
[lib] [lib]
name = "hero_job" name = "hero_job"
path = "src/lib.rs" path = "src/lib.rs"

1137
rust/client/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

18
rust/client/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "hero-job-client"
version = "0.1.0"
edition = "2021"
description = "Redis client for Hero job management"
[dependencies]
hero-job = { path = ".." }
redis = { version = "0.25", features = ["aio", "tokio-comp"] }
tokio = { version = "1.0", features = ["rt", "time"] }
chrono = { version = "0.4", features = ["serde"] }
serde_json = "1.0"
thiserror = "1.0"
log = "0.4"
[lib]
name = "hero_job_client"
path = "src/lib.rs"

View File

@@ -2,7 +2,21 @@
use chrono::Utc; use chrono::Utc;
use redis::AsyncCommands; use redis::AsyncCommands;
use crate::{Job, JobStatus, JobError}; use hero_job::{Job, JobStatus, JobError};
use thiserror::Error;
/// Client-specific error types
#[derive(Error, Debug)]
pub enum ClientError {
#[error("Redis error: {0}")]
Redis(#[from] redis::RedisError),
#[error("Job error: {0}")]
Job(#[from] JobError),
#[error("Invalid status: {0}")]
InvalidStatus(String),
#[error("Timeout waiting for job completion")]
Timeout,
}
/// Client for managing jobs in Redis /// Client for managing jobs in Redis
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -40,10 +54,10 @@ impl ClientBuilder {
} }
/// Build the client /// Build the client
pub async fn build(self) -> Result<Client, JobError> { pub async fn build(self) -> Result<Client, ClientError> {
// Create Redis client // Create Redis client
let redis_client = redis::Client::open(self.redis_url.as_str()) let redis_client = redis::Client::open(self.redis_url.as_str())
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(Client { Ok(Client {
redis_client, redis_client,
@@ -70,14 +84,14 @@ impl Client {
} }
/// List all job IDs from Redis /// List all job IDs from Redis
pub async fn list_jobs(&self) -> Result<Vec<String>, JobError> { pub async fn list_jobs(&self) -> Result<Vec<String>, ClientError> {
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let keys: Vec<String> = conn.keys(format!("{}:*", &self.jobs_key())).await let keys: Vec<String> = conn.keys(format!("{}:*", &self.jobs_key())).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let job_ids: Vec<String> = keys let job_ids: Vec<String> = keys
.into_iter() .into_iter()
.filter_map(|key| { .filter_map(|key| {
@@ -129,21 +143,21 @@ impl Client {
pub async fn set_error(&self, pub async fn set_error(&self,
job_id: &str, job_id: &str,
error: &str, error: &str,
) -> Result<(), JobError> { ) -> Result<(), ClientError> {
let job_key = self.job_key(job_id); let job_key = self.job_key(job_id);
let now = Utc::now(); let now = Utc::now();
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let _: () = conn.hset_multiple(&job_key, &[ let _: () = conn.hset_multiple(&job_key, &[
("error", error), ("error", error),
("status", JobStatus::Error.as_str()), ("status", JobStatus::Error.as_str()),
("updated_at", &now.to_rfc3339()), ("updated_at", &now.to_rfc3339()),
]).await ]).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(()) Ok(())
} }
@@ -152,20 +166,20 @@ impl Client {
pub async fn set_job_status(&self, pub async fn set_job_status(&self,
job_id: &str, job_id: &str,
status: JobStatus, status: JobStatus,
) -> Result<(), JobError> { ) -> Result<(), ClientError> {
let job_key = self.job_key(job_id); let job_key = self.job_key(job_id);
let now = Utc::now(); let now = Utc::now();
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let _: () = conn.hset_multiple(&job_key, &[ let _: () = conn.hset_multiple(&job_key, &[
("status", status.as_str()), ("status", status.as_str()),
("updated_at", &now.to_rfc3339()), ("updated_at", &now.to_rfc3339()),
]).await ]).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(()) Ok(())
} }
@@ -173,18 +187,18 @@ impl Client {
pub async fn get_status( pub async fn get_status(
&self, &self,
job_id: &str, job_id: &str,
) -> Result<JobStatus, JobError> { ) -> Result<JobStatus, ClientError> {
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let status_str: Option<String> = conn.hget(&self.job_key(job_id), "status").await let status_str: Option<String> = conn.hget(&self.job_key(job_id), "status").await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
match status_str { match status_str {
Some(s) => JobStatus::from_str(&s).ok_or_else(|| JobError::InvalidStatus(s)), Some(s) => JobStatus::from_str(&s).ok_or_else(|| ClientError::InvalidStatus(s)),
None => Err(JobError::NotFound(job_id.to_string())), None => Err(ClientError::Job(JobError::NotFound(job_id.to_string()))),
} }
} }
@@ -192,24 +206,24 @@ impl Client {
pub async fn delete_from_redis( pub async fn delete_from_redis(
&self, &self,
job_id: &str, job_id: &str,
) -> Result<(), JobError> { ) -> Result<(), ClientError> {
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let job_key = self.job_key(job_id); let job_key = self.job_key(job_id);
let _: () = conn.del(&job_key).await let _: () = conn.del(&job_key).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(()) Ok(())
} }
/// Store this job in Redis with the specified status /// Store this job in Redis with the specified status
pub async fn store_job_in_redis_with_status(&self, job: &Job, status: JobStatus) -> Result<(), JobError> { pub async fn store_job_in_redis_with_status(&self, job: &Job, status: JobStatus) -> Result<(), ClientError> {
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let job_key = self.job_key(&job.id); let job_key = self.job_key(&job.id);
@@ -224,17 +238,17 @@ impl Client {
("created_at", job.created_at.to_rfc3339()), ("created_at", job.created_at.to_rfc3339()),
("updated_at", job.updated_at.to_rfc3339()), ("updated_at", job.updated_at.to_rfc3339()),
]).await ]).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
// Set TTL for the job (24 hours) // Set TTL for the job (24 hours)
let _: () = conn.expire(&job_key, 86400).await let _: () = conn.expire(&job_key, 86400).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(()) Ok(())
} }
/// Store this job in Redis (defaults to Dispatched status for backwards compatibility) /// Store this job in Redis (defaults to Dispatched status for backwards compatibility)
pub async fn store_job_in_redis(&self, job: &Job) -> Result<(), JobError> { pub async fn store_job_in_redis(&self, job: &Job) -> Result<(), ClientError> {
self.store_job_in_redis_with_status(job, JobStatus::Dispatched).await self.store_job_in_redis_with_status(job, JobStatus::Dispatched).await
} }
@@ -242,17 +256,17 @@ impl Client {
pub async fn load_job_from_redis( pub async fn load_job_from_redis(
&self, &self,
job_id: &str, job_id: &str,
) -> Result<Job, JobError> { ) -> Result<Job, ClientError> {
let job_key = self.job_key(job_id); let job_key = self.job_key(job_id);
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
// Get job data from Redis // Get job data from Redis
let job_data: Option<String> = conn.hget(&job_key, "data").await let job_data: Option<String> = conn.hget(&job_key, "data").await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
match job_data { match job_data {
Some(data) => { Some(data) => {
@@ -260,21 +274,21 @@ impl Client {
.map_err(|e| JobError::Serialization(e))?; .map_err(|e| JobError::Serialization(e))?;
Ok(job) Ok(job)
} }
None => Err(JobError::NotFound(job_id.to_string())), None => Err(ClientError::Job(JobError::NotFound(job_id.to_string()))),
} }
} }
/// Delete a job by ID /// Delete a job by ID
pub async fn delete_job(&mut self, job_id: &str) -> Result<(), JobError> { pub async fn delete_job(&mut self, job_id: &str) -> Result<(), ClientError> {
let mut conn = self.redis_client.get_multiplexed_async_connection().await let mut conn = self.redis_client.get_multiplexed_async_connection().await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let job_key = self.job_key(job_id); let job_key = self.job_key(job_id);
let deleted_count: i32 = conn.del(&job_key).await let deleted_count: i32 = conn.del(&job_key).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
if deleted_count == 0 { if deleted_count == 0 {
return Err(JobError::NotFound(job_id.to_string())); return Err(ClientError::Job(JobError::NotFound(job_id.to_string())));
} }
Ok(()) Ok(())
@@ -285,19 +299,19 @@ impl Client {
&self, &self,
job_id: &str, job_id: &str,
result: &str, result: &str,
) -> Result<(), JobError> { ) -> Result<(), ClientError> {
let job_key = self.job_key(&job_id); let job_key = self.job_key(&job_id);
let now = Utc::now(); let now = Utc::now();
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let _: () = conn.hset_multiple(&job_key, &[ let _: () = conn.hset_multiple(&job_key, &[
("result", result), ("result", result),
("status", JobStatus::Finished.as_str()), ("status", JobStatus::Finished.as_str()),
("updated_at", &now.to_rfc3339()), ("updated_at", &now.to_rfc3339()),
]).await ]).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(()) Ok(())
} }
@@ -306,14 +320,14 @@ impl Client {
pub async fn get_result( pub async fn get_result(
&self, &self,
job_id: &str, job_id: &str,
) -> Result<Option<String>, JobError> { ) -> Result<Option<String>, ClientError> {
let job_key = self.job_key(job_id); let job_key = self.job_key(job_id);
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let result: Option<String> = conn.hget(&job_key, "result").await let result: Option<String> = conn.hget(&job_key, "result").await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(result) Ok(result)
} }
@@ -321,48 +335,48 @@ impl Client {
pub async fn get_error( pub async fn get_error(
&self, &self,
job_id: &str, job_id: &str,
) -> Result<Option<String>, JobError> { ) -> Result<Option<String>, ClientError> {
let job_key = self.job_key(job_id); let job_key = self.job_key(job_id);
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let result: Option<String> = conn.hget(&job_key, "error").await let result: Option<String> = conn.hget(&job_key, "error").await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(result) Ok(result)
} }
/// Get a job ID from the work queue (blocking pop) /// Get a job ID from the work queue (blocking pop)
pub async fn get_job_id(&self, queue_key: &str) -> Result<Option<String>, JobError> { pub async fn get_job_id(&self, queue_key: &str) -> Result<Option<String>, ClientError> {
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
// Use BRPOP with a short timeout to avoid blocking indefinitely // Use BRPOP with a short timeout to avoid blocking indefinitely
let result: Option<(String, String)> = conn.brpop(queue_key, 1.0).await let result: Option<(String, String)> = conn.brpop(queue_key, 1.0).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(result.map(|(_, job_id)| job_id)) Ok(result.map(|(_, job_id)| job_id))
} }
/// Get a job by ID (alias for load_job_from_redis) /// Get a job by ID (alias for load_job_from_redis)
pub async fn get_job(&self, job_id: &str) -> Result<Job, JobError> { pub async fn get_job(&self, job_id: &str) -> Result<Job, ClientError> {
self.load_job_from_redis(job_id).await self.load_job_from_redis(job_id).await
} }
/// Dispatch a job to a runner's queue /// Dispatch a job to a runner's queue
pub async fn dispatch_job(&self, job_id: &str, runner_name: &str) -> Result<(), JobError> { pub async fn dispatch_job(&self, job_id: &str, runner_name: &str) -> Result<(), ClientError> {
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let queue_key = self.runner_key(runner_name); let queue_key = self.runner_key(runner_name);
// Push job ID to the runner's queue (LPUSH for FIFO with BRPOP) // Push job ID to the runner's queue (LPUSH for FIFO with BRPOP)
let _: () = conn.lpush(&queue_key, job_id).await let _: () = conn.lpush(&queue_key, job_id).await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
Ok(()) Ok(())
} }
@@ -388,7 +402,7 @@ impl Client {
job: &Job, job: &Job,
runner_name: &str, runner_name: &str,
timeout_secs: u64, timeout_secs: u64,
) -> Result<String, JobError> { ) -> Result<String, ClientError> {
use tokio::time::{Duration, timeout}; use tokio::time::{Duration, timeout};
// Store the job in Redis // Store the job in Redis
@@ -406,10 +420,7 @@ impl Client {
match result { match result {
Ok(Ok(job_result)) => Ok(job_result), Ok(Ok(job_result)) => Ok(job_result),
Ok(Err(e)) => Err(e), Ok(Err(e)) => Err(e),
Err(_) => Err(JobError::Timeout(format!( Err(_) => Err(ClientError::Timeout),
"Job {} did not complete within {} seconds",
job.id, timeout_secs
))),
} }
} }
@@ -417,7 +428,7 @@ impl Client {
/// ///
/// This polls the job status every 500ms until it reaches a terminal state /// This polls the job status every 500ms until it reaches a terminal state
/// (Finished or Error), then returns the result or error. /// (Finished or Error), then returns the result or error.
async fn wait_for_job_completion(&self, job_id: &str) -> Result<String, JobError> { async fn wait_for_job_completion(&self, job_id: &str) -> Result<String, ClientError> {
use tokio::time::{sleep, Duration}; use tokio::time::{sleep, Duration};
loop { loop {
@@ -429,7 +440,7 @@ impl Client {
// Job completed successfully, get the result // Job completed successfully, get the result
let result = self.get_result(job_id).await?; let result = self.get_result(job_id).await?;
return result.ok_or_else(|| { return result.ok_or_else(|| {
JobError::InvalidData(format!("Job {} finished but has no result", job_id)) ClientError::Job(JobError::InvalidData(format!("Job {} finished but has no result", job_id)))
}); });
} }
JobStatus::Error => { JobStatus::Error => {
@@ -437,19 +448,19 @@ impl Client {
let mut conn = self.redis_client let mut conn = self.redis_client
.get_multiplexed_async_connection() .get_multiplexed_async_connection()
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
let error_msg: Option<String> = conn let error_msg: Option<String> = conn
.hget(&self.job_key(job_id), "error") .hget(&self.job_key(job_id), "error")
.await .await
.map_err(|e| JobError::Redis(e))?; .map_err(|e| ClientError::Redis(e))?;
return Err(JobError::InvalidData( return Err(ClientError::Job(JobError::InvalidData(
error_msg.unwrap_or_else(|| format!("Job {} failed with unknown error", job_id)) error_msg.unwrap_or_else(|| format!("Job {} failed with unknown error", job_id))
)); )));
} }
JobStatus::Stopping => { JobStatus::Stopping => {
return Err(JobError::InvalidData(format!("Job {} was stopped", job_id))); return Err(ClientError::Job(JobError::InvalidData(format!("Job {} was stopped", job_id))));
} }
// Job is still running (Dispatched, WaitingForPrerequisites, Started) // Job is still running (Dispatched, WaitingForPrerequisites, Started)
_ => { _ => {

View File

@@ -5,8 +5,8 @@ use thiserror::Error;
use uuid::Uuid; use uuid::Uuid;
use log::{error}; use log::{error};
pub mod client; #[cfg(target_arch = "wasm32")]
pub use client::{Client, ClientBuilder}; use wasm_bindgen::prelude::*;
/// Signature for a job - contains the signatory's public key and their signature /// Signature for a job - contains the signatory's public key and their signature
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -56,10 +56,8 @@ impl JobStatus {
} }
} }
/// Representation of a script execution request. /// Job structure representing a unit of work to be executed
/// #[cfg_attr(target_arch = "wasm32", wasm_bindgen(getter_with_clone))]
/// This structure contains all the information needed to execute a script
/// on a actor service, including the script content, dependencies, and metadata.
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Job { pub struct Job {
pub id: String, pub id: String,
@@ -69,33 +67,31 @@ pub struct Job {
pub runner: String, // name of the runner to execute this job pub runner: String, // name of the runner to execute this job
pub executor: String, // name of the executor the runner will use to execute this job pub executor: String, // name of the executor the runner will use to execute this job
pub timeout: u64, // timeout in seconds pub timeout: u64, // timeout in seconds
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
pub env_vars: HashMap<String, String>, // environment variables for script execution pub env_vars: HashMap<String, String>, // environment variables for script execution
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
pub created_at: chrono::DateTime<chrono::Utc>, pub created_at: chrono::DateTime<chrono::Utc>,
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
pub updated_at: chrono::DateTime<chrono::Utc>, pub updated_at: chrono::DateTime<chrono::Utc>,
/// Signatures from authorized signatories (public keys are included in each signature) /// Signatures from authorized signatories (public keys are included in each signature)
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(skip))]
pub signatures: Vec<JobSignature>, pub signatures: Vec<JobSignature>,
} }
/// Error types for job operations /// Error types for job operations
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum JobError { pub enum JobError {
#[error("Redis error: {0}")]
Redis(#[from] redis::RedisError),
#[error("Serialization error: {0}")] #[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error), Serialization(#[from] serde_json::Error),
#[error("Job not found: {0}")] #[error("Job not found: {0}")]
NotFound(String), NotFound(String),
#[error("Invalid job status: {0}")] #[error("Invalid data: {0}")]
InvalidStatus(String),
#[error("Timeout error: {0}")]
Timeout(String),
#[error("Invalid job data: {0}")]
InvalidData(String), InvalidData(String),
#[error("Validation error: {0}")]
Validation(String),
#[error("Signature verification failed: {0}")] #[error("Signature verification failed: {0}")]
SignatureVerificationFailed(String), SignatureVerification(String),
#[error("Unauthorized: {0}")]
Unauthorized(String),
} }
impl Job { impl Job {
@@ -167,25 +163,25 @@ impl Job {
let secp = Secp256k1::verification_only(); let secp = Secp256k1::verification_only();
let message = Message::from_digest_slice(&hash) let message = Message::from_digest_slice(&hash)
.map_err(|e| JobError::SignatureVerificationFailed(format!("Invalid message: {}", e)))?; .map_err(|e| JobError::SignatureVerification(format!("Invalid message: {}", e)))?;
// Verify each signature (if any) // Verify each signature (if any)
for sig_data in &self.signatures { for sig_data in &self.signatures {
// Decode public key // Decode public key
let pubkey_bytes = hex::decode(&sig_data.public_key) let pubkey_bytes = hex::decode(&sig_data.public_key)
.map_err(|e| JobError::SignatureVerificationFailed(format!("Invalid public key hex: {}", e)))?; .map_err(|e| JobError::SignatureVerification(format!("Invalid public key hex: {}", e)))?;
let pubkey = PublicKey::from_slice(&pubkey_bytes) let pubkey = PublicKey::from_slice(&pubkey_bytes)
.map_err(|e| JobError::SignatureVerificationFailed(format!("Invalid public key: {}", e)))?; .map_err(|e| JobError::SignatureVerification(format!("Invalid public key: {}", e)))?;
// Decode signature // Decode signature
let sig_bytes = hex::decode(&sig_data.signature) let sig_bytes = hex::decode(&sig_data.signature)
.map_err(|e| JobError::SignatureVerificationFailed(format!("Invalid signature hex: {}", e)))?; .map_err(|e| JobError::SignatureVerification(format!("Invalid signature hex: {}", e)))?;
let signature = Signature::from_compact(&sig_bytes) let signature = Signature::from_compact(&sig_bytes)
.map_err(|e| JobError::SignatureVerificationFailed(format!("Invalid signature: {}", e)))?; .map_err(|e| JobError::SignatureVerification(format!("Invalid signature: {}", e)))?;
// Verify signature // Verify signature
secp.verify_ecdsa(&message, &signature, &pubkey) secp.verify_ecdsa(&message, &signature, &pubkey)
.map_err(|e| JobError::SignatureVerificationFailed(format!("Signature verification failed: {}", e)))?; .map_err(|e| JobError::SignatureVerification(format!("Signature verification failed: {}", e)))?;
} }
Ok(()) Ok(())