Files
projectmycelium/src/models/builders.rs

3388 lines
110 KiB
Rust

//! Builder patterns for all marketplace models
//! This module provides a centralized, maintainable way to construct complex structs
//! with sensible defaults and validation.
use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use serde_json::Value;
use std::collections::HashMap;
use super::{
user::{PublishedApp, DeploymentStat, ResourceUtilization, User, UserRole, ServiceBooking},
product::{Product, ProductAttribute, ProductAvailability, ProductMetadata},
order::{Order, OrderItem, OrderStatus, PaymentDetails, Address, PurchaseType},
};
use crate::services::user_persistence::AppDeployment;
// =============================================================================
// USER MODEL BUILDERS
// =============================================================================
#[derive(Default)]
pub struct PublishedAppBuilder {
id: Option<String>,
name: Option<String>,
category: Option<String>,
version: Option<String>,
status: Option<String>,
deployments: Option<i32>,
rating: Option<f32>,
monthly_revenue: Option<i32>,
last_updated: Option<String>,
auto_healing: Option<bool>,
}
impl PublishedAppBuilder {
pub fn new() -> Self {
Self::default()
}
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 category(mut self, category: impl Into<String>) -> Self {
self.category = Some(category.into());
self
}
pub fn version(mut self, version: impl Into<String>) -> Self {
self.version = Some(version.into());
self
}
pub fn status(mut self, status: impl Into<String>) -> Self {
self.status = Some(status.into());
self
}
pub fn deployments(mut self, deployments: i32) -> Self {
self.deployments = Some(deployments);
self
}
pub fn rating(mut self, rating: f32) -> Self {
self.rating = Some(rating);
self
}
pub fn monthly_revenue_usd(mut self, revenue: i32) -> Self {
self.monthly_revenue = Some(revenue);
self
}
pub fn last_updated(mut self, updated: impl Into<String>) -> Self {
self.last_updated = Some(updated.into());
self
}
pub fn auto_healing(mut self, enabled: bool) -> Self {
self.auto_healing = Some(enabled);
self
}
pub fn build(self) -> Result<PublishedApp, String> {
Ok(PublishedApp {
id: self.id.ok_or("id is required")?,
name: self.name.ok_or("name is required")?,
description: None,
category: self.category.ok_or("category is required")?,
version: self.version.unwrap_or_else(|| "1.0.0".to_string()),
price_usd: rust_decimal::Decimal::ZERO,
deployment_count: self.deployments.unwrap_or(0),
status: self.status.unwrap_or_else(|| "Active".to_string()),
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
auto_scaling: Some(false),
revenue_history: Vec::new(),
deployments: self.deployments.unwrap_or(0),
rating: self.rating.unwrap_or(0.0),
monthly_revenue_usd: rust_decimal::Decimal::from(self.monthly_revenue.unwrap_or(0)),
last_updated: chrono::Utc::now(), // Use current time instead of string
auto_healing: self.auto_healing.or(Some(true)),
})
}
}
impl PublishedApp {
pub fn builder() -> PublishedAppBuilder {
PublishedAppBuilder::new()
}
// Template methods for common app types
pub fn analytics_template(id: impl Into<String>, name: impl Into<String>) -> Self {
Self::builder()
.id(id)
.name(name)
.category("Analytics")
.status("Active")
.auto_healing(true)
.build()
.unwrap()
}
pub fn database_template(id: impl Into<String>, name: impl Into<String>) -> Self {
Self::builder()
.id(id)
.name(name)
.category("Database")
.status("Active")
.auto_healing(false) // Databases need manual intervention
.build()
.unwrap()
}
pub fn web_template(id: impl Into<String>, name: impl Into<String>) -> Self {
Self::builder()
.id(id)
.name(name)
.category("Web")
.status("Active")
.auto_healing(true)
.build()
.unwrap()
}
// Fluent methods for chaining
pub fn with_stats(mut self, deployments: i32, rating: f32, monthly_revenue_usd: i32) -> Self {
self.deployments = deployments;
self.rating = rating;
self.monthly_revenue_usd = rust_decimal::Decimal::from(monthly_revenue_usd);
self
}
pub fn with_auto_healing(mut self, enabled: bool) -> Self {
self.auto_healing = Some(enabled);
self
}
pub fn with_version(mut self, version: impl Into<String>) -> Self {
self.version = version.into();
self
}
pub fn with_last_updated(mut self, updated: chrono::DateTime<chrono::Utc>) -> Self {
self.last_updated = updated;
self
}
}
#[derive(Default)]
pub struct DeploymentStatBuilder {
app_name: Option<String>,
region: Option<String>,
instances: Option<i32>,
status: Option<String>,
resource_usage: Option<ResourceUtilization>,
customer_name: Option<String>,
deployed_date: Option<String>,
deployment_id: Option<String>,
auto_healing: Option<bool>,
active_instances: Option<i32>,
total_instances: Option<i32>,
avg_response_time_ms: Option<f32>,
uptime_percentage: Option<f32>,
last_deployment: Option<chrono::DateTime<chrono::Utc>>,
}
impl DeploymentStatBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn app_name(mut self, name: impl Into<String>) -> Self {
self.app_name = Some(name.into());
self
}
pub fn region(mut self, region: impl Into<String>) -> Self {
self.region = Some(region.into());
self
}
pub fn instances(mut self, instances: i32) -> Self {
self.instances = Some(instances);
self
}
pub fn status(mut self, status: impl Into<String>) -> Self {
self.status = Some(status.into());
self
}
pub fn resource_usage(mut self, usage: ResourceUtilization) -> Self {
self.resource_usage = Some(usage);
self
}
pub fn customer_name(mut self, name: impl Into<String>) -> Self {
self.customer_name = Some(name.into());
self
}
pub fn deployed_date(mut self, date: impl Into<String>) -> Self {
self.deployed_date = Some(date.into());
self
}
pub fn deployment_id(mut self, id: impl Into<String>) -> Self {
self.deployment_id = Some(id.into());
self
}
pub fn auto_healing(mut self, enabled: bool) -> Self {
self.auto_healing = Some(enabled);
self
}
pub fn build(self) -> Result<DeploymentStat, String> {
Ok(DeploymentStat {
app_name: self.app_name.ok_or("app_name is required")?,
region: self.region.ok_or("region is required")?,
instances: self.instances.unwrap_or(1),
status: self.status.unwrap_or_else(|| "Active".to_string()),
resource_usage: self.resource_usage
.and_then(|usage| serde_json::to_string(&usage).ok())
.or_else(|| {
let default_usage = ResourceUtilization {
cpu: 50,
memory: 50,
storage: 50,
network: 50,
};
serde_json::to_string(&default_usage).ok()
}),
customer_name: self.customer_name,
deployed_date: self.deployed_date.map(|_| chrono::Utc::now()),
deployment_id: self.deployment_id,
active_instances: self.active_instances.unwrap_or(1),
total_instances: self.total_instances.unwrap_or(1),
avg_response_time_ms: self.avg_response_time_ms,
uptime_percentage: self.uptime_percentage,
last_deployment: self.last_deployment,
auto_healing: self.auto_healing.or(Some(true)),
})
}
}
impl DeploymentStat {
pub fn builder() -> DeploymentStatBuilder {
DeploymentStatBuilder::new()
}
}
#[derive(Default)]
pub struct UserBuilder {
id: Option<i32>,
name: Option<String>,
email: Option<String>,
role: Option<UserRole>,
country: Option<String>,
timezone: Option<String>,
created_at: Option<DateTime<Utc>>,
updated_at: Option<DateTime<Utc>>,
}
impl UserBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: i32) -> Self {
self.id = Some(id);
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn email(mut self, email: impl Into<String>) -> Self {
self.email = Some(email.into());
self
}
pub fn role(mut self, role: UserRole) -> Self {
self.role = Some(role);
self
}
pub fn country(mut self, country: impl Into<String>) -> Self {
self.country = Some(country.into());
self
}
pub fn timezone(mut self, timezone: impl Into<String>) -> Self {
self.timezone = Some(timezone.into());
self
}
pub fn build(self) -> Result<User, String> {
let now = Utc::now();
Ok(User {
id: self.id,
name: self.name.ok_or("name is required")?,
email: self.email.ok_or("email is required")?,
role: self.role.unwrap_or(UserRole::User),
country: self.country,
timezone: self.timezone,
created_at: self.created_at.or(Some(now)),
updated_at: self.updated_at.or(Some(now)),
})
}
}
impl User {
pub fn builder() -> UserBuilder {
UserBuilder::new()
}
// Template methods for common user types
pub fn new_user_template(name: impl Into<String>, email: impl Into<String>) -> Self {
Self::builder()
.name(name)
.email(email)
.role(UserRole::User)
.build()
.unwrap()
}
pub fn admin_template(name: impl Into<String>, email: impl Into<String>) -> Self {
Self::builder()
.name(name)
.email(email)
.role(UserRole::Admin)
.build()
.unwrap()
}
}
#[derive(Default)]
pub struct AppDeploymentBuilder {
id: Option<String>,
app_id: Option<String>,
app_name: Option<String>,
customer_name: Option<String>,
customer_email: Option<String>,
deployed_date: Option<String>,
status: Option<String>,
health_score: Option<f32>,
region: Option<String>,
instances: Option<i32>,
resource_usage: Option<ResourceUtilization>,
monthly_revenue: Option<i32>,
last_updated: Option<String>,
auto_healing: Option<bool>,
}
impl AppDeploymentBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn app_id(mut self, app_id: impl Into<String>) -> Self {
self.app_id = Some(app_id.into());
self
}
pub fn app_name(mut self, name: impl Into<String>) -> Self {
self.app_name = Some(name.into());
self
}
pub fn customer_name(mut self, name: impl Into<String>) -> Self {
self.customer_name = Some(name.into());
self
}
pub fn customer_email(mut self, email: impl Into<String>) -> Self {
self.customer_email = Some(email.into());
self
}
pub fn deployed_date(mut self, date: impl Into<String>) -> Self {
self.deployed_date = Some(date.into());
self
}
pub fn status(mut self, status: impl Into<String>) -> Self {
self.status = Some(status.into());
self
}
pub fn health_score(mut self, score: f32) -> Self {
self.health_score = Some(score);
self
}
pub fn region(mut self, region: impl Into<String>) -> Self {
self.region = Some(region.into());
self
}
pub fn instances(mut self, instances: i32) -> Self {
self.instances = Some(instances);
self
}
pub fn resource_usage(mut self, usage: ResourceUtilization) -> Self {
self.resource_usage = Some(usage);
self
}
pub fn monthly_revenue_usd(mut self, revenue: i32) -> Self {
self.monthly_revenue = Some(revenue);
self
}
pub fn last_updated(mut self, updated: impl Into<String>) -> Self {
self.last_updated = Some(updated.into());
self
}
pub fn auto_healing(mut self, enabled: bool) -> Self {
self.auto_healing = Some(enabled);
self
}
pub fn build(self) -> Result<AppDeployment, String> {
Ok(AppDeployment {
id: self.id.ok_or("id is required")?,
app_id: self.app_id.ok_or("app_id is required")?,
app_name: self.app_name.ok_or("app_name is required")?,
customer_name: self.customer_name.ok_or("customer_name is required")?,
customer_email: self.customer_email.ok_or("customer_email is required")?,
deployed_date: self.deployed_date.unwrap_or_else(|| Utc::now().format("%Y-%m-%d").to_string()),
created_at: Utc::now().format("%Y-%m-%d").to_string(),
status: self.status.unwrap_or_else(|| "Active".to_string()),
health_score: self.health_score.unwrap_or(98.0),
region: self.region.unwrap_or_else(|| "Global".to_string()),
instances: self.instances.unwrap_or(1),
resource_usage: self.resource_usage.unwrap_or_else(|| ResourceUtilization {
cpu: 25,
memory: 30,
storage: 15,
network: 10,
}),
monthly_revenue: self.monthly_revenue.unwrap_or(0),
last_updated: self.last_updated.unwrap_or_else(|| Utc::now().format("%Y-%m-%d %H:%M:%S").to_string()),
auto_healing: self.auto_healing.or(Some(true)),
})
}
}
impl AppDeployment {
pub fn builder() -> AppDeploymentBuilder {
AppDeploymentBuilder::new()
}
}
// =============================================================================
// PRODUCT MODEL BUILDERS
// =============================================================================
#[derive(Default)]
pub struct ProductBuilder {
id: Option<String>,
name: Option<String>,
category_id: Option<String>,
description: Option<String>,
base_price: Option<Decimal>,
base_currency: Option<String>,
attributes: HashMap<String, ProductAttribute>,
provider_id: Option<String>,
provider_name: Option<String>,
availability: Option<ProductAvailability>,
metadata: Option<ProductMetadata>,
created_at: Option<DateTime<Utc>>,
updated_at: Option<DateTime<Utc>>,
}
impl ProductBuilder {
pub fn new() -> Self {
Self::default()
}
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 category_id(mut self, category_id: impl Into<String>) -> Self {
self.category_id = Some(category_id.into());
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn base_price(mut self, price: Decimal) -> Self {
self.base_price = Some(price);
self
}
pub fn base_currency(mut self, currency: impl Into<String>) -> Self {
self.base_currency = Some(currency.into());
self
}
pub fn add_attribute(mut self, key: impl Into<String>, attribute: ProductAttribute) -> Self {
self.attributes.insert(key.into(), attribute);
self
}
pub fn provider_id(mut self, provider_id: impl Into<String>) -> Self {
self.provider_id = Some(provider_id.into());
self
}
pub fn provider_name(mut self, provider_name: impl Into<String>) -> Self {
self.provider_name = Some(provider_name.into());
self
}
pub fn availability(mut self, availability: ProductAvailability) -> Self {
self.availability = Some(availability);
self
}
pub fn metadata(mut self, metadata: ProductMetadata) -> Self {
self.metadata = Some(metadata);
self
}
pub fn build(self) -> Result<Product, String> {
let now = Utc::now();
Ok(Product {
id: self.id.ok_or("id is required")?,
name: self.name.ok_or("name is required")?,
category_id: self.category_id.ok_or("category_id is required")?,
description: self.description.unwrap_or_default(),
base_price: self.base_price.ok_or("base_price is required")?,
base_currency: self.base_currency.unwrap_or_else(|| "USD".to_string()),
attributes: self.attributes,
provider_id: self.provider_id.ok_or("provider_id is required")?,
provider_name: self.provider_name.ok_or("provider_name is required")?,
availability: self.availability.unwrap_or_default(),
metadata: self.metadata.unwrap_or_default(),
created_at: self.created_at.unwrap_or(now),
updated_at: self.updated_at.unwrap_or(now),
})
}
}
impl Product {
pub fn builder() -> ProductBuilder {
ProductBuilder::new()
}
}
// =============================================================================
// ORDER MODEL BUILDERS
// =============================================================================
#[derive(Default)]
pub struct OrderBuilder {
id: Option<String>,
user_id: Option<String>,
items: Vec<OrderItem>,
subtotal_base: Option<Decimal>,
total_base: Option<Decimal>,
base_currency: Option<String>,
currency_used: Option<String>,
currency_total: Option<Decimal>,
conversion_rate: Option<Decimal>,
status: Option<OrderStatus>,
payment_method: Option<String>,
payment_details: Option<PaymentDetails>,
billing_address: Option<Address>,
shipping_address: Option<Address>,
notes: Option<String>,
purchase_type: Option<PurchaseType>,
created_at: Option<DateTime<Utc>>,
updated_at: Option<DateTime<Utc>>,
}
impl OrderBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn user_id(mut self, user_id: impl Into<String>) -> Self {
self.user_id = Some(user_id.into());
self
}
pub fn add_item(mut self, item: OrderItem) -> Self {
self.items.push(item);
self
}
pub fn items(mut self, items: Vec<OrderItem>) -> Self {
self.items = items;
self
}
pub fn subtotal_base(mut self, subtotal: Decimal) -> Self {
self.subtotal_base = Some(subtotal);
self
}
pub fn total_base(mut self, total: Decimal) -> Self {
self.total_base = Some(total);
self
}
pub fn base_currency(mut self, currency: impl Into<String>) -> Self {
self.base_currency = Some(currency.into());
self
}
pub fn currency_used(mut self, currency: impl Into<String>) -> Self {
self.currency_used = Some(currency.into());
self
}
pub fn currency_total(mut self, total: Decimal) -> Self {
self.currency_total = Some(total);
self
}
pub fn conversion_rate(mut self, rate: Decimal) -> Self {
self.conversion_rate = Some(rate);
self
}
pub fn status(mut self, status: OrderStatus) -> Self {
self.status = Some(status);
self
}
pub fn payment_method(mut self, method: impl Into<String>) -> Self {
self.payment_method = Some(method.into());
self
}
pub fn payment_details(mut self, details: PaymentDetails) -> Self {
self.payment_details = Some(details);
self
}
pub fn billing_address(mut self, address: Address) -> Self {
self.billing_address = Some(address);
self
}
pub fn shipping_address(mut self, address: Address) -> Self {
self.shipping_address = Some(address);
self
}
pub fn notes(mut self, notes: impl Into<String>) -> Self {
self.notes = Some(notes.into());
self
}
pub fn purchase_type(mut self, purchase_type: PurchaseType) -> Self {
self.purchase_type = Some(purchase_type);
self
}
pub fn build(self) -> Result<Order, String> {
let now = Utc::now();
let subtotal = self.subtotal_base.unwrap_or_else(|| {
self.items.iter().map(|item| item.total_price_base).sum()
});
Ok(Order {
id: self.id.ok_or("id is required")?,
user_id: self.user_id.ok_or("user_id is required")?,
items: self.items,
subtotal_base: subtotal,
total_base: self.total_base.unwrap_or(subtotal),
base_currency: self.base_currency.unwrap_or_else(|| "USD".to_string()),
currency_used: self.currency_used.unwrap_or_else(|| "USD".to_string()),
currency_total: self.currency_total.unwrap_or(subtotal),
conversion_rate: self.conversion_rate.unwrap_or_else(|| Decimal::from(1)),
status: self.status.unwrap_or(OrderStatus::Pending),
payment_method: self.payment_method.unwrap_or_else(|| "credit_card".to_string()),
payment_details: self.payment_details,
billing_address: self.billing_address,
shipping_address: self.shipping_address,
notes: self.notes,
purchase_type: self.purchase_type.unwrap_or(PurchaseType::Cart),
created_at: self.created_at.unwrap_or(now),
updated_at: self.updated_at.unwrap_or(now),
})
}
}
impl Order {
pub fn builder() -> OrderBuilder {
OrderBuilder::new()
}
}
#[derive(Default)]
pub struct OrderItemBuilder {
product_id: Option<String>,
product_name: Option<String>,
product_category: Option<String>,
quantity: Option<u32>,
unit_price_base: Option<Decimal>,
total_price_base: Option<Decimal>,
specifications: HashMap<String, Value>,
provider_id: Option<String>,
provider_name: Option<String>,
}
impl OrderItemBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn product_id(mut self, id: impl Into<String>) -> Self {
self.product_id = Some(id.into());
self
}
pub fn product_name(mut self, name: impl Into<String>) -> Self {
self.product_name = Some(name.into());
self
}
pub fn product_category(mut self, category: impl Into<String>) -> Self {
self.product_category = Some(category.into());
self
}
pub fn quantity(mut self, quantity: u32) -> Self {
self.quantity = Some(quantity);
self
}
pub fn unit_price_base(mut self, price: Decimal) -> Self {
self.unit_price_base = Some(price);
self
}
pub fn add_specification(mut self, key: impl Into<String>, value: Value) -> Self {
self.specifications.insert(key.into(), value);
self
}
pub fn provider_id(mut self, id: impl Into<String>) -> Self {
self.provider_id = Some(id.into());
self
}
pub fn provider_name(mut self, name: impl Into<String>) -> Self {
self.provider_name = Some(name.into());
self
}
pub fn build(self) -> Result<OrderItem, String> {
let quantity = self.quantity.unwrap_or(1);
let unit_price = self.unit_price_base.ok_or("unit_price_base is required")?;
let total_price = self.total_price_base.unwrap_or(unit_price * Decimal::from(quantity));
Ok(OrderItem {
product_id: self.product_id.ok_or("product_id is required")?,
product_name: self.product_name.ok_or("product_name is required")?,
product_category: self.product_category.ok_or("product_category is required")?,
quantity,
unit_price_base: unit_price,
total_price_base: total_price,
specifications: self.specifications,
provider_id: self.provider_id.ok_or("provider_id is required")?,
provider_name: self.provider_name.ok_or("provider_name is required")?,
})
}
}
impl OrderItem {
pub fn builder() -> OrderItemBuilder {
OrderItemBuilder::new()
}
}
// =============================================================================
// SERVICE LAYER BUILDERS
// =============================================================================
#[derive(Default)]
pub struct CurrencyServiceBuilder {
cache_duration_minutes: Option<u64>,
base_currency: Option<String>,
display_currency: Option<String>,
auto_update: Option<bool>,
fallback_rates: Option<HashMap<String, Decimal>>,
}
impl CurrencyServiceBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn cache_duration(mut self, minutes: u64) -> Self {
self.cache_duration_minutes = Some(minutes);
self
}
pub fn base_currency(mut self, currency: impl Into<String>) -> Self {
self.base_currency = Some(currency.into());
self
}
pub fn auto_update(mut self, enabled: bool) -> Self {
self.auto_update = Some(enabled);
self
}
pub fn fallback_rates(mut self, rates: HashMap<String, Decimal>) -> Self {
self.fallback_rates = Some(rates);
self
}
pub fn display_currency(mut self, currency: impl Into<String>) -> Self {
self.display_currency = Some(currency.into());
self
}
pub fn build(self) -> Result<crate::services::currency::CurrencyService, String> {
let cache_duration = self.cache_duration_minutes.unwrap_or(60);
if cache_duration == 0 {
return Err("Cache duration must be greater than 0".to_string());
}
let base_currency = self.base_currency.unwrap_or_else(|| "USD".to_string());
if base_currency.is_empty() {
return Err("Base currency cannot be empty".to_string());
}
let display_currency = self.display_currency.unwrap_or_else(|| "USD".to_string());
Ok(crate::services::currency::CurrencyService::new_with_display_config(
cache_duration,
base_currency,
display_currency,
self.auto_update.unwrap_or(true),
self.fallback_rates.unwrap_or_default(),
))
}
}
#[derive(Default)]
pub struct ProductServiceBuilder {
currency_service: Option<crate::services::currency::CurrencyService>,
cache_enabled: Option<bool>,
default_category: Option<String>,
include_slice_products: Option<bool>,
}
impl ProductServiceBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn currency_service(mut self, service: crate::services::currency::CurrencyService) -> Self {
self.currency_service = Some(service);
self
}
pub fn cache_enabled(mut self, enabled: bool) -> Self {
self.cache_enabled = Some(enabled);
self
}
pub fn default_category(mut self, category: impl Into<String>) -> Self {
self.default_category = Some(category.into());
self
}
pub fn include_slice_products(mut self, include: bool) -> Self {
self.include_slice_products = Some(include);
self
}
pub fn build(self) -> Result<crate::services::product::ProductService, String> {
let currency_service = self.currency_service.unwrap_or_else(|| {
CurrencyServiceBuilder::new()
.build()
.expect("Failed to create default currency service")
});
if self.include_slice_products.unwrap_or(true) {
Ok(crate::services::product::ProductService::new_with_slice_support(
currency_service,
true,
))
} else {
Ok(crate::services::product::ProductService::new_with_config(
currency_service,
self.cache_enabled.unwrap_or(true),
self.default_category,
))
}
}
}
#[derive(Default)]
pub struct OrderServiceBuilder {
currency_service: Option<crate::services::currency::CurrencyService>,
product_service: Option<crate::services::product::ProductService>,
auto_save: Option<bool>,
}
impl OrderServiceBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn currency_service(mut self, service: crate::services::currency::CurrencyService) -> Self {
self.currency_service = Some(service);
self
}
pub fn product_service(mut self, service: crate::services::product::ProductService) -> Self {
self.product_service = Some(service);
self
}
pub fn auto_save(mut self, enabled: bool) -> Self {
self.auto_save = Some(enabled);
self
}
pub fn build(self) -> Result<crate::services::order::OrderService, String> {
let currency_service = self.currency_service.unwrap_or_else(|| {
CurrencyServiceBuilder::new()
.build()
.expect("Failed to create default currency service")
});
let product_service = self.product_service.unwrap_or_else(|| {
ProductServiceBuilder::new()
.currency_service(currency_service.clone())
.build()
.expect("Failed to create default product service")
});
Ok(crate::services::order::OrderService::new_with_config(
currency_service,
product_service,
self.auto_save.unwrap_or(true),
))
}
}
#[derive(Default)]
pub struct PoolServiceBuilder {
initial_pools: Option<HashMap<String, crate::models::pool::LiquidityPool>>,
analytics_enabled: Option<bool>,
}
impl PoolServiceBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn initial_pools(mut self, pools: HashMap<String, crate::models::pool::LiquidityPool>) -> Self {
self.initial_pools = Some(pools);
self
}
pub fn analytics_enabled(mut self, enabled: bool) -> Self {
self.analytics_enabled = Some(enabled);
self
}
pub fn build(self) -> Result<crate::services::pool_service::PoolService, String> {
Ok(crate::services::pool_service::PoolService::new_with_config(
self.initial_pools.unwrap_or_default(),
self.analytics_enabled.unwrap_or(true),
))
}
}
// =============================================================================
// CONTEXT BUILDER FOR TEMPLATE RENDERING
// =============================================================================
#[derive(Default)]
pub struct ContextBuilder {
active_page: Option<String>,
active_section: Option<String>,
gitea_enabled: Option<bool>,
enable_mock_data: Option<bool>,
user: Option<crate::models::user::User>,
user_json: Option<String>,
custom_data: HashMap<String, serde_json::Value>,
}
impl ContextBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn active_page(mut self, page: impl Into<String>) -> Self {
self.active_page = Some(page.into());
self
}
pub fn active_section(mut self, section: impl Into<String>) -> Self {
self.active_section = Some(section.into());
self
}
pub fn gitea_enabled(mut self, enabled: bool) -> Self {
self.gitea_enabled = Some(enabled);
self
}
/// Controls whether mock data/UI should be available in templates
pub fn enable_mock_data(mut self, enabled: bool) -> Self {
self.enable_mock_data = Some(enabled);
self
}
pub fn user(mut self, user: crate::models::user::User) -> Self {
self.user = Some(user);
self
}
pub fn user_json(mut self, json: impl Into<String>) -> Self {
self.user_json = Some(json.into());
self
}
pub fn custom(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
self.custom_data.insert(key.into(), value);
self
}
pub fn user_if_available(mut self, session: &actix_session::Session) -> Self {
if let Ok(Some(user_json)) = session.get::<String>("user") {
self.user_json = Some(user_json);
}
self
}
pub fn build(self) -> tera::Context {
let mut ctx = tera::Context::new();
if let Some(page) = self.active_page {
ctx.insert("active_page", &page);
}
if let Some(section) = self.active_section {
ctx.insert("active_section", &section);
}
if let Some(enabled) = self.gitea_enabled {
ctx.insert("gitea_enabled", &enabled);
}
// Insert enable_mock_data: prefer explicit value from builder,
// otherwise default from global app config
let mocks_enabled = match self.enable_mock_data {
Some(v) => v,
None => crate::config::get_app_config().enable_mock_data(),
};
ctx.insert("enable_mock_data", &mocks_enabled);
if let Some(user) = self.user {
ctx.insert("user", &user);
}
if let Some(user_json) = self.user_json {
ctx.insert("user_json", &user_json);
}
for (key, value) in self.custom_data {
ctx.insert(&key, &value);
}
ctx
}
}
// =============================================================================
// CONFIGURATION BUILDERS
// =============================================================================
#[derive(Default)]
pub struct SessionDataBuilder {
wallet_balance: Option<Decimal>,
transactions: Option<Vec<crate::models::user::Transaction>>,
staked_amount: Option<Decimal>,
pool_positions: Option<HashMap<String, crate::services::user_persistence::PoolPosition>>,
name: Option<String>,
country: Option<String>,
timezone: Option<String>,
}
impl SessionDataBuilder {
pub fn new() -> Self {
Self::default()
}
/// Create a new user with just email (most common case)
pub fn new_user(email: impl Into<String>) -> crate::services::user_persistence::UserPersistentData {
let mut data = Self::new().build();
data.user_email = email.into();
data
}
/// Create a new user with email and balance
pub fn new_user_with_balance(email: impl Into<String>, balance: Decimal) -> crate::services::user_persistence::UserPersistentData {
let mut data = Self::new().wallet_balance(balance).build();
data.user_email = email.into();
data
}
/// Load existing user or create new one (most common pattern)
pub fn load_or_create(email: impl Into<String>) -> crate::services::user_persistence::UserPersistentData {
let email_str = email.into();
crate::services::user_persistence::UserPersistence::load_user_data(&email_str)
.unwrap_or_else(|| Self::new_user(&email_str))
}
/// Create user with email field set (internal helper)
pub fn with_email(self, email: impl Into<String>) -> Self {
// We'll set email in build() method
self
}
pub fn wallet_balance(mut self, balance: Decimal) -> Self {
self.wallet_balance = Some(balance);
self
}
pub fn transactions(mut self, transactions: Vec<crate::models::user::Transaction>) -> Self {
self.transactions = Some(transactions);
self
}
pub fn staked_amount(mut self, amount: Decimal) -> Self {
self.staked_amount = Some(amount);
self
}
pub fn pool_positions(mut self, positions: HashMap<String, crate::services::user_persistence::PoolPosition>) -> Self {
self.pool_positions = Some(positions);
self
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn country(mut self, country: impl Into<String>) -> Self {
self.country = Some(country.into());
self
}
pub fn timezone(mut self, timezone: impl Into<String>) -> Self {
self.timezone = Some(timezone.into());
self
}
pub fn build(self) -> crate::services::user_persistence::UserPersistentData {
crate::services::user_persistence::UserPersistentData {
user_email: String::new(), // Will be set by caller
wallet_balance_usd: self.wallet_balance.unwrap_or_default(),
transactions: self.transactions.unwrap_or_default(),
staked_amount_usd: self.staked_amount.unwrap_or_default(),
pool_positions: self.pool_positions.unwrap_or_default(),
name: self.name,
country: self.country,
timezone: self.timezone,
display_currency: Some("USD".to_string()),
quick_topup_amounts: Some(vec![dec!(10), dec!(25), dec!(50), dec!(100)]),
..Default::default()
}
}
}
// MockDataBuilder removed - using persistent data only
// =============================================================================
// RESOURCE_PROVIDER DATA BUILDER
// =============================================================================
#[derive(Default)]
pub struct ResourceProviderDataBuilder {
total_nodes: Option<i32>,
online_nodes: Option<i32>,
total_capacity: Option<crate::models::user::NodeCapacity>,
used_capacity: Option<crate::models::user::NodeCapacity>,
monthly_earnings: Option<i32>,
total_earnings: Option<i32>,
uptime_percentage: Option<f32>,
nodes: Option<Vec<crate::models::user::FarmNode>>,
earnings_history: Option<Vec<crate::models::user::EarningsRecord>>,
active_slices: Option<i32>,
}
impl ResourceProviderDataBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn total_nodes(mut self, total_nodes: i32) -> Self {
self.total_nodes = Some(total_nodes);
self
}
pub fn online_nodes(mut self, online_nodes: i32) -> Self {
self.online_nodes = Some(online_nodes);
self
}
pub fn total_capacity(mut self, capacity: crate::models::user::NodeCapacity) -> Self {
self.total_capacity = Some(capacity);
self
}
pub fn used_capacity(mut self, capacity: crate::models::user::NodeCapacity) -> Self {
self.used_capacity = Some(capacity);
self
}
pub fn monthly_earnings_usd(mut self, earnings: i32) -> Self {
self.monthly_earnings = Some(earnings);
self
}
pub fn total_earnings_usd(mut self, earnings: i32) -> Self {
self.total_earnings = Some(earnings);
self
}
pub fn uptime_percentage(mut self, uptime: f32) -> Self {
self.uptime_percentage = Some(uptime);
self
}
pub fn nodes(mut self, nodes: Vec<crate::models::user::FarmNode>) -> Self {
self.nodes = Some(nodes);
self
}
pub fn earnings_history(mut self, history: Vec<crate::models::user::EarningsRecord>) -> Self {
self.earnings_history = Some(history);
self
}
pub fn earnings(mut self, earnings: Vec<crate::models::user::EarningsRecord>) -> Self {
self.earnings_history = Some(earnings);
self
}
pub fn active_slices(mut self, active_slices: i32) -> Self {
self.active_slices = Some(active_slices);
self
}
pub fn calculate_totals(mut self) -> Self {
// Calculate totals from existing data
if let Some(ref nodes) = self.nodes {
self.total_nodes = Some(nodes.len() as i32);
self.online_nodes = Some(nodes.iter().filter(|n| matches!(n.status, crate::models::user::NodeStatus::Online)).count() as i32);
// Calculate total and used capacity from all nodes
let mut total_capacity = crate::models::user::NodeCapacity {
cpu_cores: 0,
memory_gb: 0,
storage_gb: 0,
bandwidth_mbps: 0,
ssd_storage_gb: 0,
hdd_storage_gb: 0,
ram_gb: 0,
};
let mut used_capacity = crate::models::user::NodeCapacity {
cpu_cores: 0,
memory_gb: 0,
storage_gb: 0,
bandwidth_mbps: 0,
ssd_storage_gb: 0,
hdd_storage_gb: 0,
ram_gb: 0,
};
for node in nodes {
total_capacity.cpu_cores += node.capacity.cpu_cores;
total_capacity.memory_gb += node.capacity.memory_gb;
total_capacity.storage_gb += node.capacity.storage_gb;
total_capacity.bandwidth_mbps += node.capacity.bandwidth_mbps;
total_capacity.ssd_storage_gb += node.capacity.ssd_storage_gb;
total_capacity.hdd_storage_gb += node.capacity.hdd_storage_gb;
used_capacity.cpu_cores += node.used_capacity.cpu_cores;
used_capacity.memory_gb += node.used_capacity.memory_gb;
used_capacity.storage_gb += node.used_capacity.storage_gb;
used_capacity.bandwidth_mbps += node.used_capacity.bandwidth_mbps;
used_capacity.ssd_storage_gb += node.used_capacity.ssd_storage_gb;
used_capacity.hdd_storage_gb += node.used_capacity.hdd_storage_gb;
}
self.total_capacity = Some(total_capacity);
self.used_capacity = Some(used_capacity);
// Calculate uptime percentage
if !nodes.is_empty() {
let avg_uptime = nodes.iter().map(|n| n.uptime_percentage).sum::<f32>() / nodes.len() as f32;
self.uptime_percentage = Some(avg_uptime);
}
}
if let Some(ref earnings) = self.earnings_history {
let total: i32 = earnings.iter().map(|e| e.amount.to_string().parse::<i32>().unwrap_or(0)).sum();
self.total_earnings = Some(total);
self.monthly_earnings = Some(total); // Set monthly earnings as well
}
self
}
pub fn build(self) -> Result<crate::models::user::ResourceProviderData, String> {
Ok(crate::models::user::ResourceProviderData {
total_nodes: self.total_nodes.unwrap_or(0),
online_nodes: self.online_nodes.unwrap_or(0),
total_capacity: self.total_capacity.unwrap_or(crate::models::user::NodeCapacity {
cpu_cores: 0,
memory_gb: 0,
storage_gb: 0,
bandwidth_mbps: 0,
ssd_storage_gb: 0,
hdd_storage_gb: 0,
ram_gb: 0,
}),
used_capacity: self.used_capacity.unwrap_or(crate::models::user::NodeCapacity {
cpu_cores: 0,
memory_gb: 0,
storage_gb: 0,
bandwidth_mbps: 0,
ssd_storage_gb: 0,
hdd_storage_gb: 0,
ram_gb: 0,
}),
monthly_earnings_usd: rust_decimal::Decimal::from(self.monthly_earnings.unwrap_or(0)),
total_earnings_usd: rust_decimal::Decimal::from(self.total_earnings.unwrap_or(0)),
uptime_percentage: self.uptime_percentage.unwrap_or(0.0),
nodes: self.nodes.unwrap_or_default().into_iter().map(|node| crate::models::user::NodeInfo {
id: node.id,
name: node.name,
status: node.status,
location: node.location,
capacity: crate::models::user::NodeCapacity {
cpu_cores: 8,
memory_gb: 32,
storage_gb: 1500,
bandwidth_mbps: 1000,
ssd_storage_gb: 500,
hdd_storage_gb: 1000,
ram_gb: 32,
},
monthly_earnings_usd: 0,
cpu_total: 8,
memory_total_gb: 32,
ssd_storage_gb: 500,
hdd_storage_gb: 1000,
cpu_used: 2,
memory_used_gb: 8,
ssd_used_gb: 100,
hdd_used_gb: 200,
uptime_percentage: 99.5,
network_bandwidth_mbps: 1000,
farming_start_date: chrono::Utc::now(),
last_updated: chrono::Utc::now(),
utilization_7_day_avg: 45.0,
slice_formats_supported: vec!["small".to_string(), "medium".to_string()],
slice_last_calculated: Some(chrono::Utc::now()),
}).collect(),
earnings_history: self.earnings_history.unwrap_or_default(),
slice_templates: Vec::default(), // Will be populated separately
active_slices: self.active_slices.unwrap_or(0),
active_nodes: self.online_nodes.unwrap_or(0),
revenue_history: Vec::new(),
total_monthly_earnings_usd: self.monthly_earnings.unwrap_or(0),
})
}
}
// =============================================================================
// SLICE PRODUCT BUILDER
// =============================================================================
#[derive(Default)]
pub struct SliceProductBuilder {
resource_provider_id: Option<String>,
resource_provider_name: Option<String>,
slice_name: Option<String>,
cpu_cores: Option<i32>,
memory_gb: Option<i32>,
storage_gb: Option<i32>,
bandwidth_mbps: Option<i32>,
min_uptime_sla: Option<f32>,
public_ips: Option<i32>,
node_id: Option<String>,
slice_type: Option<crate::models::product::SliceType>,
price_per_hour: Option<rust_decimal::Decimal>,
}
impl SliceProductBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn resource_provider_id(mut self, resource_provider_id: impl Into<String>) -> Self {
self.resource_provider_id = Some(resource_provider_id.into());
self
}
pub fn resource_provider_name(mut self, resource_provider_name: impl Into<String>) -> Self {
self.resource_provider_name = Some(resource_provider_name.into());
self
}
pub fn slice_name(mut self, slice_name: impl Into<String>) -> Self {
self.slice_name = Some(slice_name.into());
self
}
pub fn cpu_cores(mut self, cpu_cores: i32) -> Self {
self.cpu_cores = Some(cpu_cores);
self
}
pub fn memory_gb(mut self, memory_gb: i32) -> Self {
self.memory_gb = Some(memory_gb);
self
}
pub fn storage_gb(mut self, storage_gb: i32) -> Self {
self.storage_gb = Some(storage_gb);
self
}
pub fn bandwidth_mbps(mut self, bandwidth_mbps: i32) -> Self {
self.bandwidth_mbps = Some(bandwidth_mbps);
self
}
pub fn min_uptime_sla(mut self, min_uptime_sla: f32) -> Self {
self.min_uptime_sla = Some(min_uptime_sla);
self
}
pub fn public_ips(mut self, public_ips: i32) -> Self {
self.public_ips = Some(public_ips);
self
}
pub fn node_id(mut self, node_id: impl Into<String>) -> Self {
self.node_id = Some(node_id.into());
self
}
pub fn slice_type(mut self, slice_type: crate::models::product::SliceType) -> Self {
self.slice_type = Some(slice_type);
self
}
pub fn price_per_hour(mut self, price_per_hour: rust_decimal::Decimal) -> Self {
self.price_per_hour = Some(price_per_hour);
self
}
pub fn build(self) -> Result<crate::models::product::Product, String> {
let resource_provider_id = self.resource_provider_id.ok_or("resource_provider_id is required")?;
let resource_provider_name = self.resource_provider_name.ok_or("resource_provider_name is required")?;
let slice_name = self.slice_name.ok_or("slice_name is required")?;
let cpu_cores = self.cpu_cores.ok_or("cpu_cores is required")?;
let memory_gb = self.memory_gb.ok_or("memory_gb is required")?;
let storage_gb = self.storage_gb.ok_or("storage_gb is required")?;
let bandwidth_mbps = self.bandwidth_mbps.ok_or("bandwidth_mbps is required")?;
let price_per_hour = self.price_per_hour.ok_or("price_per_hour is required")?;
let slice_config = crate::models::product::SliceConfiguration {
cpu_cores,
memory_gb,
storage_gb,
bandwidth_mbps,
min_uptime_sla: self.min_uptime_sla.unwrap_or(99.0),
public_ips: self.public_ips.unwrap_or(0),
node_id: self.node_id,
slice_type: self.slice_type.unwrap_or(crate::models::product::SliceType::Basic),
pricing: crate::models::product::SlicePricing::from_hourly(
price_per_hour,
5.0, // 5% daily discount
15.0, // 15% monthly discount
25.0 // 25% yearly discount
),
};
Ok(crate::models::product::Product::create_slice_product(
resource_provider_id,
resource_provider_name,
slice_name,
slice_config,
price_per_hour,
))
}
}
// =============================================================================
// FARM NODE BUILDER
// =============================================================================
#[derive(Default)]
pub struct FarmNodeBuilder {
id: Option<String>,
name: Option<String>,
location: Option<String>,
status: Option<crate::models::user::NodeStatus>,
capacity: Option<crate::models::user::NodeCapacity>,
used_capacity: Option<crate::models::user::NodeCapacity>,
uptime_percentage: Option<f32>,
farming_start_date: Option<chrono::DateTime<chrono::Utc>>,
last_updated: Option<chrono::DateTime<chrono::Utc>>,
utilization_7_day_avg: Option<f32>,
slice_formats_supported: Option<Vec<String>>,
earnings_today: Option<rust_decimal::Decimal>,
last_seen: Option<chrono::DateTime<chrono::Utc>>,
health_score: Option<f32>,
region: Option<String>,
node_type: Option<String>,
rental_options: Option<crate::models::user::NodeRentalOptions>,
availability_status: Option<crate::models::user::NodeAvailabilityStatus>,
grid_node_id: Option<u32>,
grid_data: Option<crate::models::user::GridNodeData>,
node_group_id: Option<String>,
group_assignment_date: Option<chrono::DateTime<chrono::Utc>>,
group_slice_format: Option<String>,
group_slice_price: Option<rust_decimal::Decimal>,
}
impl FarmNodeBuilder {
pub fn new() -> Self {
Self::default()
}
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 location(mut self, location: impl Into<String>) -> Self {
self.location = Some(location.into());
self
}
pub fn status(mut self, status: crate::models::user::NodeStatus) -> Self {
self.status = Some(status);
self
}
pub fn capacity(mut self, capacity: crate::models::user::NodeCapacity) -> Self {
self.capacity = Some(capacity);
self
}
pub fn used_capacity(mut self, capacity: crate::models::user::NodeCapacity) -> Self {
self.used_capacity = Some(capacity);
self
}
pub fn uptime_percentage(mut self, uptime: f32) -> Self {
self.uptime_percentage = Some(uptime);
self
}
pub fn earnings_today_usd(mut self, earnings: rust_decimal::Decimal) -> Self {
self.earnings_today = Some(earnings);
self
}
pub fn grid_node_id(mut self, grid_node_id: u32) -> Self {
self.grid_node_id = Some(grid_node_id);
self
}
pub fn grid_data(mut self, grid_data: crate::models::user::GridNodeData) -> Self {
self.grid_data = Some(grid_data);
self
}
pub fn node_group_id(mut self, group_id: impl Into<String>) -> Self {
self.node_group_id = Some(group_id.into());
self
}
pub fn group_assignment_date(mut self, date: chrono::DateTime<chrono::Utc>) -> Self {
self.group_assignment_date = Some(date);
self
}
pub fn group_slice_format(mut self, format: impl Into<String>) -> Self {
self.group_slice_format = Some(format.into());
self
}
pub fn group_slice_price(mut self, price: rust_decimal::Decimal) -> Self {
self.group_slice_price = Some(price);
self
}
pub fn last_seen(mut self, last_seen: chrono::DateTime<chrono::Utc>) -> Self {
self.last_seen = Some(last_seen);
self
}
pub fn health_score(mut self, score: f32) -> Self {
self.health_score = Some(score);
self
}
pub fn region(mut self, region: impl Into<String>) -> Self {
self.region = Some(region.into());
self
}
pub fn node_type(mut self, node_type: impl Into<String>) -> Self {
self.node_type = Some(node_type.into());
self
}
pub fn rental_options(mut self, options: crate::models::user::NodeRentalOptions) -> Self {
self.rental_options = Some(options);
self
}
pub fn availability_status(mut self, status: crate::models::user::NodeAvailabilityStatus) -> Self {
self.availability_status = Some(status);
self
}
pub fn build(self) -> Result<crate::models::user::FarmNode, String> {
Ok(crate::models::user::FarmNode {
id: self.id.ok_or("id is required")?,
name: self.name.unwrap_or_else(|| "Unnamed Node".to_string()),
location: self.location.unwrap_or_else(|| "Unknown".to_string()),
status: self.status.unwrap_or(crate::models::user::NodeStatus::Offline),
capacity: self.capacity.unwrap_or(crate::models::user::NodeCapacity {
cpu_cores: 0,
memory_gb: 0,
storage_gb: 0,
bandwidth_mbps: 0,
ssd_storage_gb: 0,
hdd_storage_gb: 0,
ram_gb: 0,
}),
used_capacity: self.used_capacity.unwrap_or(crate::models::user::NodeCapacity {
cpu_cores: 0,
memory_gb: 0,
storage_gb: 0,
bandwidth_mbps: 0,
ssd_storage_gb: 0,
hdd_storage_gb: 0,
ram_gb: 0,
}),
uptime_percentage: self.uptime_percentage.unwrap_or(0.0),
farming_start_date: self.farming_start_date.unwrap_or_else(|| chrono::Utc::now() - chrono::Duration::days(30)),
last_updated: self.last_updated.unwrap_or_else(|| chrono::Utc::now()),
utilization_7_day_avg: self.utilization_7_day_avg.unwrap_or(65.0),
slice_formats_supported: self.slice_formats_supported.unwrap_or_else(|| vec!["1x1".to_string(), "2x2".to_string(), "4x4".to_string()]),
earnings_today_usd: self.earnings_today.unwrap_or_else(|| rust_decimal_macros::dec!(0)),
last_seen: Some(self.last_seen.unwrap_or_else(|| chrono::Utc::now())),
health_score: self.health_score.unwrap_or(0.0),
region: self.region.unwrap_or_else(|| "Unknown".to_string()),
node_type: self.node_type.unwrap_or_else(|| "MyceliumNode".to_string()),
slice_formats: None,
rental_options: self.rental_options.map(|ro| serde_json::to_value(&ro).unwrap_or_default()),
staking_options: None,
availability_status: self.availability_status.unwrap_or_default(),
grid_node_id: self.grid_node_id.map(|id| id.to_string()),
grid_data: self.grid_data.map(|gd| serde_json::to_value(&gd).unwrap_or_default()),
node_group_id: self.node_group_id,
group_assignment_date: self.group_assignment_date,
group_slice_format: self.group_slice_format,
group_slice_price: self.group_slice_price,
// NEW: Marketplace SLA field (None for builder)
marketplace_sla: None,
total_base_slices: 0,
allocated_base_slices: 0,
slice_allocations: Vec::new(),
available_combinations: Vec::new(),
slice_pricing: Some(serde_json::to_value(&crate::services::slice_calculator::SlicePricing::default()).unwrap_or_default()),
slice_last_calculated: None,
})
}
}
// =============================================================================
// NODE GROUP BUILDERS
// =============================================================================
#[derive(Default)]
pub struct NodeGroupBuilder {
id: Option<String>,
name: Option<String>,
description: Option<String>,
group_type: Option<crate::models::user::NodeGroupType>,
node_ids: Option<Vec<String>>,
group_config: Option<crate::models::user::NodeGroupConfig>,
created_at: Option<chrono::DateTime<chrono::Utc>>,
updated_at: Option<chrono::DateTime<chrono::Utc>>,
}
impl NodeGroupBuilder {
pub fn new() -> Self {
Self::default()
}
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 description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn group_type(mut self, group_type: crate::models::user::NodeGroupType) -> Self {
self.group_type = Some(group_type);
self
}
pub fn node_ids(mut self, node_ids: Vec<String>) -> Self {
self.node_ids = Some(node_ids);
self
}
pub fn group_config(mut self, config: crate::models::user::NodeGroupConfig) -> Self {
self.group_config = Some(config);
self
}
pub fn created_at(mut self, created_at: chrono::DateTime<chrono::Utc>) -> Self {
self.created_at = Some(created_at);
self
}
pub fn updated_at(mut self, updated_at: chrono::DateTime<chrono::Utc>) -> Self {
self.updated_at = Some(updated_at);
self
}
/// Helper method to create a default group
pub fn default_group(mut self, default_type: crate::models::user::DefaultGroupType) -> Self {
self.id = Some(default_type.get_id().to_string());
self.name = Some(default_type.get_name().to_string());
self.description = Some(default_type.get_description().to_string());
self.group_type = Some(crate::models::user::NodeGroupType::Default(default_type.get_id().to_string()));
self.group_config = Some(default_type.get_default_config());
self
}
/// Helper method to create a custom group
pub fn custom_group(mut self, id: impl Into<String>, name: impl Into<String>) -> Self {
self.id = Some(id.into());
self.name = Some(name.into());
self.group_type = Some(crate::models::user::NodeGroupType::Custom);
self.group_config = Some(crate::models::user::NodeGroupConfig::default());
self
}
pub fn build(self) -> Result<crate::models::user::NodeGroup, String> {
Ok(crate::models::user::NodeGroup {
id: self.id.ok_or("id is required")?,
name: self.name.ok_or("name is required")?,
description: self.description,
group_type: self.group_type.ok_or("group_type is required")?,
node_ids: self.node_ids.unwrap_or_default(),
group_config: self.group_config.unwrap_or_default(),
created_at: self.created_at.unwrap_or_else(|| chrono::Utc::now()),
updated_at: self.updated_at.unwrap_or_else(|| chrono::Utc::now()),
})
}
}
// =============================================================================
// GRID NODE DATA BUILDERS
// =============================================================================
#[derive(Default)]
pub struct GridNodeDataBuilder {
grid_node_id: Option<u32>,
city: Option<String>,
country: Option<String>,
farm_name: Option<String>,
farm_id: Option<u32>,
public_ips: Option<u32>,
total_resources: Option<crate::models::user::NodeCapacity>,
used_resources: Option<crate::models::user::NodeCapacity>,
certification_type: Option<String>,
farming_policy_id: Option<u32>,
last_updated: Option<chrono::DateTime<chrono::Utc>>,
}
impl GridNodeDataBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn grid_node_id(mut self, id: u32) -> Self {
self.grid_node_id = Some(id);
self
}
pub fn city(mut self, city: impl Into<String>) -> Self {
self.city = Some(city.into());
self
}
pub fn country(mut self, country: impl Into<String>) -> Self {
self.country = Some(country.into());
self
}
pub fn farm_name(mut self, farm_name: impl Into<String>) -> Self {
self.farm_name = Some(farm_name.into());
self
}
pub fn farm_id(mut self, farm_id: u32) -> Self {
self.farm_id = Some(farm_id);
self
}
pub fn public_ips(mut self, public_ips: u32) -> Self {
self.public_ips = Some(public_ips);
self
}
pub fn total_resources(mut self, resources: crate::models::user::NodeCapacity) -> Self {
self.total_resources = Some(resources);
self
}
pub fn used_resources(mut self, resources: crate::models::user::NodeCapacity) -> Self {
self.used_resources = Some(resources);
self
}
pub fn certification_type(mut self, cert_type: impl Into<String>) -> Self {
self.certification_type = Some(cert_type.into());
self
}
pub fn farming_policy_id(mut self, policy_id: u32) -> Self {
self.farming_policy_id = Some(policy_id);
self
}
pub fn last_updated(mut self, last_updated: chrono::DateTime<chrono::Utc>) -> Self {
self.last_updated = Some(last_updated);
self
}
pub fn build(self) -> Result<crate::models::user::GridNodeData, String> {
Ok(crate::models::user::GridNodeData {
node_id: self.grid_node_id.ok_or("grid_node_id is required")?,
capacity: self.total_resources.clone().unwrap_or(crate::models::user::NodeCapacity {
cpu_cores: 0,
memory_gb: 0,
storage_gb: 0,
bandwidth_mbps: 0,
ssd_storage_gb: 0,
hdd_storage_gb: 0,
ram_gb: 0,
}),
location: format!("{}, {}",
self.city.as_deref().unwrap_or("Unknown"),
self.country.as_deref().unwrap_or("Unknown")),
status: "Online".to_string(),
uptime: 99.5,
grid_node_id: self.grid_node_id.ok_or("grid_node_id is required")?,
city: self.city.unwrap_or_else(|| "Unknown".to_string()),
country: self.country.unwrap_or_else(|| "Unknown".to_string()),
farm_name: self.farm_name.unwrap_or_else(|| "Unknown".to_string()),
farm_id: self.farm_id.unwrap_or(0),
public_ips: self.public_ips.map(|ip_count| (0..ip_count).map(|i| format!("192.168.1.{}", 100 + i)).collect()).unwrap_or_default(),
total_resources: self.total_resources.clone().unwrap_or(crate::models::user::NodeCapacity {
cpu_cores: 0,
memory_gb: 0,
storage_gb: 0,
bandwidth_mbps: 0,
ssd_storage_gb: 0,
hdd_storage_gb: 0,
ram_gb: 0,
}),
used_resources: self.used_resources.unwrap_or(crate::models::user::NodeCapacity {
cpu_cores: 0,
memory_gb: 0,
storage_gb: 0,
bandwidth_mbps: 0,
ssd_storage_gb: 0,
hdd_storage_gb: 0,
ram_gb: 0,
}),
certification_type: self.certification_type.unwrap_or_else(|| "DIY".to_string()),
farming_policy_id: self.farming_policy_id.unwrap_or(0),
last_updated: self.last_updated.unwrap_or_else(|| chrono::Utc::now()),
})
}
}
// =============================================================================
// NODE RENTAL BUILDERS
// =============================================================================
#[derive(Default)]
pub struct NodeRentalBuilder {
id: Option<String>,
node_id: Option<String>,
renter_email: Option<String>,
rental_type: Option<crate::models::user::NodeRentalType>,
monthly_cost: Option<Decimal>,
start_date: Option<DateTime<Utc>>,
end_date: Option<DateTime<Utc>>,
status: Option<crate::models::user::NodeRentalStatus>,
auto_renewal: Option<bool>,
payment_method: Option<String>,
metadata: HashMap<String, Value>,
}
impl NodeRentalBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn node_id(mut self, node_id: impl Into<String>) -> Self {
self.node_id = Some(node_id.into());
self
}
pub fn renter_email(mut self, email: impl Into<String>) -> Self {
self.renter_email = Some(email.into());
self
}
pub fn rental_type(mut self, rental_type: crate::models::user::NodeRentalType) -> Self {
self.rental_type = Some(rental_type);
self
}
pub fn monthly_cost(mut self, cost: Decimal) -> Self {
self.monthly_cost = Some(cost);
self
}
pub fn start_date(mut self, date: DateTime<Utc>) -> Self {
self.start_date = Some(date);
self
}
pub fn end_date(mut self, date: DateTime<Utc>) -> Self {
self.end_date = Some(date);
self
}
pub fn status(mut self, status: crate::models::user::NodeRentalStatus) -> Self {
self.status = Some(status);
self
}
pub fn auto_renewal(mut self, enabled: bool) -> Self {
self.auto_renewal = Some(enabled);
self
}
pub fn payment_method(mut self, method: impl Into<String>) -> Self {
self.payment_method = Some(method.into());
self
}
pub fn metadata(mut self, key: impl Into<String>, value: Value) -> Self {
self.metadata.insert(key.into(), value);
self
}
pub fn build(self) -> Result<crate::models::user::NodeRental, String> {
let id = self.id.unwrap_or_else(|| format!("rental_{}", uuid::Uuid::new_v4()));
Ok(crate::models::user::NodeRental {
rental_id: id.clone(),
customer_email: self.renter_email.as_ref().ok_or("renter_email is required")?.clone(),
node_id: self.node_id.ok_or("node_id is required")?,
rental_type: self.rental_type.ok_or("rental_type is required")?,
monthly_cost: self.monthly_cost.ok_or("monthly_cost is required")?,
start_date: self.start_date.unwrap_or_else(Utc::now),
end_date: self.end_date.ok_or("end_date is required")?,
auto_renewal: self.auto_renewal.unwrap_or(false),
payment_status: crate::models::user::PaymentStatus::Pending,
sla_id: None,
custom_configuration: None,
metadata: self.metadata,
id,
status: self.status.unwrap_or(crate::models::user::NodeRentalStatus::Pending),
renter_email: self.renter_email.ok_or("renter_email is required")?,
payment_method: self.payment_method.unwrap_or_else(|| "USD".to_string()),
})
}
}
#[derive(Default)]
pub struct ResourceProviderRentalEarningBuilder {
id: Option<String>,
node_id: Option<String>,
rental_id: Option<String>,
renter_email: Option<String>,
amount: Option<Decimal>,
currency: Option<String>,
earning_date: Option<DateTime<Utc>>,
rental_type: Option<crate::models::user::NodeRentalType>,
payment_status: Option<crate::models::user::PaymentStatus>,
}
impl ResourceProviderRentalEarningBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn node_id(mut self, node_id: impl Into<String>) -> Self {
self.node_id = Some(node_id.into());
self
}
pub fn rental_id(mut self, rental_id: impl Into<String>) -> Self {
self.rental_id = Some(rental_id.into());
self
}
pub fn renter_email(mut self, email: impl Into<String>) -> Self {
self.renter_email = Some(email.into());
self
}
pub fn amount(mut self, amount: Decimal) -> Self {
self.amount = Some(amount);
self
}
pub fn currency(mut self, currency: impl Into<String>) -> Self {
self.currency = Some(currency.into());
self
}
pub fn earning_date(mut self, date: DateTime<Utc>) -> Self {
self.earning_date = Some(date);
self
}
pub fn rental_type(mut self, rental_type: crate::models::user::NodeRentalType) -> Self {
self.rental_type = Some(rental_type);
self
}
pub fn payment_status(mut self, status: crate::models::user::PaymentStatus) -> Self {
self.payment_status = Some(status);
self
}
pub fn build(self) -> Result<crate::models::user::ResourceProviderRentalEarning, String> {
let id = self.id.unwrap_or_else(|| format!("earning_{}", uuid::Uuid::new_v4()));
Ok(crate::models::user::ResourceProviderRentalEarning {
id,
node_id: self.node_id.ok_or("node_id is required")?,
rental_id: self.rental_id.ok_or("rental_id is required")?,
renter_email: self.renter_email.ok_or("renter_email is required")?,
amount: self.amount.ok_or("amount is required")?,
currency: self.currency.unwrap_or_else(|| "USD".to_string()),
earning_date: self.earning_date.unwrap_or_else(Utc::now),
date: self.earning_date.unwrap_or_else(Utc::now).to_string(),
rental_type: self.rental_type.ok_or("rental_type is required")?,
payment_status: self.payment_status.unwrap_or(crate::models::user::PaymentStatus::Pending),
})
}
}
// =============================================================================
// USER ACTIVITY BUILDER
// =============================================================================
#[derive(Default)]
pub struct UserActivityBuilder {
id: Option<String>,
user_email: Option<String>,
activity_type: Option<crate::models::user::ActivityType>,
description: Option<String>,
timestamp: Option<chrono::DateTime<chrono::Utc>>,
metadata: Option<serde_json::Value>,
category: Option<String>,
importance: Option<crate::models::user::ActivityImportance>,
ip_address: Option<String>,
user_agent: Option<String>,
session_id: Option<String>,
}
impl UserActivityBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn activity_type(mut self, activity_type: crate::models::user::ActivityType) -> Self {
self.activity_type = Some(activity_type);
self
}
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
pub fn timestamp(mut self, timestamp: chrono::DateTime<chrono::Utc>) -> Self {
self.timestamp = Some(timestamp);
self
}
pub fn metadata(mut self, metadata: std::collections::HashMap<String, serde_json::Value>) -> Self {
self.metadata = Some(serde_json::to_value(metadata).unwrap_or_default());
self
}
pub fn category(mut self, category: impl Into<String>) -> Self {
self.category = Some(category.into());
self
}
pub fn importance(mut self, importance: crate::models::user::ActivityImportance) -> Self {
self.importance = Some(importance);
self
}
pub fn build(self) -> Result<crate::models::user::UserActivity, String> {
Ok(crate::models::user::UserActivity {
id: self.id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
user_email: self.user_email.unwrap_or_else(|| "unknown@example.com".to_string()),
activity_type: self.activity_type.ok_or("activity_type is required")?,
description: self.description.unwrap_or_else(|| "No description".to_string()),
timestamp: self.timestamp.unwrap_or_else(|| chrono::Utc::now()),
metadata: self.metadata,
category: self.category.unwrap_or_else(|| "General".to_string()),
importance: self.importance.unwrap_or(crate::models::user::ActivityImportance::Medium),
ip_address: self.ip_address,
user_agent: self.user_agent,
session_id: self.session_id,
})
}
}
// =============================================================================
// USER DASHBOARD DATA BUILDER
// =============================================================================
#[derive(Default)]
pub struct UserDashboardDataBuilder {
user_info: Option<crate::models::user::UserInfo>,
activities: Option<Vec<crate::models::user::UserActivity>>,
statistics: Option<crate::models::user::UsageStatistics>,
services: Option<Vec<crate::models::user::Service>>,
active_deployments: Option<i32>,
wallet_summary: Option<crate::models::user::WalletSummary>,
recommendations: Option<Vec<crate::models::user::Recommendation>>,
quick_actions: Option<Vec<crate::models::user::QuickAction>>,
}
impl UserDashboardDataBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn user_info(mut self, user_info: crate::models::user::UserInfo) -> Self {
self.user_info = Some(user_info);
self
}
pub fn activities(mut self, activities: Vec<crate::models::user::UserActivity>) -> Self {
self.activities = Some(activities);
self
}
pub fn statistics(mut self, statistics: Option<crate::models::user::UsageStatistics>) -> Self {
self.statistics = statistics;
self
}
pub fn services(mut self, services: Vec<crate::models::user::Service>) -> Self {
self.services = Some(services);
self
}
pub fn active_deployments(mut self, deployments: i32) -> Self {
self.active_deployments = Some(deployments);
self
}
pub fn wallet_summary(mut self, wallet_summary: crate::models::user::WalletSummary) -> Self {
self.wallet_summary = Some(wallet_summary);
self
}
pub fn recommendations(mut self, recommendations: Vec<crate::models::user::Recommendation>) -> Self {
self.recommendations = Some(recommendations);
self
}
pub fn quick_actions(mut self, quick_actions: Vec<crate::models::user::QuickAction>) -> Self {
self.quick_actions = Some(quick_actions);
self
}
pub fn build(self) -> Result<std::collections::HashMap<String, serde_json::Value>, String> {
let mut dashboard_data = std::collections::HashMap::new();
if let Some(user_info) = self.user_info {
dashboard_data.insert("user_info".to_string(), serde_json::to_value(user_info).unwrap());
}
if let Some(activities) = self.activities {
dashboard_data.insert("activities".to_string(), serde_json::to_value(activities).unwrap());
}
if let Some(statistics) = self.statistics {
dashboard_data.insert("statistics".to_string(), serde_json::to_value(statistics).unwrap());
}
if let Some(services) = self.services {
dashboard_data.insert("services".to_string(), serde_json::to_value(services).unwrap());
}
if let Some(active_deployments) = self.active_deployments {
dashboard_data.insert("active_deployments".to_string(), serde_json::to_value(active_deployments).unwrap());
}
if let Some(wallet_summary) = self.wallet_summary {
dashboard_data.insert("wallet_summary".to_string(), serde_json::to_value(wallet_summary).unwrap());
}
if let Some(recommendations) = self.recommendations {
dashboard_data.insert("recommendations".to_string(), serde_json::to_value(recommendations).unwrap());
}
if let Some(quick_actions) = self.quick_actions {
dashboard_data.insert("quick_actions".to_string(), serde_json::to_value(quick_actions).unwrap());
}
Ok(dashboard_data)
}
}
// =============================================================================
// NODE MARKETPLACE SERVICE BUILDER
// =============================================================================
#[derive(Default)]
pub struct NodeMarketplaceServiceBuilder {
currency_service: Option<crate::services::currency::CurrencyService>,
include_offline_nodes: Option<bool>,
price_calculation_method: Option<String>,
cache_enabled: Option<bool>,
}
impl NodeMarketplaceServiceBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn currency_service(mut self, service: crate::services::currency::CurrencyService) -> Self {
self.currency_service = Some(service);
self
}
pub fn include_offline_nodes(mut self, include: bool) -> Self {
self.include_offline_nodes = Some(include);
self
}
pub fn price_calculation_method(mut self, method: impl Into<String>) -> Self {
self.price_calculation_method = Some(method.into());
self
}
pub fn cache_enabled(mut self, enabled: bool) -> Self {
self.cache_enabled = Some(enabled);
self
}
pub fn build(self) -> Result<crate::services::node_marketplace::NodeMarketplaceService, String> {
let currency_service = self.currency_service.unwrap_or_else(|| {
CurrencyServiceBuilder::new()
.build()
.expect("Failed to create default currency service")
});
Ok(crate::services::node_marketplace::NodeMarketplaceService::new_with_config(
currency_service,
self.include_offline_nodes.unwrap_or(true),
self.price_calculation_method.unwrap_or_else(|| "capacity_based".to_string()),
self.cache_enabled.unwrap_or(true),
))
}
}
// =============================================================================
// NODE CREATION DATA BUILDER
// =============================================================================
#[derive(Default)]
pub struct NodeCreationDataBuilder {
name: Option<String>,
location: Option<String>,
cpu_cores: Option<i32>,
memory_gb: Option<i32>,
storage_gb: Option<i32>,
bandwidth_mbps: Option<i32>,
region: Option<String>,
node_type: Option<String>,
slice_formats: Option<Vec<String>>,
rental_options: Option<crate::models::user::NodeRentalOptions>,
slice_prices: Option<std::collections::HashMap<String, rust_decimal::Decimal>>,
}
impl NodeCreationDataBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn location(mut self, location: impl Into<String>) -> Self {
self.location = Some(location.into());
self
}
pub fn cpu_cores(mut self, cores: i32) -> Self {
self.cpu_cores = Some(cores);
self
}
pub fn memory_gb(mut self, memory: i32) -> Self {
self.memory_gb = Some(memory);
self
}
pub fn storage_gb(mut self, storage: i32) -> Self {
self.storage_gb = Some(storage);
self
}
pub fn bandwidth_mbps(mut self, bandwidth: i32) -> Self {
self.bandwidth_mbps = Some(bandwidth);
self
}
pub fn region(mut self, region: impl Into<String>) -> Self {
self.region = Some(region.into());
self
}
pub fn node_type(mut self, node_type: impl Into<String>) -> Self {
self.node_type = Some(node_type.into());
self
}
pub fn slice_formats(mut self, formats: Vec<String>) -> Self {
self.slice_formats = Some(formats);
self
}
pub fn rental_options(mut self, options: crate::models::user::NodeRentalOptions) -> Self {
self.rental_options = Some(options);
self
}
pub fn slice_prices(mut self, prices: std::collections::HashMap<String, rust_decimal::Decimal>) -> Self {
self.slice_prices = Some(prices);
self
}
pub fn build(self) -> Result<crate::services::resource_provider::NodeCreationData, String> {
Ok(crate::services::resource_provider::NodeCreationData {
name: self.name.ok_or("name is required")?,
location: self.location.ok_or("location is required")?,
cpu_cores: self.cpu_cores.unwrap_or(4),
memory_gb: self.memory_gb.unwrap_or(8),
storage_gb: self.storage_gb.unwrap_or(100),
bandwidth_mbps: self.bandwidth_mbps.unwrap_or(100),
region: self.region,
node_type: self.node_type,
slice_formats: self.slice_formats,
rental_options: self.rental_options,
slice_prices: self.slice_prices,
slice_pricing: None,
})
}
}
// =============================================================================
// FULL NODE PRICING BUILDER
// =============================================================================
#[derive(Default)]
pub struct FullNodePricingBuilder {
hourly: Option<rust_decimal::Decimal>,
daily: Option<rust_decimal::Decimal>,
monthly: Option<rust_decimal::Decimal>,
yearly: Option<rust_decimal::Decimal>,
auto_calculate: Option<bool>,
daily_discount_percent: Option<f32>,
monthly_discount_percent: Option<f32>,
yearly_discount_percent: Option<f32>,
}
impl FullNodePricingBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn hourly(mut self, hourly: rust_decimal::Decimal) -> Self {
self.hourly = Some(hourly);
self
}
pub fn daily(mut self, daily: rust_decimal::Decimal) -> Self {
self.daily = Some(daily);
self
}
pub fn monthly(mut self, monthly: rust_decimal::Decimal) -> Self {
self.monthly = Some(monthly);
self
}
pub fn yearly(mut self, yearly: rust_decimal::Decimal) -> Self {
self.yearly = Some(yearly);
self
}
pub fn auto_calculate(mut self, auto_calculate: bool) -> Self {
self.auto_calculate = Some(auto_calculate);
self
}
pub fn daily_discount_percent(mut self, percent: f32) -> Self {
self.daily_discount_percent = Some(percent);
self
}
pub fn monthly_discount_percent(mut self, percent: f32) -> Self {
self.monthly_discount_percent = Some(percent);
self
}
pub fn yearly_discount_percent(mut self, percent: f32) -> Self {
self.yearly_discount_percent = Some(percent);
self
}
pub fn build(self) -> Result<crate::models::user::FullNodePricing, String> {
let mut pricing = crate::models::user::FullNodePricing {
hourly: self.hourly.unwrap_or_else(|| rust_decimal_macros::dec!(0)),
daily: self.daily.unwrap_or_else(|| rust_decimal_macros::dec!(0)),
monthly: self.monthly.unwrap_or_else(|| rust_decimal_macros::dec!(0)),
yearly: self.yearly.unwrap_or_else(|| rust_decimal_macros::dec!(0)),
auto_calculate: self.auto_calculate.unwrap_or(true),
daily_discount_percent: self.daily_discount_percent.unwrap_or(0.0),
monthly_discount_percent: self.monthly_discount_percent.unwrap_or(0.0),
yearly_discount_percent: self.yearly_discount_percent.unwrap_or(0.0),
monthly_cost: self.monthly.unwrap_or_else(|| rust_decimal_macros::dec!(0)),
setup_fee: None,
deposit_required: None,
};
// Auto-calculate rates if enabled and hourly rate is set
if pricing.auto_calculate && pricing.hourly > rust_decimal_macros::dec!(0) {
pricing.calculate_from_hourly();
}
Ok(pricing)
}
}
// =============================================================================
// NODE RENTAL OPTIONS BUILDER
// =============================================================================
#[derive(Default)]
pub struct NodeRentalOptionsBuilder {
slice_formats: Option<Vec<String>>,
pricing: Option<crate::models::user::FullNodePricing>,
slice_rental_enabled: Option<bool>,
full_node_rental_enabled: Option<bool>,
full_node_pricing: Option<crate::models::user::FullNodePricing>,
minimum_rental_days: Option<u32>,
maximum_rental_days: Option<u32>,
auto_renewal_enabled: Option<bool>,
}
impl NodeRentalOptionsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn slice_rental_enabled(mut self, enabled: bool) -> Self {
self.slice_rental_enabled = Some(enabled);
self
}
pub fn full_node_rental_enabled(mut self, enabled: bool) -> Self {
self.full_node_rental_enabled = Some(enabled);
self
}
pub fn full_node_pricing(mut self, pricing: crate::models::user::FullNodePricing) -> Self {
self.full_node_pricing = Some(pricing);
self
}
pub fn minimum_rental_days(mut self, days: u32) -> Self {
self.minimum_rental_days = Some(days);
self
}
pub fn maximum_rental_days(mut self, days: u32) -> Self {
self.maximum_rental_days = Some(days);
self
}
pub fn auto_renewal_enabled(mut self, enabled: bool) -> Self {
self.auto_renewal_enabled = Some(enabled);
self
}
pub fn build(self) -> Result<crate::models::user::NodeRentalOptions, String> {
Ok(crate::models::user::NodeRentalOptions {
full_node_available: self.full_node_rental_enabled.unwrap_or(false),
slice_formats: self.slice_formats.unwrap_or_else(|| vec!["1x1".to_string(), "2x2".to_string(), "4x4".to_string()]),
pricing: self.pricing.unwrap_or_else(|| crate::models::user::FullNodePricing {
monthly_cost: rust_decimal::Decimal::try_from(100.0).unwrap_or_default(),
setup_fee: Some(rust_decimal::Decimal::try_from(10.0).unwrap_or_default()),
deposit_required: Some(rust_decimal::Decimal::try_from(50.0).unwrap_or_default()),
hourly: rust_decimal::Decimal::try_from(4.17).unwrap_or_default(),
daily: rust_decimal::Decimal::try_from(3.33).unwrap_or_default(),
monthly: rust_decimal::Decimal::try_from(100.0).unwrap_or_default(),
yearly: rust_decimal::Decimal::try_from(1000.0).unwrap_or_default(),
daily_discount_percent: 20.0,
monthly_discount_percent: 10.0,
yearly_discount_percent: 17.0,
auto_calculate: true,
}),
slice_rental_enabled: self.slice_rental_enabled.unwrap_or(true),
full_node_rental_enabled: self.full_node_rental_enabled.unwrap_or(false),
full_node_pricing: self.full_node_pricing,
minimum_rental_days: self.minimum_rental_days.unwrap_or(30),
maximum_rental_days: self.maximum_rental_days,
auto_renewal_enabled: self.auto_renewal_enabled.unwrap_or(false),
})
}
}
// =============================================================================
// SERVICE AND SERVICE REQUEST BUILDERS
// =============================================================================
#[derive(Default)]
pub struct ServiceBuilder {
id: Option<String>,
name: Option<String>,
description: Option<String>,
category: Option<String>,
price_usd: Option<rust_decimal::Decimal>,
hourly_rate_usd: Option<rust_decimal::Decimal>,
availability: Option<bool>,
}
impl ServiceBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: &str) -> Self {
self.id = Some(id.to_string());
self
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_string());
self
}
pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_string());
self
}
pub fn category(mut self, category: &str) -> Self {
self.category = Some(category.to_string());
self
}
pub fn price_usd(mut self, price: f64) -> Self {
self.price_usd = Some(rust_decimal::Decimal::try_from(price).unwrap_or_default());
self
}
pub fn hourly_rate_usd(mut self, rate: Option<f64>) -> Self {
self.hourly_rate_usd = rate.map(|r| rust_decimal::Decimal::try_from(r).unwrap_or_default());
self
}
pub fn availability(mut self, available: bool) -> Self {
self.availability = Some(available);
self
}
pub fn build(self) -> Result<crate::models::user::Service, String> {
Ok(crate::models::user::Service {
id: self.id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
name: self.name.unwrap_or_default(),
description: self.description.unwrap_or_default(),
category: self.category.unwrap_or_default(),
price_usd: self.price_usd.unwrap_or_default(),
hourly_rate_usd: self.hourly_rate_usd,
availability: self.availability.unwrap_or(true),
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
clients: 0,
price_per_hour_usd: self.price_usd.unwrap_or_default().to_string().parse().unwrap_or(0),
status: "Active".to_string(),
rating: 0.0,
total_hours: None,
})
}
}
#[derive(Default)]
pub struct ServiceRequestBuilder {
id: Option<String>,
customer_email: Option<String>,
service_id: Option<String>,
description: Option<String>,
status: Option<String>,
estimated_hours: Option<i32>,
hourly_rate_usd: Option<rust_decimal::Decimal>,
total_cost_usd: Option<rust_decimal::Decimal>,
progress_percentage: Option<f32>,
created_date: Option<String>,
completed_date: Option<String>,
}
impl ServiceRequestBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: &str) -> Self {
self.id = Some(id.to_string());
self
}
pub fn customer_email(mut self, email: &str) -> Self {
self.customer_email = Some(email.to_string());
self
}
pub fn service_id(mut self, service_id: &str) -> Self {
self.service_id = Some(service_id.to_string());
self
}
pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_string());
self
}
pub fn status(mut self, status: &str) -> Self {
self.status = Some(status.to_string());
self
}
pub fn estimated_hours(mut self, hours: i32) -> Self {
self.estimated_hours = Some(hours);
self
}
pub fn hourly_rate_usd(mut self, rate: rust_decimal::Decimal) -> Self {
self.hourly_rate_usd = Some(rate);
self
}
pub fn total_cost_usd(mut self, cost: rust_decimal::Decimal) -> Self {
self.total_cost_usd = Some(cost);
self
}
pub fn progress_percentage(mut self, progress: f32) -> Self {
self.progress_percentage = Some(progress);
self
}
pub fn created_date(mut self, date: &str) -> Self {
self.created_date = Some(date.to_string());
self
}
pub fn completed_date(mut self, date: &str) -> Self {
self.completed_date = Some(date.to_string());
self
}
pub fn build(self) -> Result<crate::models::user::ServiceRequest, String> {
Ok(crate::models::user::ServiceRequest {
id: self.id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
customer_email: self.customer_email.unwrap_or_default(),
service_id: self.service_id.unwrap_or_default(),
description: self.description,
status: self.status.unwrap_or_else(|| "Pending".to_string()),
estimated_hours: self.estimated_hours,
hourly_rate_usd: self.hourly_rate_usd,
total_cost_usd: self.total_cost_usd,
progress_percentage: self.progress_percentage,
created_date: self.created_date,
completed_date: self.completed_date,
progress: None,
priority: "Medium".to_string(),
hours_worked: None,
notes: None,
service_name: "Service".to_string(),
budget: self.total_cost_usd.unwrap_or_default(),
requested_date: chrono::Utc::now().format("%Y-%m-%d").to_string(),
client_phone: None,
client_name: None,
client_email: None,
})
}
}
// =============================================================================
// NODE STAKING OPTIONS BUILDER
// =============================================================================
#[derive(Default)]
pub struct NodeStakingOptionsBuilder {
staking_enabled: Option<bool>,
minimum_stake: Option<Decimal>,
reward_rate: Option<f32>,
staked_amount: Option<Decimal>,
staking_start_date: Option<DateTime<Utc>>,
staking_period_months: Option<u32>,
early_withdrawal_allowed: Option<bool>,
early_withdrawal_penalty_percent: Option<f32>,
}
impl NodeStakingOptionsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn staking_enabled(mut self, enabled: bool) -> Self {
self.staking_enabled = Some(enabled);
self
}
pub fn minimum_stake(mut self, stake: Decimal) -> Self {
self.minimum_stake = Some(stake);
self
}
pub fn reward_rate(mut self, rate: f32) -> Self {
self.reward_rate = Some(rate);
self
}
pub fn staked_amount(mut self, amount: Decimal) -> Self {
self.staked_amount = Some(amount);
self
}
pub fn staking_start_date(mut self, date: DateTime<Utc>) -> Self {
self.staking_start_date = Some(date);
self
}
pub fn staking_period_months(mut self, months: u32) -> Self {
self.staking_period_months = Some(months);
self
}
pub fn early_withdrawal_allowed(mut self, allowed: bool) -> Self {
self.early_withdrawal_allowed = Some(allowed);
self
}
pub fn early_withdrawal_penalty_percent(mut self, percent: f32) -> Self {
self.early_withdrawal_penalty_percent = Some(percent);
self
}
pub fn build(self) -> Result<crate::models::user::NodeStakingOptions, String> {
Ok(crate::models::user::NodeStakingOptions {
enabled: self.staking_enabled.unwrap_or(false),
minimum_stake: self.minimum_stake.unwrap_or(Decimal::new(100, 0)),
reward_rate: self.reward_rate.unwrap_or(5.0),
staking_enabled: self.staking_enabled.unwrap_or(false),
staked_amount: self.staked_amount.unwrap_or(Decimal::ZERO),
staking_start_date: self.staking_start_date,
staking_period_months: self.staking_period_months.unwrap_or(12),
early_withdrawal_allowed: self.early_withdrawal_allowed.unwrap_or(true),
early_withdrawal_penalty_percent: self.early_withdrawal_penalty_percent.unwrap_or(25.0),
})
}
}
/// Builder for UserDeployment
#[derive(Default)]
pub struct UserDeploymentBuilder {
id: Option<String>,
app_name: Option<String>,
status: Option<crate::models::user::DeploymentStatus>,
cost_per_month: Option<rust_decimal::Decimal>,
deployed_at: Option<chrono::DateTime<chrono::Utc>>,
provider: Option<String>,
region: Option<String>,
resource_usage: Option<crate::models::user::ResourceUtilization>,
}
impl UserDeploymentBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: impl Into<String>) -> Self {
self.id = Some(id.into());
self
}
pub fn app_name(mut self, name: impl Into<String>) -> Self {
self.app_name = Some(name.into());
self
}
pub fn status(mut self, status: crate::models::user::DeploymentStatus) -> Self {
self.status = Some(status);
self
}
pub fn cost_per_month(mut self, cost: rust_decimal::Decimal) -> Self {
self.cost_per_month = Some(cost);
self
}
pub fn deployed_at(mut self, date: chrono::DateTime<chrono::Utc>) -> Self {
self.deployed_at = Some(date);
self
}
pub fn provider(mut self, provider: impl Into<String>) -> Self {
self.provider = Some(provider.into());
self
}
pub fn region(mut self, region: impl Into<String>) -> Self {
self.region = Some(region.into());
self
}
pub fn resource_usage(mut self, usage: crate::models::user::ResourceUtilization) -> Self {
self.resource_usage = Some(usage);
self
}
pub fn build(self) -> Result<crate::models::user::UserDeployment, String> {
Ok(crate::models::user::UserDeployment {
id: self.id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
app_name: self.app_name.ok_or("app_name is required")?,
status: self.status.unwrap_or_default(),
cost_per_month: self.cost_per_month.unwrap_or(rust_decimal::Decimal::ZERO),
deployed_at: self.deployed_at.unwrap_or_else(|| chrono::Utc::now()),
provider: self.provider.unwrap_or_else(|| "Unknown".to_string()),
region: self.region.unwrap_or_else(|| "Unknown".to_string()),
resource_usage: self.resource_usage.unwrap_or_default(),
})
}
}
// =============================================================================
// SERVICE BOOKING BUILDER
// =============================================================================
#[derive(Default)]
pub struct ServiceBookingBuilder {
id: Option<String>,
service_id: Option<String>,
service_name: Option<String>,
provider_email: Option<String>,
customer_email: Option<String>,
budget: Option<Decimal>,
estimated_hours: Option<i32>,
hourly_rate_usd: Option<Decimal>,
progress_percentage: Option<f32>,
status: Option<String>,
requested_date: Option<String>,
priority: Option<String>,
description: Option<String>,
booking_date: Option<String>,
}
impl ServiceBookingBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn id(mut self, id: &str) -> Self {
self.id = Some(id.to_string());
self
}
pub fn service_id(mut self, service_id: &str) -> Self {
self.service_id = Some(service_id.to_string());
self
}
pub fn service_name(mut self, service_name: &str) -> Self {
self.service_name = Some(service_name.to_string());
self
}
pub fn provider_email(mut self, provider_email: &str) -> Self {
self.provider_email = Some(provider_email.to_string());
self
}
pub fn customer_email(mut self, customer_email: &str) -> Self {
self.customer_email = Some(customer_email.to_string());
self
}
pub fn budget(mut self, budget: Decimal) -> Self {
self.budget = Some(budget);
self
}
pub fn estimated_hours(mut self, hours: i32) -> Self {
self.estimated_hours = Some(hours);
self
}
pub fn hourly_rate_usd(mut self, rate: Decimal) -> Self {
self.hourly_rate_usd = Some(rate);
self
}
pub fn progress_percentage(mut self, progress: f32) -> Self {
self.progress_percentage = Some(progress);
self
}
pub fn status(mut self, status: &str) -> Self {
self.status = Some(status.to_string());
self
}
pub fn requested_date(mut self, date: &str) -> Self {
self.requested_date = Some(date.to_string());
self
}
pub fn priority(mut self, priority: &str) -> Self {
self.priority = Some(priority.to_string());
self
}
pub fn description(mut self, description: Option<String>) -> Self {
self.description = description;
self
}
pub fn booking_date(mut self, date: &str) -> Self {
self.booking_date = Some(date.to_string());
self
}
pub fn build(self) -> Result<ServiceBooking, String> {
Ok(ServiceBooking {
id: self.id.ok_or("ID is required")?,
service_id: self.service_id.ok_or("Service ID is required")?,
service_name: self.service_name.ok_or("Service name is required")?,
provider_email: self.provider_email.ok_or("Provider email is required")?,
customer_email: self.customer_email.ok_or("Customer email is required")?,
budget: self.budget.unwrap_or(Decimal::ZERO),
estimated_hours: self.estimated_hours,
hourly_rate_usd: self.hourly_rate_usd,
total_cost_usd: None,
progress_percentage: self.progress_percentage,
created_date: Some(chrono::Utc::now().format("%Y-%m-%d").to_string()),
status: self.status.unwrap_or_else(|| "Pending".to_string()),
requested_date: self.requested_date.unwrap_or_else(|| chrono::Utc::now().format("%Y-%m-%d").to_string()),
priority: self.priority.unwrap_or_else(|| "Medium".to_string()),
description: self.description,
booking_date: self.booking_date.map(|date| chrono::DateTime::parse_from_rfc3339(&date).ok().map(|dt| dt.with_timezone(&chrono::Utc))).flatten(),
client_phone: None,
progress: None,
completed_date: None,
})
}
}
impl ServiceBooking {
pub fn builder() -> ServiceBookingBuilder {
ServiceBookingBuilder::new()
}
}
// =============================================================================
// CUSTOMER SERVICE DATA BUILDER
// =============================================================================
#[derive(Default)]
pub struct CustomerServiceDataBuilder {
active_bookings: Option<i32>,
completed_bookings: Option<i32>,
total_spent: Option<Decimal>,
monthly_spending: Option<Decimal>,
average_rating_given: Option<f32>,
service_bookings: Option<Vec<crate::models::user::ServiceBooking>>,
favorite_providers: Option<Vec<String>>,
spending_history: Option<Vec<crate::models::user::SpendingRecord>>,
}
impl CustomerServiceDataBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn active_bookings(mut self, count: i32) -> Self {
self.active_bookings = Some(count);
self
}
pub fn completed_bookings(mut self, count: i32) -> Self {
self.completed_bookings = Some(count);
self
}
pub fn total_spent(mut self, amount: Decimal) -> Self {
self.total_spent = Some(amount);
self
}
pub fn monthly_spending(mut self, amount: Decimal) -> Self {
self.monthly_spending = Some(amount);
self
}
pub fn average_rating_given(mut self, rating: f32) -> Self {
self.average_rating_given = Some(rating);
self
}
pub fn service_bookings(mut self, bookings: Vec<crate::models::user::ServiceBooking>) -> Self {
self.service_bookings = Some(bookings);
self
}
pub fn favorite_providers(mut self, providers: Vec<String>) -> Self {
self.favorite_providers = Some(providers);
self
}
pub fn spending_history(mut self, history: Vec<crate::models::user::SpendingRecord>) -> Self {
self.spending_history = Some(history);
self
}
pub fn build(self) -> Result<crate::models::user::CustomerServiceData, String> {
Ok(crate::models::user::CustomerServiceData {
active_bookings: self.active_bookings.unwrap_or(0),
completed_bookings: self.completed_bookings.unwrap_or(0),
total_spent: self.total_spent.unwrap_or(Decimal::ZERO),
monthly_spending: self.monthly_spending.unwrap_or(Decimal::ZERO),
average_rating_given: self.average_rating_given.unwrap_or(0.0),
service_bookings: self.service_bookings.unwrap_or_default(),
favorite_providers: self.favorite_providers.unwrap_or_default(),
spending_history: self.spending_history.unwrap_or_default(),
pending_transactions: 0,
total_spent_usd: self.total_spent.unwrap_or(Decimal::ZERO).to_string().parse::<i32>().unwrap_or(0),
})
}
}
impl crate::models::user::CustomerServiceData {
pub fn builder() -> CustomerServiceDataBuilder {
CustomerServiceDataBuilder::new()
}
}
#[derive(Default)]
pub struct SpendingRecordBuilder {
date: Option<String>,
amount: Option<f32>,
service_name: Option<String>,
provider_name: Option<String>,
}
impl SpendingRecordBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn date(mut self, date: &str) -> Self {
self.date = Some(date.to_string());
self
}
pub fn amount(mut self, amount: f32) -> Self {
self.amount = Some(amount);
self
}
pub fn service_name(mut self, name: &str) -> Self {
self.service_name = Some(name.to_string());
self
}
pub fn provider_name(mut self, name: &str) -> Self {
self.provider_name = Some(name.to_string());
self
}
pub fn build(self) -> Result<crate::models::user::SpendingRecord, String> {
Ok(crate::models::user::SpendingRecord {
date: self.date.ok_or("Date is required")?,
amount: self.amount.unwrap_or(0.0),
service_name: self.service_name.ok_or("Service name is required")?,
provider_name: self.provider_name.ok_or("Provider name is required")?,
})
}
}
impl crate::models::user::SpendingRecord {
pub fn builder() -> SpendingRecordBuilder {
SpendingRecordBuilder::new()
}
}
// =============================================================================
// AUTO TOP-UP BUILDERS
// =============================================================================
#[derive(Default)]
pub struct AutoTopUpSettingsBuilder {
enabled: Option<bool>,
threshold_amount: Option<Decimal>,
topup_amount: Option<Decimal>,
payment_method_id: Option<String>,
daily_limit: Option<Decimal>,
monthly_limit: Option<Decimal>,
}
impl AutoTopUpSettingsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = Some(enabled);
self
}
pub fn threshold_amount(mut self, amount: Decimal) -> Self {
self.threshold_amount = Some(amount);
self
}
pub fn topup_amount(mut self, amount: Decimal) -> Self {
self.topup_amount = Some(amount);
self
}
pub fn payment_method_id(mut self, id: impl Into<String>) -> Self {
self.payment_method_id = Some(id.into());
self
}
pub fn daily_limit(mut self, limit: Decimal) -> Self {
self.daily_limit = Some(limit);
self
}
pub fn monthly_limit(mut self, limit: Decimal) -> Self {
self.monthly_limit = Some(limit);
self
}
pub fn build(self) -> Result<crate::services::user_persistence::AutoTopUpSettings, String> {
Ok(crate::services::user_persistence::AutoTopUpSettings {
enabled: self.enabled.unwrap_or(false),
threshold_amount_usd: self.threshold_amount.unwrap_or(dec!(10.0)),
topup_amount_usd: self.topup_amount.unwrap_or(dec!(25.0)),
payment_method_id: self.payment_method_id.ok_or("payment_method_id is required")?,
daily_limit_usd: self.daily_limit,
monthly_limit_usd: self.monthly_limit,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
})
}
}
// =============================================================================
// SSH KEY BUILDERS
// =============================================================================
/// Builder for SSH keys following established patterns
#[derive(Default)]
pub struct SSHKeyBuilder {
id: Option<String>,
name: Option<String>,
public_key: Option<String>,
fingerprint: Option<String>,
key_type: Option<crate::models::ssh_key::SSHKeyType>,
created_at: Option<DateTime<Utc>>,
last_used: Option<DateTime<Utc>>,
is_default: Option<bool>,
comment: Option<String>,
}
impl SSHKeyBuilder {
pub fn new() -> Self {
Self::default()
}
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 public_key(mut self, public_key: impl Into<String>) -> Self {
self.public_key = Some(public_key.into());
self
}
pub fn fingerprint(mut self, fingerprint: impl Into<String>) -> Self {
self.fingerprint = Some(fingerprint.into());
self
}
pub fn key_type(mut self, key_type: crate::models::ssh_key::SSHKeyType) -> Self {
self.key_type = Some(key_type);
self
}
pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
self.created_at = Some(created_at);
self
}
pub fn last_used(mut self, last_used: DateTime<Utc>) -> Self {
self.last_used = Some(last_used);
self
}
pub fn is_default(mut self, is_default: bool) -> Self {
self.is_default = Some(is_default);
self
}
pub fn comment(mut self, comment: impl Into<String>) -> Self {
self.comment = Some(comment.into());
self
}
pub fn build(self) -> Result<crate::models::ssh_key::SSHKey, String> {
Ok(crate::models::ssh_key::SSHKey {
id: self.id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
name: self.name.ok_or("name is required")?,
public_key: self.public_key.ok_or("public_key is required")?,
fingerprint: self.fingerprint.ok_or("fingerprint is required")?,
key_type: self.key_type.ok_or("key_type is required")?,
created_at: self.created_at.unwrap_or_else(|| Utc::now()),
last_used: self.last_used,
is_default: self.is_default.unwrap_or(false),
comment: self.comment,
})
}
}
impl crate::models::ssh_key::SSHKey {
pub fn builder() -> SSHKeyBuilder {
SSHKeyBuilder::new()
}
}