Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Commit

Permalink
Create and serialize most Message kinds (#37)
Browse files Browse the repository at this point in the history
* Create Close messages

* Create Quote messages

* Create Order messages

* Create OrderStatus messages

* Don't serialize None
  • Loading branch information
Diane Huxley authored Dec 8, 2023
1 parent 58f7116 commit f5c9371
Show file tree
Hide file tree
Showing 8 changed files with 439 additions and 4 deletions.
3 changes: 2 additions & 1 deletion crates/protocol/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ jsonschema = "0.17.1"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
type-safe-id = { version = "0.2.1", features = ["serde"] }
thiserror = "1.0.50"
thiserror = "1.0.50"
serde_with = "3.4.0"
83 changes: 83 additions & 0 deletions crates/protocol/src/message/close.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use ::serde::{Deserialize, Serialize};
use chrono::Utc;
use serde_with::skip_serializing_none;
use type_safe_id::{DynamicType, TypeSafeId};

use super::{Message, MessageError, MessageKind, MessageMetadata};

pub struct Close;

impl Close {
pub fn create(
from: String,
to: String,
exchange_id: TypeSafeId<DynamicType>,
reason: Option<String>,
) -> Result<Message<CloseData>, MessageError> {
let metadata = MessageMetadata {
from,
to,
kind: MessageKind::Close,
id: MessageKind::Close.typesafe_id()?,
exchange_id: exchange_id,
created_at: Utc::now(),
};

let data = CloseData { reason };

Ok(Message {
metadata,
data,
signature: None,
})
}
}

/// A struct representing the data contained within the [`Message`] for a [`Close`].
///
/// See [Quote](https://github.com/TBD54566975/tbdex/tree/main/specs/protocol#close) for more
/// information.
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[skip_serializing_none]
#[serde(rename_all = "camelCase")]
pub struct CloseData {
/// an explanation of why the exchange is being closed/completed
reason: Option<String>,
}

#[cfg(test)]
mod tests {
use super::*;
use crate::test_data::TestData;

#[test]
fn can_create() {
let close = Close::create(
"did:example:from_1234".to_string(),
"did:example:to_1234".to_string(),
MessageKind::Rfq.typesafe_id().unwrap(),
Some("I don't want to do business with you any more".to_string()),
)
.expect("failed to create Close");

assert_eq!(
close.data.reason,
Some("I don't want to do business with you any more".to_string())
);
assert_eq!(close.metadata.id.type_prefix(), "close");
}

#[test]
fn can_parse_close_from_json() {
let close = TestData::get_close(
"did:example:from_1234".to_string(),
MessageKind::Rfq
.typesafe_id()
.expect("failed to generate exchange_id"),
);
let json = serde_json::to_string(&close).expect("failed to serialize Close");
let parsed_close: Message<CloseData> =
serde_json::from_str(&json).expect("failed to deserialize Close");
assert_eq!(close, parsed_close);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
pub mod close;
pub mod order;
pub mod order_status;
pub mod quote;

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::to_string;
use type_safe_id::{DynamicType, TypeSafeId};

/// An enum representing all possible [`Message`] kinds.
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum MessageKind {
Close,
Expand All @@ -15,7 +20,7 @@ pub enum MessageKind {
}

/// A struct representing the metadata present on every [`Message`].
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MessageMetadata {
/// The message's ID
Expand All @@ -34,7 +39,7 @@ pub struct MessageMetadata {
}

/// A struct representing the structure and common functionality available to all Messages.
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Message<T> {
/// An object containing fields about the message
Expand Down
72 changes: 72 additions & 0 deletions crates/protocol/src/message/order.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use ::serde::{Deserialize, Serialize};
use chrono::Utc;
use type_safe_id::{DynamicType, TypeSafeId};

use super::{Message, MessageError, MessageKind, MessageMetadata};

pub struct Order;

impl Order {
pub fn create(
from: String,
to: String,
exchange_id: TypeSafeId<DynamicType>,
) -> Result<Message<OrderData>, MessageError> {
let metadata = MessageMetadata {
from,
to,
kind: MessageKind::Order,
id: MessageKind::Order.typesafe_id()?,
exchange_id: exchange_id,
created_at: Utc::now(),
};

let data = OrderData ;

Ok(Message {
metadata,
data,
signature: None,
})
}
}

/// A struct representing the data contained within the [`Message`] for an [`Order`].
/// Currently, [`Order`] contains no data fields.
///
/// See [Order](https://github.com/TBD54566975/tbdex/tree/main/specs/protocol#order) for more
/// information.
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderData;

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn can_create() {
let order = Order::create(
"did:example:from_1234".to_string(),
"did:example:to_1234".to_string(),
MessageKind::Rfq.typesafe_id().unwrap(),
)
.expect("failed to create Order");

assert_eq!(order.metadata.id.type_prefix(), "order");
}

#[test]
fn can_parse_order_from_json() {
let order = Order::create(
"did:example:from_1234".to_string(),
"did:example:to_1234".to_string(),
MessageKind::Rfq.typesafe_id().unwrap(),
)
.expect("Could not create Order");
let json: String = serde_json::to_string(&order).expect("failed to serialize Order");
let parsed_order: Message<OrderData> =
serde_json::from_str(&json).expect("failed to deserialize Order");
assert_eq!(order, parsed_order);
}
}
80 changes: 80 additions & 0 deletions crates/protocol/src/message/order_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use ::serde::{Deserialize, Serialize};
use chrono::Utc;
use type_safe_id::{DynamicType, TypeSafeId};

use super::{Message, MessageError, MessageKind, MessageMetadata};

pub struct OrderStatus;

impl OrderStatus {
pub fn create(
from: String,
to: String,
exchange_id: TypeSafeId<DynamicType>,
order_status: String,
) -> Result<Message<OrderStatusData>, MessageError> {
let metadata = MessageMetadata {
from,
to,
kind: MessageKind::OrderStatus,
id: MessageKind::OrderStatus.typesafe_id()?,
exchange_id: exchange_id,
created_at: Utc::now(),
};

let data = OrderStatusData {
order_status,
};

Ok(Message {
metadata,
data,
signature: None,
})
}
}

/// A struct representing the data contained within the [`Message`] for an [`OrderStatus`].
/// Currently, [`OrderStatus`] contains no data fields.
///
/// See [OrderStatus](https://github.com/TBD54566975/tbdex/tree/main/specs/protocol#orderstatus) for more
/// information.
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderStatusData {
/// Current status of Order that's being executed
order_status: String,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn can_create() {
let order_status: Message<OrderStatusData> = OrderStatus::create(
"did:example:from_1234".to_string(),
"did:example:to_1234".to_string(),
MessageKind::Rfq.typesafe_id().unwrap(),
"COMPLETED".to_string()
)
.expect("failed to create OrderStatus");

assert_eq!(order_status.metadata.id.type_prefix(), "orderstatus");
}

#[test]
fn can_parse_order_status_from_json() {
let order_status = OrderStatus::create(
"did:example:from_1234".to_string(),
"did:example:to_1234".to_string(),
MessageKind::Rfq.typesafe_id().unwrap(),
"COMPLETED".to_string()
)
.expect("Could not create OrderStatus");
let json: String = serde_json::to_string(&order_status).expect("failed to serialize OrderStatus");
let parsed_order_status: Message<OrderStatusData> =
serde_json::from_str(&json).expect("failed to deserialize OrderStatus");
assert_eq!(order_status, parsed_order_status);
}
}
Loading

0 comments on commit f5c9371

Please sign in to comment.