1#![no_std]
2#![doc = include_str!("../README.md")]
3
4#[cfg(feature = "std")]
5extern crate std;
6
7macro_rules! impl_static_error {
8 ($error:ty, $($pattern:pat => $message:literal),+ $(,)?) => {
9 impl core::fmt::Display for $error {
10 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
11 formatter.write_str(match self {
12 $($pattern => $message,)+
13 })
14 }
15 }
16
17 impl core::error::Error for $error {}
18 };
19}
20
21pub mod action_polling;
22pub mod buffer;
23pub mod pagination;
24pub mod rate_limit;
25pub mod transport;
26
27#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
29pub enum Provider {
30 Hetzner,
32}
33
34#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
36pub enum ApiFamily {
37 Cloud,
39 Dns,
41 Security,
43 Storage,
45 Extended,
47}
48
49#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
51pub enum Method {
52 Get,
54 Post,
56 Put,
58 Delete,
60}
61
62impl Method {
63 #[must_use]
65 pub const fn as_str(self) -> &'static str {
66 match self {
67 Self::Get => "GET",
68 Self::Post => "POST",
69 Self::Put => "PUT",
70 Self::Delete => "DELETE",
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::{ApiFamily, Method, Provider};
78 use crate::action_polling::ActionPollError;
79 use crate::pagination::PaginationError;
80 use crate::rate_limit::RateLimitError;
81 use crate::transport::{ContentTypeError, RequestTargetError};
82 use core::fmt::{self, Write};
83
84 #[test]
85 fn exposes_provider_neutral_domains() {
86 assert_eq!(Provider::Hetzner, Provider::Hetzner);
87 assert_eq!(ApiFamily::Cloud, ApiFamily::Cloud);
88 assert_eq!(Method::Get, Method::Get);
89 assert_eq!(Method::Post.as_str(), "POST");
90 }
91
92 #[test]
93 fn public_errors_implement_payload_free_core_error() {
94 fn assert_error<E: core::error::Error>() {}
95
96 assert_error::<PaginationError>();
97 assert_error::<RateLimitError>();
98 assert_error::<ContentTypeError>();
99 assert_error::<RequestTargetError>();
100 assert_error::<ActionPollError<&'static str>>();
101
102 assert_display(PaginationError::PageZero, "page number must be nonzero");
103 assert_display(RateLimitError::LimitZero, "rate limit must be nonzero");
104 assert_display(ContentTypeError::Empty, "content type is empty");
105 assert_display(RequestTargetError::Empty, "request target is empty");
106 assert_display(
107 ActionPollError::Policy("sentinel-secret"),
108 "action poll policy failed",
109 );
110 }
111
112 fn assert_display(error: impl fmt::Display, expected: &str) {
113 let mut output = DisplayBuffer::new();
114 assert!(write!(&mut output, "{error}").is_ok());
115 assert_eq!(output.as_str(), expected);
116 }
117
118 struct DisplayBuffer {
119 bytes: [u8; 128],
120 len: usize,
121 }
122
123 impl DisplayBuffer {
124 const fn new() -> Self {
125 Self {
126 bytes: [0; 128],
127 len: 0,
128 }
129 }
130
131 fn as_str(&self) -> &str {
132 core::str::from_utf8(self.bytes.get(..self.len).unwrap_or_default()).unwrap_or_default()
133 }
134 }
135
136 impl Write for DisplayBuffer {
137 fn write_str(&mut self, value: &str) -> fmt::Result {
138 let end = self.len.checked_add(value.len()).ok_or(fmt::Error)?;
139 let target = self.bytes.get_mut(self.len..end).ok_or(fmt::Error)?;
140 target.copy_from_slice(value.as_bytes());
141 self.len = end;
142 Ok(())
143 }
144 }
145}