Skip to main content

systemprompt_models/api/responses/
envelopes.rs

1//! Primary HATEOAS-style response envelopes.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9use crate::api::pagination::PaginationInfo;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct ResponseLinks {
13    pub self_link: String,
14
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub next: Option<String>,
17
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub prev: Option<String>,
20
21    pub docs: String,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ResponseMeta {
26    pub timestamp: DateTime<Utc>,
27
28    pub version: String,
29
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub pagination: Option<PaginationInfo>,
32}
33
34impl ResponseMeta {
35    #[must_use]
36    pub fn new() -> Self {
37        Self {
38            timestamp: Utc::now(),
39            version: "1.0.0".to_owned(),
40            pagination: None,
41        }
42    }
43
44    #[must_use]
45    pub fn with_pagination(mut self, pagination: PaginationInfo) -> Self {
46        self.pagination = Some(pagination);
47        self
48    }
49}
50
51impl Default for ResponseMeta {
52    fn default() -> Self {
53        Self::new()
54    }
55}
56
57#[derive(Debug, Serialize, Deserialize)]
58pub struct ApiResponse<T>
59where
60    T: 'static,
61{
62    pub data: T,
63
64    pub meta: ResponseMeta,
65
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub links: Option<ResponseLinks>,
68}
69
70impl<T: Serialize + 'static> ApiResponse<T> {
71    pub fn new(data: T) -> Self {
72        Self {
73            data,
74            meta: ResponseMeta::new(),
75            links: None,
76        }
77    }
78
79    #[must_use]
80    pub fn with_links(mut self, links: ResponseLinks) -> Self {
81        self.links = Some(links);
82        self
83    }
84
85    #[must_use]
86    pub fn with_meta(mut self, meta: ResponseMeta) -> Self {
87        self.meta = meta;
88        self
89    }
90}
91
92#[derive(Debug, Serialize, Deserialize)]
93pub struct SingleResponse<T>
94where
95    T: 'static,
96{
97    pub data: T,
98
99    pub meta: ResponseMeta,
100
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub links: Option<ResponseLinks>,
103}
104
105impl<T: Serialize + 'static> SingleResponse<T> {
106    pub fn new(data: T) -> Self {
107        Self {
108            data,
109            meta: ResponseMeta::new(),
110            links: None,
111        }
112    }
113
114    pub const fn with_meta(data: T, meta: ResponseMeta) -> Self {
115        Self {
116            data,
117            meta,
118            links: None,
119        }
120    }
121
122    #[must_use]
123    pub fn with_links(mut self, links: ResponseLinks) -> Self {
124        self.links = Some(links);
125        self
126    }
127}
128
129#[derive(Debug, Serialize, Deserialize)]
130pub struct CollectionResponse<T>
131where
132    T: 'static,
133{
134    pub data: Vec<T>,
135
136    pub meta: ResponseMeta,
137
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub links: Option<ResponseLinks>,
140}
141
142impl<T: Serialize + 'static> CollectionResponse<T> {
143    pub fn new(data: Vec<T>) -> Self {
144        Self {
145            data,
146            meta: ResponseMeta::new(),
147            links: None,
148        }
149    }
150
151    pub fn paginated(data: Vec<T>, pagination: PaginationInfo) -> Self {
152        Self {
153            data,
154            meta: ResponseMeta::new().with_pagination(pagination),
155            links: None,
156        }
157    }
158
159    #[must_use]
160    pub fn with_links(mut self, links: ResponseLinks) -> Self {
161        self.links = Some(links);
162        self
163    }
164}