Skip to main content

api_bones/
common.rs

1//! Common RFC-conformant primitive types used across all external APIs.
2//!
3//! ## Standards
4//! - Timestamps: [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339) (Internet Date/Time Format)
5//! - Identifiers: [RFC 4122](https://www.rfc-editor.org/rfc/rfc4122) (UUID)
6
7#[cfg(all(not(feature = "std"), feature = "alloc", not(feature = "chrono")))]
8use alloc::string::String;
9
10/// RFC 3339 timestamp alias for API responses.
11///
12/// Serializes as `"2026-03-09T15:00:00Z"` via `chrono`'s serde integration.
13/// See [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339).
14///
15/// # Examples
16///
17/// ```rust
18/// use api_bones::common::Timestamp;
19/// let ts: Timestamp = "2026-01-01T00:00:00Z".parse().unwrap();
20/// assert_eq!(ts.to_rfc3339(), "2026-01-01T00:00:00+00:00");
21/// ```
22#[cfg(feature = "chrono")]
23pub type Timestamp = chrono::DateTime<chrono::Utc>;
24
25/// RFC 3339 timestamp alias (string fallback when `chrono` feature is disabled).
26///
27/// Requires `std` or `alloc` when `chrono` is disabled.
28#[cfg(all(not(feature = "chrono"), any(feature = "std", feature = "alloc")))]
29pub type Timestamp = String;
30
31/// RFC 4122 UUID v4 resource identifier.
32///
33/// See [RFC 4122](https://www.rfc-editor.org/rfc/rfc4122).
34///
35/// # Examples
36///
37/// ```rust
38/// use api_bones::common::ResourceId;
39/// let id: ResourceId = uuid::Uuid::nil();
40/// assert_eq!(id.to_string(), "00000000-0000-0000-0000-000000000000");
41/// ```
42#[cfg(feature = "uuid")]
43pub type ResourceId = uuid::Uuid;
44
45/// Parse an RFC 3339 timestamp string.
46///
47/// See [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339).
48///
49/// # Errors
50///
51/// Returns a `chrono::ParseError` if `s` is not a valid RFC 3339 timestamp.
52///
53/// # Examples
54///
55/// ```rust
56/// use api_bones::common::parse_timestamp;
57/// let ts = parse_timestamp("2026-03-09T15:00:00Z").unwrap();
58/// assert_eq!(ts.to_rfc3339(), "2026-03-09T15:00:00+00:00");
59/// ```
60#[cfg(feature = "chrono")]
61pub fn parse_timestamp(s: &str) -> Result<Timestamp, chrono::ParseError> {
62    s.parse()
63}
64
65/// Generate a new RFC 4122 v4 resource identifier.
66///
67/// See [RFC 4122 ยง4.4](https://www.rfc-editor.org/rfc/rfc4122#section-4.4).
68///
69/// # Examples
70///
71/// ```rust
72/// use api_bones::common::new_resource_id;
73/// let id = new_resource_id();
74/// assert_eq!(id.get_version_num(), 4);
75/// ```
76#[cfg(feature = "uuid")]
77#[must_use]
78pub fn new_resource_id() -> ResourceId {
79    uuid::Uuid::new_v4()
80}
81
82#[cfg(all(test, any(feature = "uuid", feature = "chrono")))]
83mod tests {
84    use super::*;
85
86    #[cfg(feature = "uuid")]
87    #[test]
88    fn resource_id_is_v4() {
89        let id = new_resource_id();
90        assert_eq!(id.get_version_num(), 4);
91    }
92
93    #[cfg(feature = "chrono")]
94    #[test]
95    fn timestamp_parses_rfc3339() {
96        // RFC 3339 format: YYYY-MM-DDTHH:MM:SSZ
97        let ts = parse_timestamp("2026-03-09T15:00:00Z").unwrap();
98        assert_eq!(ts.to_rfc3339(), "2026-03-09T15:00:00+00:00");
99    }
100}