use super::types::*;
use crate::plugin::inventory::InventoryState;
use crate::service::Service;
use async_trait::async_trait;
use std::any::Any;
#[derive(Clone, Default)]
pub struct LogisticsService;
#[async_trait]
impl Service for LogisticsService {
fn name(&self) -> &'static str {
"issun:logistics_service"
}
fn clone_box(&self) -> Box<dyn Service> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl LogisticsService {
pub fn calculate_transfer_amount(
throughput: u32,
available: u32,
capacity: u32,
global_multiplier: f32,
) -> u32 {
let effective_throughput = (throughput as f32 * global_multiplier) as u32;
effective_throughput.min(available).min(capacity)
}
pub fn passes_filter(item_id: &ItemId, filter: &Option<Vec<ItemId>>) -> bool {
match filter {
None => true, Some(allowed) => allowed.contains(item_id),
}
}
pub fn determine_status(transferred: u32, available: u32, capacity: u32) -> TransporterStatus {
if transferred > 0 {
TransporterStatus::Active
} else if available == 0 {
TransporterStatus::SourceEmpty
} else if capacity == 0 {
TransporterStatus::DestinationFull
} else {
TransporterStatus::Blocked
}
}
pub fn find_transferable_items(
inventory_state: &InventoryState,
source_id: &InventoryEntityId,
filter: &Option<Vec<ItemId>>,
) -> Vec<(ItemId, u32)> {
if let Some(inv) = inventory_state.get_inventory(source_id) {
inv.iter()
.filter(|(item_id, _)| Self::passes_filter(item_id, filter))
.map(|(item_id, &amount)| (item_id.clone(), amount))
.collect()
} else {
Vec::new()
}
}
pub fn calculate_base_cost(amount: u32, distance: f32) -> f32 {
amount as f32 * distance * 0.1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_transfer_amount() {
assert_eq!(
LogisticsService::calculate_transfer_amount(10, 100, 100, 1.0),
10
);
assert_eq!(
LogisticsService::calculate_transfer_amount(10, 5, 100, 1.0),
5
);
assert_eq!(
LogisticsService::calculate_transfer_amount(10, 100, 3, 1.0),
3
);
assert_eq!(
LogisticsService::calculate_transfer_amount(10, 100, 100, 2.0),
20
);
}
#[test]
fn test_passes_filter() {
assert!(LogisticsService::passes_filter(&"iron".into(), &None));
let filter = Some(vec!["iron".into(), "gold".into()]);
assert!(LogisticsService::passes_filter(&"iron".into(), &filter));
assert!(!LogisticsService::passes_filter(&"copper".into(), &filter));
}
#[test]
fn test_determine_status() {
assert_eq!(
LogisticsService::determine_status(10, 100, 100),
TransporterStatus::Active
);
assert_eq!(
LogisticsService::determine_status(0, 0, 100),
TransporterStatus::SourceEmpty
);
assert_eq!(
LogisticsService::determine_status(0, 100, 0),
TransporterStatus::DestinationFull
);
assert_eq!(
LogisticsService::determine_status(0, 100, 100),
TransporterStatus::Blocked
);
}
#[test]
fn test_calculate_base_cost() {
assert_eq!(LogisticsService::calculate_base_cost(10, 5.0), 5.0);
assert_eq!(LogisticsService::calculate_base_cost(100, 1.0), 10.0);
}
}