gingr/mapping/mod.rs
1//! Promotion helpers from quarantined Gingr records into source-agnostic candidates.
2//!
3//! Mapping code may read Gingr-shaped DTOs, but the values it returns are domain
4//! candidates plus caller-owned source refs. Provider ids stay at this boundary;
5//! the domain does not learn Gingr vocabulary.
6//!
7//! ```rust
8//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
9//! use std::collections::BTreeMap;
10//!
11//! use domain::source;
12//! use gingr::{endpoint, mapping, response};
13//!
14//! let provider_record = response::OwnerRecord {
15//! id: endpoint::OwnerId::new(501),
16//! first_name: Some("Sam".to_owned()),
17//! last_name: Some("Rivera".to_owned()),
18//! email: Some(response::provider::Email::new("sam@example.test")),
19//! cell_phone: None,
20//! unknown: BTreeMap::new(),
21//! };
22//!
23//! let source_ref = source::RecordRef::new(
24//! source::System::Gingr,
25//! source::record::Id::try_new(provider_record.id.to_string())?,
26//! );
27//! let promoted = mapping::customer::contact_candidate(&provider_record)?;
28//!
29//! assert_eq!(source_ref.system(), source::System::Gingr);
30//! assert_eq!(source_ref.record_id().as_str(), "501");
31//! assert_eq!(promoted.provider_owner_id, endpoint::OwnerId::new(501));
32//! assert!(promoted.email.is_some());
33//! # Ok(())
34//! # }
35//! ```
36
37/// Gingr customer mapper boundary that promotes provider payloads into domain candidates.
38pub mod customer;
39/// Gingr pet mapper boundary that promotes provider payloads into domain candidates.
40pub mod pet;
41/// Gingr retail mapper boundary that promotes provider payloads into domain candidates.
42pub mod retail;
43
44/// Result type returned by fallible mapping operations.
45pub type Result<T> = core::result::Result<T, Error>;
46
47#[derive(Debug, Clone, PartialEq, Eq, strum::Display)]
48/// Gingr fields required by DTO-to-domain mapping routines.
49pub enum ProviderField {
50 #[strum(to_string = "owner name")]
51 /// Required Gingr owner-name field for customer mapping.
52 OwnerName,
53 #[strum(to_string = "animal name")]
54 /// Required Gingr animal-name field for pet mapping.
55 AnimalName,
56 #[strum(to_string = "retail item name")]
57 /// Required Gingr retail item name for product mapping.
58 RetailItemName,
59 #[strum(to_string = "retail item sku")]
60 /// Required Gingr retail SKU for product matching.
61 RetailItemSku,
62 #[strum(to_string = "retail item category")]
63 /// Required Gingr retail category for merchandising mapping.
64 RetailItemCategory,
65}
66
67#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
68/// Errors raised when provider values cannot safely cross this Gingr boundary.
69pub enum Error {
70 #[error("missing required Gingr provider field: {field}")]
71 /// DTO mapping cannot proceed because Gingr omitted a required field.
72 MissingRequiredProviderField {
73 /// Provider field required before this mapping can create a source-backed candidate.
74 field: ProviderField,
75 },
76 #[error("invalid domain value promoted from Gingr provider field {field}: {reason}")]
77 /// Signals that a domain value cannot be represented safely in storage.
78 InvalidDomainValue {
79 /// Provider field whose promoted value failed NVA domain validation.
80 field: ProviderField,
81 /// Validation reason returned by the downstream domain type or mapper.
82 reason: String,
83 },
84}