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