perspective_client/utils/
mod.rs1mod clone;
16mod logging;
17
18#[cfg(test)]
19mod tests;
20
21use std::sync::Arc;
22
23use rand::rngs::StdRng;
24use rand::{Rng, SeedableRng};
25use rand_unique::{RandomSequence, RandomSequenceBuilder};
26use thiserror::*;
27
28use crate::proto;
29
30#[derive(Clone, Error, Debug)]
31pub enum ClientError {
32 #[error("View not found")]
33 ViewNotFound,
34
35 #[error("Abort(): {0}")]
36 Internal(String),
37
38 #[error("Transport error: {0}")]
39 TransportError(String),
40
41 #[error("Client not yet initialized")]
42 NotInitialized,
43
44 #[error("Unknown error: {0}")]
45 Unknown(String),
46
47 #[error("Unwrapped option")]
48 Option,
49
50 #[error("Bad string")]
51 Utf8(#[from] std::str::Utf8Error),
52
53 #[error("Undecipherable server message {0:?}")]
54 DecodeError(#[from] prost::DecodeError),
55
56 #[error("Response aborted")]
57 ResponseAborted,
58
59 #[error("Unexpected response {0:?}")]
60 ResponseFailed(Box<proto::response::ClientResp>),
61
62 #[error("Not yet implemented {0:?}")]
63 NotImplemented(&'static str),
64
65 #[error("Can't use both `limit` and `index` arguments")]
66 BadTableOptions,
67
68 #[error("External error: {0}")]
69 ExternalError(Arc<Box<dyn std::error::Error + Send + Sync>>),
70
71 #[error("Undecipherable proto message")]
72 ProtoError(#[from] prost::EncodeError),
73
74 #[error("Duplicate name {0}")]
75 DuplicateNameError(String),
76}
77
78pub type ClientResult<T> = Result<T, ClientError>;
79
80impl From<Box<dyn std::error::Error + Send + Sync>> for ClientError {
81 fn from(value: Box<dyn std::error::Error + Send + Sync>) -> Self {
82 ClientError::ExternalError(Arc::new(value))
83 }
84}
85
86impl<'a, A> From<std::sync::PoisonError<std::sync::MutexGuard<'a, A>>> for ClientError {
87 fn from(_: std::sync::PoisonError<std::sync::MutexGuard<'a, A>>) -> Self {
88 ClientError::Internal("Lock Error".to_owned())
89 }
90}
91
92impl From<Option<proto::response::ClientResp>> for ClientError {
93 fn from(value: Option<proto::response::ClientResp>) -> Self {
94 match value {
95 Some(proto::response::ClientResp::ServerError(x)) => match x.status_code() {
96 proto::StatusCode::ServerError => ClientError::Internal(x.message),
97 proto::StatusCode::ViewNotFound => ClientError::ViewNotFound,
98 proto::StatusCode::TransportError => ClientError::TransportError(x.message),
99 },
100 Some(x) => ClientError::ResponseFailed(Box::new(x)),
101 None => ClientError::ResponseAborted,
102 }
103 }
104}
105
106impl From<proto::response::ClientResp> for ClientError {
107 fn from(value: proto::response::ClientResp) -> Self {
108 match value {
109 proto::response::ClientResp::ServerError(x) => match x.status_code() {
110 proto::StatusCode::ServerError => ClientError::Internal(x.message),
111 proto::StatusCode::ViewNotFound => ClientError::ViewNotFound,
112 proto::StatusCode::TransportError => ClientError::TransportError(x.message),
113 },
114 x => ClientError::ResponseFailed(Box::new(x)),
115 }
116 }
117}
118
119pub trait PerspectiveResultExt {
120 fn unwrap_or_log(&self);
121}
122
123impl<T, E> PerspectiveResultExt for Result<T, E>
124where
125 E: std::error::Error,
126{
127 fn unwrap_or_log(&self) {
128 if let Err(e) = self {
129 tracing::warn!("{}", e);
130 }
131 }
132}
133
134#[derive(Clone)]
136pub struct IDGen(Arc<std::sync::Mutex<RandomSequence<u32>>>);
137
138impl Default for IDGen {
139 fn default() -> Self {
140 Self(Arc::new(std::sync::Mutex::new(Self::new_seq())))
141 }
142}
143
144impl IDGen {
145 fn new_seq() -> RandomSequence<u32> {
146 let mut rng = rand::rngs::ThreadRng::default();
147 let config = RandomSequenceBuilder::<u32>::rand(&mut rng);
148 config.into_iter()
149 }
150
151 pub fn next(&self) -> u32 {
152 let mut idgen = self.0.lock().unwrap();
153 if let Some(x) = idgen.next() {
154 x
155 } else {
156 *idgen = Self::new_seq();
157 idgen.next().unwrap()
158 }
159 }
160}
161
162const SIZE: usize = 21;
163
164const CHARACTERS: [char; 64] = [
165 '_', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
166 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
167 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
168 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
169];
170
171pub fn randid() -> String {
173 let mask = CHARACTERS.len().next_power_of_two() - 1;
174 let step: usize = 8 * SIZE / 5;
175 let mut id = String::with_capacity(SIZE);
176 loop {
177 let mut random = StdRng::from_os_rng();
178 let mut bytes: Vec<u8> = vec![0; step];
179 random.fill(&mut bytes[..]);
180 for &byte in &bytes {
181 let byte = byte as usize & mask;
182 if CHARACTERS.len() > byte {
183 id.push(CHARACTERS[byte]);
184 if id.len() == SIZE {
185 return id;
186 }
187 }
188 }
189 }
190}