Skip to main content

nakadi_types/
lib.rs

1//! # Nakadi-Types
2//!
3//! `nakadi-types` contains types for interacting with the [Nakadi](https://nakadi.io) Event Broker.
4//!
5//! There is no real logic implemented in this crate.
6//!
7//! Almost all types in this crate match 1 to 1 to a type of the Nakadi API. Some
8//! types where Nakadi returns collections in a wrapping object are made explicit.
9//! In this case the field of the wrapping object is renamed to `items` for
10//! serialization purposes.
11//!
12//! This crate is used by [Nakadion](https://crates.io/crates/nakadion)
13//!
14//! ## Documentation and Environment Variables
15//!
16//! Within the documentation environment variables can contain spaces and
17//! line breaks. This is because part of the documentation was created using macros.
18//! The names of the variables of cause must not contain these characters. So be
19//! careful when copy & pasting.
20use std::error::Error as StdError;
21use std::fmt;
22use std::str::FromStr;
23
24use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize};
25use url::Url;
26
27pub(crate) mod helpers;
28
29pub mod event;
30pub mod event_type;
31pub mod misc;
32pub mod partition;
33pub mod publishing;
34pub mod subscription;
35
36new_type! {
37    #[doc="The base URL to the Nakadi API."]
38    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39    pub copy struct NakadiBaseUrl(Url, env="NAKADI_BASE_URL");
40}
41
42impl NakadiBaseUrl {
43    pub fn as_url(&self) -> &Url {
44        &self.0
45    }
46
47    pub fn as_str(&self) -> &str {
48        &self.0.as_ref()
49    }
50}
51
52impl AsRef<str> for NakadiBaseUrl {
53    fn as_ref(&self) -> &str {
54        self.as_str()
55    }
56}
57
58/// The flow id of the request, which is written into the logs and passed to called services. Helpful
59/// for operational troubleshooting and log analysis.
60///
61/// See also [Nakadi Manual](https://nakadi.io/manual.html#/event-types_get*x-flow-id)
62#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
63pub struct FlowId(String);
64
65impl FlowId {
66    pub fn new<T: Into<String>>(s: T) -> Self {
67        FlowId(s.into())
68    }
69
70    pub fn new_disp<T: fmt::Display>(s: T) -> Self {
71        FlowId(s.to_string())
72    }
73
74    pub fn as_str(&self) -> &str {
75        &self.0.as_ref()
76    }
77
78    pub fn into_inner(self) -> String {
79        self.0
80    }
81
82    pub fn random() -> Self {
83        FlowId(uuid::Uuid::new_v4().to_string())
84    }
85}
86
87/// Crates a random `FlowId` when converted into a `FlowId`
88#[derive(Debug, Clone, Copy)]
89pub struct RandomFlowId;
90
91impl From<RandomFlowId> for FlowId {
92    fn from(_: RandomFlowId) -> Self {
93        Self::random()
94    }
95}
96
97impl From<()> for FlowId {
98    fn from(_: ()) -> Self {
99        Self::random()
100    }
101}
102
103impl From<String> for FlowId {
104    fn from(v: String) -> Self {
105        Self::new(v)
106    }
107}
108
109impl From<&str> for FlowId {
110    fn from(v: &str) -> Self {
111        Self::new(v)
112    }
113}
114
115impl From<uuid::Uuid> for FlowId {
116    fn from(v: uuid::Uuid) -> Self {
117        Self::new(v.to_string())
118    }
119}
120
121impl fmt::Display for FlowId {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        write!(f, "{}", self.0)
124    }
125}
126
127impl AsRef<str> for FlowId {
128    fn as_ref(&self) -> &str {
129        &self.0
130    }
131}
132
133impl FromStr for FlowId {
134    type Err = Error;
135
136    fn from_str(s: &str) -> Result<Self, Self::Err> {
137        Ok(FlowId::new(s))
138    }
139}
140
141/// An error for cases where further investigation is not necessary.
142///
143/// The `catch all` error...
144#[derive(Debug)]
145pub struct Error {
146    message: Option<String>,
147    source: Option<Box<dyn StdError + Send + Sync + 'static>>,
148}
149
150impl Error {
151    pub fn new<T: fmt::Display>(msg: T) -> Self {
152        Self {
153            message: Some(msg.to_string()),
154            source: None,
155        }
156    }
157
158    pub fn from_error<E: StdError + Send + Sync + 'static>(err: E) -> Self {
159        Self {
160            message: None,
161            source: Some(Box::new(err)),
162        }
163    }
164
165    pub fn boxed(self) -> Box<dyn StdError + Send + 'static> {
166        Box::new(self)
167    }
168}
169
170impl fmt::Display for Error {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        match (self.message.as_ref(), self.source().as_ref()) {
173            (Some(msg), Some(err)) => write!(f, "{} - Caused by: {}", msg, err)?,
174            (Some(msg), _) => write!(f, "{}", msg)?,
175            (_, Some(err)) => write!(f, "An error occurred caused by: {}", err)?,
176            _ => write!(f, "some unspecified error occurred")?,
177        }
178
179        Ok(())
180    }
181}
182
183impl StdError for Error {
184    fn source(&self) -> Option<&(dyn StdError + 'static)> {
185        self.source.as_ref().map(|p| &**p as &dyn StdError)
186    }
187}
188
189impl From<serde_json::Error> for Error {
190    fn from(err: serde_json::Error) -> Self {
191        Self::new(err.to_string())
192    }
193}
194
195/// Some API endpoints return an empty `String` where we would expect it to be `None`
196fn deserialize_empty_string_is_none<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
197where
198    D: Deserializer<'de>,
199    T: FromStr,
200    T::Err: std::fmt::Display,
201{
202    let s = String::deserialize(deserializer)?;
203    if s.is_empty() {
204        Ok(None)
205    } else {
206        let parsed = s
207            .parse::<T>()
208            .map_err(|err| SerdeError::custom(format!("deserialization error: {}", err)))?;
209        Ok(Some(parsed))
210    }
211}