Skip to main content

gingr/endpoint/
owners_animals.rs

1use super::{AnimalId, FormId, Method, OwnerId, Request, ReservationId, non_empty_text};
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4/// Opaque provider `where` clause string passed to Gingr owner/animal search endpoints.
5pub struct ProviderWhereClause {
6    field: String,
7    value: String,
8}
9
10impl ProviderWhereClause {
11    /// Constructs this typed Gingr boundary value after the caller has chosen the provider input to trust.
12    pub fn new(field: impl Into<String>, value: impl Into<String>) -> Self {
13        Self {
14            field: field.into(),
15            value: value.into(),
16        }
17    }
18
19    fn parameter_pair(&self) -> (String, String) {
20        (format!("params[{}]", self.field), self.value.clone())
21    }
22}
23
24#[derive(Clone, Debug, Default, PartialEq, Eq)]
25/// Request descriptor for Gingr owner/customer search results.
26pub struct Owners {
27    provider_where_clauses: Vec<ProviderWhereClause>,
28}
29
30impl Owners {
31    /// Starts a builder that makes each provider parameter explicit before request capture.
32    pub fn builder() -> OwnersBuilder {
33        OwnersBuilder::default()
34    }
35}
36
37#[derive(Clone, Debug, Default)]
38/// Builder for provider-side owner search filters and explicit sensitive lookup values.
39pub struct OwnersBuilder {
40    provider_where_clauses: Vec<ProviderWhereClause>,
41}
42
43impl OwnersBuilder {
44    /// Applies a raw Gingr where clause while keeping it typed at the boundary.
45    pub fn provider_where_clause(mut self, clause: ProviderWhereClause) -> Self {
46        self.provider_where_clauses.push(clause);
47        self
48    }
49
50    /// Finalizes the provider request descriptor after required fields are present and wrappers have validated local invariants.
51    pub fn build(self) -> Owners {
52        Owners {
53            provider_where_clauses: self.provider_where_clauses,
54        }
55    }
56}
57
58impl Request for Owners {
59    fn method(&self) -> Method {
60        Method::Get
61    }
62
63    fn path(&self) -> &'static str {
64        "/api/v1/owners"
65    }
66
67    fn parameters(&self) -> Vec<(String, String)> {
68        self.provider_where_clauses
69            .iter()
70            .map(ProviderWhereClause::parameter_pair)
71            .collect()
72    }
73}
74
75#[derive(Clone, Debug, Default, PartialEq, Eq)]
76/// Request descriptor for Gingr animal/pet search results.
77pub struct Animals {
78    provider_where_clauses: Vec<ProviderWhereClause>,
79}
80
81impl Animals {
82    /// Starts a builder that makes each provider parameter explicit before request capture.
83    pub fn builder() -> AnimalsBuilder {
84        AnimalsBuilder::default()
85    }
86}
87
88#[derive(Clone, Debug, Default)]
89/// Builder for provider-side animal search filters and explicit sensitive lookup values.
90pub struct AnimalsBuilder {
91    provider_where_clauses: Vec<ProviderWhereClause>,
92}
93
94impl AnimalsBuilder {
95    /// Applies a raw Gingr where clause while keeping it typed at the boundary.
96    pub fn provider_where_clause(mut self, clause: ProviderWhereClause) -> Self {
97        self.provider_where_clauses.push(clause);
98        self
99    }
100
101    /// Finalizes the provider request descriptor after required fields are present and wrappers have validated local invariants.
102    pub fn build(self) -> Animals {
103        Animals {
104            provider_where_clauses: self.provider_where_clauses,
105        }
106    }
107}
108
109impl Request for Animals {
110    fn method(&self) -> Method {
111        Method::Get
112    }
113
114    fn path(&self) -> &'static str {
115        "/api/v1/animals"
116    }
117
118    fn parameters(&self) -> Vec<(String, String)> {
119        self.provider_where_clauses
120            .iter()
121            .map(ProviderWhereClause::parameter_pair)
122            .collect()
123    }
124}
125
126#[derive(Clone, Debug, PartialEq, Eq)]
127/// Sensitive lookup term such as email or phone that must be redacted from request diagnostics.
128pub struct SensitiveLookup(String);
129
130impl SensitiveLookup {
131    /// Constructs this typed Gingr boundary value after the caller has chosen the provider input to trust.
132    pub fn new(value: impl Into<String>) -> super::Result<Self> {
133        Ok(Self(non_empty_text(value)?))
134    }
135
136    fn as_str(&self) -> &str {
137        &self.0
138    }
139}
140
141#[derive(Clone, Debug, PartialEq, Eq)]
142/// Typed Gingr owner lookup strategies, separating sensitive search inputs from normal IDs.
143pub enum OwnerLookup {
144    /// Lookup uses a Gingr owner identifier.
145    OwnerId(OwnerId),
146    /// Lookup uses a Gingr animal identifier.
147    AnimalId(AnimalId),
148    /// Lookup uses a Gingr reservation identifier.
149    ReservationId(ReservationId),
150    /// Lookup uses an owner phone number and should be treated as sensitive.
151    Phone(SensitiveLookup),
152    /// Lookup uses an owner email address and should be treated as sensitive.
153    Email(SensitiveLookup),
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
157/// Request descriptor for one Gingr owner/customer record by provider ID.
158pub struct Owner {
159    lookup: OwnerLookup,
160}
161
162impl Owner {
163    /// Builds an owner lookup by Gingr owner identifier.
164    pub fn by_id(id: OwnerId) -> Self {
165        Self {
166            lookup: OwnerLookup::OwnerId(id),
167        }
168    }
169
170    /// Builds an owner lookup by Gingr animal identifier.
171    pub fn by_animal(id: AnimalId) -> Self {
172        Self {
173            lookup: OwnerLookup::AnimalId(id),
174        }
175    }
176
177    /// Builds an owner lookup by Gingr reservation identifier.
178    pub fn by_reservation(id: ReservationId) -> Self {
179        Self {
180            lookup: OwnerLookup::ReservationId(id),
181        }
182    }
183
184    /// Builds an owner lookup by phone number.
185    pub fn by_phone(phone: SensitiveLookup) -> Self {
186        Self {
187            lookup: OwnerLookup::Phone(phone),
188        }
189    }
190
191    /// Builds an owner lookup by email address.
192    pub fn by_email(email: SensitiveLookup) -> Self {
193        Self {
194            lookup: OwnerLookup::Email(email),
195        }
196    }
197}
198
199impl Request for Owner {
200    fn method(&self) -> Method {
201        Method::Get
202    }
203
204    fn path(&self) -> &'static str {
205        "/api/v1/owner"
206    }
207
208    fn parameters(&self) -> Vec<(String, String)> {
209        match &self.lookup {
210            OwnerLookup::OwnerId(id) => vec![("id".to_owned(), id.to_string())],
211            OwnerLookup::AnimalId(id) => vec![("animal_id".to_owned(), id.to_string())],
212            OwnerLookup::ReservationId(id) => vec![("reservation_id".to_owned(), id.to_string())],
213            OwnerLookup::Phone(phone) => vec![("phone".to_owned(), phone.as_str().to_owned())],
214            OwnerLookup::Email(email) => vec![("email".to_owned(), email.as_str().to_owned())],
215        }
216    }
217
218    fn sensitive_parameter_names(&self) -> &'static [&'static str] {
219        match self.lookup {
220            OwnerLookup::Phone(_) => &["phone"],
221            OwnerLookup::Email(_) => &["email"],
222            _ => &[],
223        }
224    }
225}
226
227#[derive(Clone, Copy, Debug, PartialEq, Eq)]
228/// Gingr owner/animal form classes supported by form search endpoints.
229pub enum FormKind {
230    /// Provider value refers to a Gingr owner/customer.
231    Owner,
232    /// Provider value refers to a Gingr animal/pet.
233    Animal,
234}
235
236impl FormKind {
237    /// Returns the Gingr form identifier associated with this form class.
238    pub const fn form_id(self) -> FormId {
239        match self {
240            Self::Owner => FormId::new(1),
241            Self::Animal => FormId::new(2),
242        }
243    }
244}
245
246impl core::fmt::Display for FormKind {
247    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
248        let value = match self {
249            Self::Owner => "owner_form",
250            Self::Animal => "animal_form",
251        };
252        formatter.write_str(value)
253    }
254}
255
256#[derive(Clone, Debug, PartialEq, Eq)]
257/// Request descriptor for a Gingr form definition by provider form ID.
258pub struct Form {
259    kind: FormKind,
260}
261
262impl Form {
263    /// Wraps an already-observed Gingr identifier without claiming anything beyond provider provenance.
264    pub const fn new(kind: FormKind) -> Self {
265        Self { kind }
266    }
267}
268
269impl Request for Form {
270    fn method(&self) -> Method {
271        Method::Get
272    }
273
274    fn path(&self) -> &'static str {
275        "/forms/get_form"
276    }
277
278    fn parameters(&self) -> Vec<(String, String)> {
279        vec![("form".to_owned(), self.kind.to_string())]
280    }
281}
282
283/// Gingr custom field endpoint boundary with provider parameters kept explicit.
284pub mod custom_field {
285    use super::*;
286
287    #[derive(Clone, Debug, PartialEq, Eq)]
288    /// Non-empty provider custom-field name used for Gingr custom-field search.
289    pub struct Name(String);
290
291    impl Name {
292        /// Constructs this typed Gingr boundary value after the caller has chosen the provider input to trust.
293        pub fn new(value: impl Into<String>) -> super::super::Result<Self> {
294            Ok(Self(non_empty_text(value)?))
295        }
296    }
297
298    #[derive(Clone, Debug, PartialEq, Eq)]
299    /// Request descriptor for Gingr custom-field search across provider owner or animal fields.
300    pub struct Search {
301        form: FormKind,
302        field_name: Name,
303        search: SensitiveLookup,
304    }
305
306    impl Search {
307        /// Starts a builder that makes each provider parameter explicit before request capture.
308        pub fn builder() -> SearchBuilder {
309            SearchBuilder::default()
310        }
311    }
312
313    #[derive(Clone, Debug, Default)]
314    /// Builder for custom-field searches that keeps field name, text value, and optional location explicit.
315    pub struct SearchBuilder {
316        form: Option<FormKind>,
317        field_name: Option<Name>,
318        search: Option<SensitiveLookup>,
319    }
320
321    impl SearchBuilder {
322        /// Selects the Gingr owner/animal form to search.
323        pub fn form(mut self, form: FormKind) -> Self {
324            self.form = Some(form);
325            self
326        }
327
328        /// Selects the provider form field being searched.
329        pub fn field_name(mut self, field_name: Name) -> Self {
330            self.field_name = Some(field_name);
331            self
332        }
333
334        /// Sets the provider search string for form lookup.
335        pub fn search(mut self, search: SensitiveLookup) -> Self {
336            self.search = Some(search);
337            self
338        }
339
340        /// Finalizes the provider request descriptor after required fields are present and wrappers have validated local invariants.
341        pub fn build(self) -> Search {
342            Search {
343                form: self.form.expect("Search requires form"),
344                field_name: self.field_name.expect("Search requires field_name"),
345                search: self.search.expect("Search requires search"),
346            }
347        }
348    }
349
350    impl Request for Search {
351        fn method(&self) -> Method {
352            Method::Get
353        }
354
355        fn path(&self) -> &'static str {
356            "/api/v1/custom_field_search"
357        }
358
359        fn parameters(&self) -> Vec<(String, String)> {
360            vec![
361                ("form_id".to_owned(), self.form.form_id().to_string()),
362                ("field_name".to_owned(), self.field_name.0.clone()),
363                ("search".to_owned(), self.search.as_str().to_owned()),
364            ]
365        }
366
367        fn sensitive_parameter_names(&self) -> &'static [&'static str] {
368            &["search"]
369        }
370    }
371}
372
373#[derive(Clone, Debug, PartialEq, Eq)]
374/// Request descriptor for Gingr feeding or medication info tied to one animal record.
375pub struct AnimalCareInfo {
376    path: &'static str,
377    animal_id: AnimalId,
378}
379
380impl AnimalCareInfo {
381    /// Builds an animal-care endpoint request for feeding instructions.
382    pub fn feeding(animal_id: AnimalId) -> Self {
383        Self {
384            path: "/api/v1/get_feeding_info",
385            animal_id,
386        }
387    }
388
389    /// Builds an animal-care endpoint request for medication instructions.
390    pub fn medication(animal_id: AnimalId) -> Self {
391        Self {
392            path: "/api/v1/get_medication_info",
393            animal_id,
394        }
395    }
396}
397
398impl Request for AnimalCareInfo {
399    fn method(&self) -> Method {
400        Method::Get
401    }
402
403    fn path(&self) -> &'static str {
404        self.path
405    }
406
407    fn parameters(&self) -> Vec<(String, String)> {
408        vec![("animal_id".to_owned(), self.animal_id.to_string())]
409    }
410}