use crate::error::{Error, Result};
use chrono::{DateTime, Utc};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum TransferType {
TransferAllRights,
TransferCustody,
Transfer,
}
impl Default for TransferType {
fn default() -> Self {
TransferType::Transfer
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct Transfer {
pub id: String,
pub name: String,
pub transfer_type: TransferType,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Vec::is_empty"))]
pub realized_by: Vec<String>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Vec::is_empty"))]
pub planned_by: Vec<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub part_of: Option<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub realized_in: Option<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub in_scope_of: Option<String>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub note: Option<String>,
pub created_at: DateTime<Utc>,
}
impl Transfer {
pub fn builder() -> TransferBuilder {
TransferBuilder::default()
}
pub fn add_event(&mut self, event_id: impl Into<String>) {
self.realized_by.push(event_id.into());
}
pub fn add_commitment(&mut self, commitment_id: impl Into<String>) {
self.planned_by.push(commitment_id.into());
}
pub fn is_realized(&self) -> bool {
!self.realized_by.is_empty()
}
pub fn is_planned(&self) -> bool {
!self.planned_by.is_empty()
}
}
#[derive(Debug, Default)]
pub struct TransferBuilder {
id: Option<String>,
name: Option<String>,
transfer_type: Option<TransferType>,
realized_by: Vec<String>,
planned_by: Vec<String>,
part_of: Option<String>,
realized_in: Option<String>,
in_scope_of: Option<String>,
note: Option<String>,
}
impl TransferBuilder {
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn transfer_type(mut self, transfer_type: TransferType) -> Self {
self.transfer_type = Some(transfer_type);
self
}
pub fn realized_by(mut self, event_id: impl Into<String>) -> Self {
self.realized_by.push(event_id.into());
self
}
pub fn planned_by(mut self, commitment_id: impl Into<String>) -> Self {
self.planned_by.push(commitment_id.into());
self
}
pub fn part_of(mut self, exchange_id: impl Into<String>) -> Self {
self.part_of = Some(exchange_id.into());
self
}
pub fn realized_in(mut self, agreement_id: impl Into<String>) -> Self {
self.realized_in = Some(agreement_id.into());
self
}
pub fn in_scope_of(mut self, scope: impl Into<String>) -> Self {
self.in_scope_of = Some(scope.into());
self
}
pub fn note(mut self, note: impl Into<String>) -> Self {
self.note = Some(note.into());
self
}
pub fn build(self) -> Result<Transfer> {
let id = self.id.ok_or_else(|| Error::missing_field("id"))?;
let name = self.name.ok_or_else(|| Error::missing_field("name"))?;
let transfer_type = self.transfer_type.unwrap_or_default();
Ok(Transfer {
id,
name,
transfer_type,
realized_by: self.realized_by,
planned_by: self.planned_by,
part_of: self.part_of,
realized_in: self.realized_in,
in_scope_of: self.in_scope_of,
note: self.note,
created_at: Utc::now(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transfer_builder() {
let transfer = Transfer::builder()
.id("transfer-001")
.name("Sell tomatoes")
.transfer_type(TransferType::Transfer)
.build()
.unwrap();
assert_eq!(transfer.id, "transfer-001");
assert_eq!(transfer.transfer_type, TransferType::Transfer);
}
#[test]
fn test_transfer_with_events() {
let mut transfer = Transfer::builder()
.id("transfer-001")
.name("Test Transfer")
.build()
.unwrap();
transfer.add_event("event-001");
transfer.add_event("event-002");
assert!(transfer.is_realized());
assert_eq!(transfer.realized_by.len(), 2);
}
#[cfg(feature = "serde")]
#[test]
fn test_transfer_serialization() {
let transfer = Transfer::builder()
.id("transfer-001")
.name("Test")
.transfer_type(TransferType::TransferCustody)
.build()
.unwrap();
let json = serde_json::to_string(&transfer).unwrap();
let parsed: Transfer = serde_json::from_str(&json).unwrap();
assert_eq!(transfer.id, parsed.id);
assert_eq!(transfer.transfer_type, parsed.transfer_type);
}
}