use alloc::string::String;
use alloc::vec::Vec;
use crate::{JsonUtil, PublicKey, Tag};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StallData {
pub id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub currency: String,
pub shipping: Vec<ShippingMethod>,
}
impl StallData {
pub fn new(id: &str, name: &str, currency: &str) -> Self {
Self {
id: id.into(),
name: name.into(),
description: None,
currency: currency.into(),
shipping: Vec::new(),
}
}
pub fn description(self, description: &str) -> Self {
Self {
description: Some(description.into()),
..self
}
}
pub fn shipping(self, shipping: Vec<ShippingMethod>) -> Self {
Self { shipping, ..self }
}
}
impl From<StallData> for Vec<Tag> {
fn from(value: StallData) -> Self {
vec![Tag::identifier(value.id)]
}
}
impl JsonUtil for StallData {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductData {
pub id: String,
pub stall_id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub images: Option<Vec<String>>,
pub currency: String,
pub price: f64,
pub quantity: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub specs: Option<Vec<Vec<String>>>,
pub shipping: Vec<ShippingCost>,
#[serde(skip_serializing)]
pub categories: Option<Vec<String>>,
}
impl ProductData {
pub fn new(id: &str, stall_id: &str, name: &str, currency: &str) -> Self {
Self {
id: id.into(),
stall_id: stall_id.into(),
name: name.into(),
description: None,
images: None,
currency: currency.into(),
price: 0.0,
quantity: 1,
specs: None,
shipping: Vec::new(),
categories: None,
}
}
pub fn description(self, description: &str) -> Self {
Self {
description: Some(description.into()),
..self
}
}
pub fn images(self, images: Vec<String>) -> Self {
Self {
images: Some(images),
..self
}
}
pub fn price(self, price: f64) -> Self {
Self { price, ..self }
}
pub fn quantity(self, quantity: u64) -> Self {
Self { quantity, ..self }
}
pub fn specs(self, specs: Vec<Vec<String>>) -> Self {
let valid = specs.into_iter().filter(|spec| spec.len() == 2).collect();
Self {
specs: Some(valid),
..self
}
}
pub fn shipping(self, shipping: Vec<ShippingCost>) -> Self {
Self { shipping, ..self }
}
pub fn categories(self, categories: Vec<String>) -> Self {
Self {
categories: Some(categories),
..self
}
}
}
impl From<ProductData> for Vec<Tag> {
fn from(value: ProductData) -> Self {
let categories: Vec<String> = value.categories.unwrap_or_default();
let mut tags: Vec<Tag> = Vec::with_capacity(1 + categories.len());
tags.push(Tag::identifier(value.stall_id));
categories.iter().for_each(|cat| {
tags.push(Tag::hashtag(cat));
});
tags
}
}
impl JsonUtil for ProductData {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShippingMethod {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
pub cost: f64,
pub regions: Vec<String>,
}
impl ShippingMethod {
pub fn new<S>(id: S, cost: f64) -> Self
where
S: Into<String>,
{
Self {
id: id.into(),
name: None,
cost,
regions: Vec::new(),
}
}
pub fn name<S>(self, name: S) -> Self
where
S: Into<String>,
{
Self {
name: Some(name.into()),
..self
}
}
pub fn regions(self, regions: Vec<String>) -> Self {
Self { regions, ..self }
}
pub fn get_shipping_cost(&self) -> ShippingCost {
ShippingCost {
id: self.id.clone(),
cost: self.cost,
}
}
}
impl JsonUtil for ShippingMethod {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShippingCost {
pub id: String,
pub cost: f64,
}
impl JsonUtil for ShippingCost {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomerOrder {
pub id: String,
#[serde(rename = "type")]
pub r#type: usize,
pub name: Option<String>,
pub address: Option<String>,
pub message: Option<String>,
pub contact: CustomerContact,
pub items: Vec<CustomerOrderItem>,
pub shipping_id: String,
}
impl JsonUtil for CustomerOrder {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MerchantPaymentRequest {
pub id: String,
#[serde(rename = "type")]
pub r#type: usize,
pub payment_options: Vec<PaymentOption>,
}
impl JsonUtil for MerchantPaymentRequest {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MerchantVerifyPayment {
pub id: String,
#[serde(rename = "type")]
pub r#type: usize,
pub paid: bool,
pub shipped: bool,
}
impl JsonUtil for MerchantVerifyPayment {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomerContact {
pub nostr: Option<PublicKey>,
pub phone: Option<String>,
pub email: Option<String>,
}
impl JsonUtil for CustomerContact {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CustomerOrderItem {
pub id: String,
pub quantity: u64,
}
impl JsonUtil for CustomerOrderItem {
type Err = serde_json::Error;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentOption {
#[serde(rename = "type")]
pub r#type: String,
pub link: String,
}
impl JsonUtil for PaymentOption {
type Err = serde_json::Error;
}
#[cfg(test)]
mod tests {
use alloc::string::String;
use alloc::vec::Vec;
use super::*;
#[test]
fn test_stall_data() {
let stall = StallData::new("123", "Test Stall", "USD")
.description("Test Description")
.shipping(vec![ShippingMethod::new("123", 5.0).name("default")]);
let tags: Vec<Tag> = stall.clone().into();
assert_eq!(tags.len(), 1);
assert_eq!(tags[0], Tag::identifier("123"), "tags contains stall id");
let string: String = stall.as_json();
assert_eq!(
string,
r#"{"id":"123","name":"Test Stall","description":"Test Description","currency":"USD","shipping":[{"id":"123","name":"default","cost":5.0,"regions":[]}]}"#
);
}
#[test]
fn test_product_data() {
let product = ProductData::new("123", "456", "Test Product", "USD")
.images(vec!["https://example.com/image.png".into()])
.price(10.0)
.quantity(10)
.specs(vec![vec!["Size".into(), "M".into()]])
.shipping(vec![ShippingCost {
id: "123".into(),
cost: 5.0,
}])
.categories(vec!["Test".into(), "Product".into()]);
let tags: Vec<Tag> = product.clone().into();
assert_eq!(tags.len(), 3);
assert_eq!(tags[0], Tag::identifier("456"), "tags contains stall id");
assert_eq!(tags[1], Tag::hashtag("Test"), "tags contains category");
assert_eq!(tags[2], Tag::hashtag("Product"), "tags contains category");
let string: String = product.as_json();
assert_eq!(
string,
r#"{"id":"123","stall_id":"456","name":"Test Product","images":["https://example.com/image.png"],"currency":"USD","price":10.0,"quantity":10,"specs":[["Size","M"]],"shipping":[{"id":"123","cost":5.0}]}"#
);
}
}