1#![deny(missing_docs)]
15
16pub mod export;
17pub mod limits;
18#[cfg(feature = "media-grants")]
19pub mod media_grant;
20#[cfg(feature = "pagination")]
21pub mod pagination;
22
23use std::{fmt, str::FromStr};
24
25use serde::{Deserialize, Serialize};
26use thiserror::Error;
27
28#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
30#[serde(rename_all = "lowercase")]
31pub enum DeploymentEnvironment {
32 #[default]
34 Local,
35 Testing,
37 Staging,
39 Production,
41}
42
43impl DeploymentEnvironment {
44 const fn as_str(self) -> &'static str {
45 match self {
46 Self::Local => "local",
47 Self::Testing => "testing",
48 Self::Staging => "staging",
49 Self::Production => "production",
50 }
51 }
52}
53
54impl fmt::Display for DeploymentEnvironment {
55 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
56 formatter.write_str(self.as_str())
57 }
58}
59
60impl FromStr for DeploymentEnvironment {
61 type Err = ParseEnvironmentError;
62
63 fn from_str(value: &str) -> Result<Self, Self::Err> {
64 match value {
65 "local" => Ok(Self::Local),
66 "testing" => Ok(Self::Testing),
67 "staging" => Ok(Self::Staging),
68 "production" => Ok(Self::Production),
69 _ => Err(ParseEnvironmentError(value.to_owned())),
70 }
71 }
72}
73
74#[derive(Clone, Debug, Error, Eq, PartialEq)]
76#[error("unsupported environment `{0}`; expected local, testing, staging, or production")]
77pub struct ParseEnvironmentError(String);
78
79#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
81#[serde(rename_all = "lowercase")]
82pub enum LogFormat {
83 #[default]
85 Auto,
86 Json,
88 Pretty,
90}
91
92impl LogFormat {
93 #[must_use]
95 pub const fn resolve(self, environment: DeploymentEnvironment) -> Self {
96 match (self, environment) {
97 (Self::Auto, DeploymentEnvironment::Local) => Self::Pretty,
98 (
99 Self::Auto,
100 DeploymentEnvironment::Testing
101 | DeploymentEnvironment::Staging
102 | DeploymentEnvironment::Production,
103 ) => Self::Json,
104 (explicit, _) => explicit,
105 }
106 }
107}
108
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111pub enum ProcessKind {
112 Api,
114 Worker,
116 Migrate,
118 Seed,
120}
121
122impl ProcessKind {
123 const fn as_str(self) -> &'static str {
124 match self {
125 Self::Api => "api",
126 Self::Worker => "worker",
127 Self::Migrate => "migrate",
128 Self::Seed => "seed",
129 }
130 }
131}
132
133impl fmt::Display for ProcessKind {
134 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
135 formatter.write_str(self.as_str())
136 }
137}
138
139#[derive(Clone, Debug, Eq, PartialEq)]
141pub struct ServiceIdentity {
142 product: String,
143 process: ProcessKind,
144 version: String,
145 commit: String,
146 environment: DeploymentEnvironment,
147}
148
149impl ServiceIdentity {
150 pub fn new(
152 product: impl Into<String>,
153 process: ProcessKind,
154 version: impl Into<String>,
155 commit: impl Into<String>,
156 environment: DeploymentEnvironment,
157 ) -> Self {
158 Self {
159 product: product.into(),
160 process,
161 version: version.into(),
162 commit: commit.into(),
163 environment,
164 }
165 }
166
167 pub fn product(&self) -> &str {
169 &self.product
170 }
171
172 pub const fn process(&self) -> ProcessKind {
174 self.process
175 }
176
177 pub fn version(&self) -> &str {
179 &self.version
180 }
181
182 pub fn commit(&self) -> &str {
184 &self.commit
185 }
186
187 pub const fn environment(&self) -> DeploymentEnvironment {
189 self.environment
190 }
191
192 pub fn service_name(&self) -> String {
194 format!("{}-{}", self.product, self.process)
195 }
196}
197
198#[derive(Clone, Debug, Eq, PartialEq)]
200pub struct BuildInfo {
201 version: String,
202 commit: String,
203 rust_version: String,
204}
205
206impl BuildInfo {
207 pub fn new(
209 version: impl Into<String>,
210 commit: impl Into<String>,
211 rust_version: impl Into<String>,
212 ) -> Self {
213 Self {
214 version: version.into(),
215 commit: commit.into(),
216 rust_version: rust_version.into(),
217 }
218 }
219
220 pub fn version(&self) -> &str {
222 &self.version
223 }
224
225 pub fn commit(&self) -> &str {
227 &self.commit
228 }
229
230 pub fn rust_version(&self) -> &str {
232 &self.rust_version
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn shared_vocabulary_follows_the_telemetry_contract() {
242 let identity = ServiceIdentity::new(
243 "orders",
244 ProcessKind::Api,
245 "1.2.3",
246 "abc123",
247 DeploymentEnvironment::Production,
248 );
249
250 assert_eq!(identity.service_name(), "orders-api");
251 assert_eq!(identity.environment().to_string(), "production");
252 assert_eq!(
253 LogFormat::Auto.resolve(DeploymentEnvironment::Production),
254 LogFormat::Json
255 );
256 }
257
258 #[test]
259 fn deployment_environments_parse_display_and_serialize() {
260 for (text, environment) in [
261 ("local", DeploymentEnvironment::Local),
262 ("testing", DeploymentEnvironment::Testing),
263 ("staging", DeploymentEnvironment::Staging),
264 ("production", DeploymentEnvironment::Production),
265 ] {
266 assert_eq!(text.parse::<DeploymentEnvironment>(), Ok(environment));
267 assert_eq!(environment.to_string(), text);
268 assert_eq!(
269 serde_json::to_string(&environment).expect("serialize environment"),
270 format!("\"{text}\"")
271 );
272 assert_eq!(
273 serde_json::from_str::<DeploymentEnvironment>(&format!("\"{text}\""))
274 .expect("deserialize environment"),
275 environment
276 );
277 }
278 }
279}
280
281#[doc = include_str!("../README.md")]
283#[cfg(doctest)]
284struct ReadmeDoctests;