switchgear_service_api/
discovery.rs1use crate::service::HasServiceErrorSource;
2use async_trait::async_trait;
3use secp256k1::PublicKey;
4use serde::{Deserialize, Serialize};
5use std::collections::BTreeSet;
6use std::error::Error;
7use std::io;
8
9#[async_trait]
10pub trait DiscoveryBackendStore {
11 type Error: Error + Send + Sync + 'static + HasServiceErrorSource;
12
13 async fn get(&self, public_key: &PublicKey) -> Result<Option<DiscoveryBackend>, Self::Error>;
14
15 async fn get_all(&self, etag: Option<u64>) -> Result<DiscoveryBackends, Self::Error>;
16
17 async fn post(&self, backend: DiscoveryBackend) -> Result<Option<PublicKey>, Self::Error>;
18
19 async fn put(&self, backend: DiscoveryBackend) -> Result<bool, Self::Error>;
20
21 async fn patch(&self, backend: DiscoveryBackendPatch) -> Result<bool, Self::Error>;
22
23 async fn delete(&self, public_key: &PublicKey) -> Result<bool, Self::Error>;
24}
25
26#[async_trait]
27pub trait HttpDiscoveryBackendClient: DiscoveryBackendStore {
28 async fn health(&self) -> Result<(), Self::Error>;
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
32#[serde(rename_all = "camelCase")]
33pub struct DiscoveryBackends {
34 pub etag: u64,
35 pub backends: Option<Vec<DiscoveryBackend>>,
36}
37
38impl DiscoveryBackends {
39 pub fn etag_from_str(etag: &str) -> io::Result<u64> {
40 let etag = hex::decode(etag).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
41 let etag: [u8; 8] = etag.try_into().map_err(|etag: Vec<u8>| {
42 io::Error::new(
43 io::ErrorKind::InvalidData,
44 format!("invalid etag size: {} bytes", etag.len()),
45 )
46 })?;
47 Ok(u64::from_be_bytes(etag))
48 }
49
50 pub fn etag_string(&self) -> String {
51 hex::encode(self.etag.to_be_bytes())
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
56#[serde(rename_all = "camelCase")]
57pub struct DiscoveryBackend {
58 pub public_key: PublicKey,
59 #[serde(flatten)]
60 pub backend: DiscoveryBackendSparse,
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct DiscoveryBackendSparse {
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub name: Option<String>,
68 pub partitions: BTreeSet<String>,
69 pub weight: usize,
70 pub enabled: bool,
71 #[serde(with = "json_bytes")]
72 pub implementation: Vec<u8>,
73}
74
75mod json_bytes {
76 use serde::{Deserialize, Deserializer, Serialize, Serializer};
77 use serde_json::Value;
78
79 pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
80 where
81 S: Serializer,
82 {
83 let value: Value = serde_json::from_slice(bytes).map_err(serde::ser::Error::custom)?;
84 value.serialize(serializer)
85 }
86
87 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
88 where
89 D: Deserializer<'de>,
90 {
91 let value = Value::deserialize(deserializer)?;
92 serde_json::to_vec(&value).map_err(serde::de::Error::custom)
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub struct DiscoveryBackendPatch {
99 pub public_key: PublicKey,
100 #[serde(flatten)]
101 pub backend: DiscoveryBackendPatchSparse,
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
105#[serde(rename_all = "camelCase")]
106pub struct DiscoveryBackendPatchSparse {
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub name: Option<Option<String>>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub partitions: Option<BTreeSet<String>>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub weight: Option<usize>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub enabled: Option<bool>,
115}