#![allow(non_snake_case)]
use std::sync::{Arc, RwLock};
use std::collections::HashMap as FxHashMap;
use std::time::Duration;
use serde::{Serialize, Deserialize};
use super::core::{RiDevice, RiDeviceType, RiDeviceStatus};
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiResourcePool {
name: String,
device_type: RiDeviceType,
devices: FxHashMap<String, Arc<RiDevice>>,
total_capacity: usize,
available_capacity: usize,
allocated_capacity: usize,
pending_requests: usize,
total_compute_units: usize,
total_memory_gb: f64,
total_storage_gb: f64,
total_bandwidth_gbps: f64,
available_compute_units: usize,
available_memory_gb: f64,
available_storage_gb: f64,
available_bandwidth_gbps: f64,
connection_pool: Arc<RwLock<RiConnectionPool>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass(get_all, set_all))]
pub struct RiResourcePoolConfig {
pub name: String,
pub device_type: RiDeviceType,
pub max_concurrent_allocations: usize,
pub allocation_timeout_secs: u64,
pub health_check_interval_secs: u64,
}
impl Default for RiResourcePoolConfig {
fn default() -> Self {
Self {
name: "default_pool".to_string(),
device_type: RiDeviceType::CPU,
max_concurrent_allocations: 10,
allocation_timeout_secs: 60,
health_check_interval_secs: 30,
}
}
}
impl RiResourcePool {
pub fn new(config: RiResourcePoolConfig) -> Self {
let connection_pool = Arc::new(RwLock::new(RiConnectionPool::new(
config.max_concurrent_allocations,
Duration::from_secs(config.allocation_timeout_secs),
Duration::from_secs(config.health_check_interval_secs),
)));
Self {
name: config.name,
device_type: config.device_type,
devices: FxHashMap::with_capacity(16),
total_capacity: 0,
available_capacity: 0,
allocated_capacity: 0,
pending_requests: 0,
total_compute_units: 0,
total_memory_gb: 0.0,
total_storage_gb: 0.0,
total_bandwidth_gbps: 0.0,
available_compute_units: 0,
available_memory_gb: 0.0,
available_storage_gb: 0.0,
available_bandwidth_gbps: 0.0,
connection_pool,
}
}
pub fn add_device(&mut self, device: Arc<RiDevice>) -> bool {
if device.device_type() != self.device_type {
return false;
}
let device_id = device.id().to_string();
if self.devices.contains_key(&device_id) {
return false;
}
let compute_units = device.capabilities().compute_units.unwrap_or(0);
let memory_gb = device.capabilities().memory_gb.unwrap_or(0.0);
let storage_gb = device.capabilities().storage_gb.unwrap_or(0.0);
let bandwidth_gbps = device.capabilities().bandwidth_gbps.unwrap_or(0.0);
self.devices.insert(device_id, device);
self.total_capacity += 1;
self.available_capacity += 1;
self.total_compute_units += compute_units;
self.total_memory_gb += memory_gb;
self.total_storage_gb += storage_gb;
self.total_bandwidth_gbps += bandwidth_gbps;
self.available_compute_units += compute_units;
self.available_memory_gb += memory_gb;
self.available_storage_gb += storage_gb;
self.available_bandwidth_gbps += bandwidth_gbps;
true
}
pub fn remove_device(&mut self, device_id: &str) -> bool {
if let Some(device) = self.devices.remove(device_id) {
let capabilities = device.capabilities();
self.total_capacity -= 1;
if device.is_available() {
self.available_capacity -= 1;
self.available_compute_units -= capabilities.compute_units.unwrap_or(0);
self.available_memory_gb -= capabilities.memory_gb.unwrap_or(0.0);
self.available_storage_gb -= capabilities.storage_gb.unwrap_or(0.0);
self.available_bandwidth_gbps -= capabilities.bandwidth_gbps.unwrap_or(0.0);
} else if device.is_allocated() {
self.allocated_capacity -= 1;
}
self.total_compute_units -= capabilities.compute_units.unwrap_or(0);
self.total_memory_gb -= capabilities.memory_gb.unwrap_or(0.0);
self.total_storage_gb -= capabilities.storage_gb.unwrap_or(0.0);
self.total_bandwidth_gbps -= capabilities.bandwidth_gbps.unwrap_or(0.0);
true
} else {
false
}
}
pub fn allocate(&mut self, _allocation_id: &str) -> Option<Arc<RiDevice>> {
if self.available_capacity == 0 {
return None;
}
for device in self.devices.values() {
if device.is_available() {
let capabilities = device.capabilities();
self.available_capacity -= 1;
self.allocated_capacity += 1;
self.available_compute_units -= capabilities.compute_units.unwrap_or(0);
self.available_memory_gb -= capabilities.memory_gb.unwrap_or(0.0);
self.available_storage_gb -= capabilities.storage_gb.unwrap_or(0.0);
self.available_bandwidth_gbps -= capabilities.bandwidth_gbps.unwrap_or(0.0);
let mut pool = match self.connection_pool.write() {
Ok(pool) => pool,
Err(e) => {
log::error!("Failed to acquire connection pool lock: {}", e);
return None;
}
};
pool.add_connection(device.id().to_string(), device.id().to_string());
return Some(device.clone());
}
}
None
}
pub fn release(&mut self, allocation_id: &str) -> bool {
for device in self.devices.values() {
if let Some(current_allocation) = device.get_allocation_id() {
if current_allocation == allocation_id {
let capabilities = device.capabilities();
self.allocated_capacity -= 1;
self.available_capacity += 1;
self.available_compute_units += capabilities.compute_units.unwrap_or(0);
self.available_memory_gb += capabilities.memory_gb.unwrap_or(0.0);
self.available_storage_gb += capabilities.storage_gb.unwrap_or(0.0);
self.available_bandwidth_gbps += capabilities.bandwidth_gbps.unwrap_or(0.0);
match self.connection_pool.write() {
Ok(mut pool) => {
pool.remove_connection(device.id());
}
Err(e) => {
log::error!("Failed to acquire connection pool lock for removal: {}", e);
}
}
return true;
}
}
}
false
}
pub fn get_status(&self) -> super::RiResourcePoolStatus {
super::RiResourcePoolStatus {
total_capacity: self.total_capacity,
available_capacity: self.available_capacity,
allocated_capacity: self.allocated_capacity,
pending_requests: self.pending_requests,
utilization_rate: if self.total_capacity > 0 {
(self.allocated_capacity as f64 / self.total_capacity as f64) * 100.0
} else {
0.0
},
}
}
#[inline]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
pub fn device_type(&self) -> RiDeviceType {
self.device_type
}
#[inline]
pub fn get_devices(&self) -> Vec<Arc<RiDevice>> {
self.devices.values().cloned().collect()
}
pub fn get_available_devices(&self) -> Vec<Arc<RiDevice>> {
self.devices.values()
.filter(|device| device.is_available())
.cloned()
.collect()
}
pub fn get_allocated_devices(&self) -> Vec<Arc<RiDevice>> {
self.devices.values()
.filter(|device| device.is_allocated())
.cloned()
.collect()
}
#[inline]
pub fn has_available_capacity(&self) -> bool {
self.available_capacity > 0
}
#[inline]
pub fn utilization_rate(&self) -> f64 {
if self.total_capacity > 0 {
self.allocated_capacity as f64 / self.total_capacity as f64
} else {
0.0
}
}
pub fn is_healthy(&self) -> bool {
self.available_capacity > 0 || self.allocated_capacity > 0
}
pub fn get_statistics(&self) -> RiResourcePoolStatistics {
let devices = self.get_devices();
let available_devices = self.get_available_devices();
let allocated_devices = self.get_allocated_devices();
let total_compute_units: usize = devices.iter()
.filter_map(|d| d.capabilities().compute_units)
.sum();
let total_memory_gb: f64 = devices.iter()
.filter_map(|d| d.capabilities().memory_gb)
.sum();
let total_storage_gb: f64 = devices.iter()
.filter_map(|d| d.capabilities().storage_gb)
.sum();
let total_bandwidth_gbps: f64 = devices.iter()
.filter_map(|d| d.capabilities().bandwidth_gbps)
.sum();
let average_health_score: f64 = if !devices.is_empty() {
devices.iter()
.map(|d| d.health_score() as f64)
.sum::<f64>() / devices.len() as f64
} else {
0.0
};
let connection_pool_stats = match self.connection_pool.read() {
Ok(pool) => Some(pool.get_statistics()),
Err(e) => {
log::error!("Failed to acquire connection pool read lock: {}", e);
None
}
};
let mut status_distribution = FxHashMap::with_capacity(4);
for device in &devices {
*status_distribution.entry(device.status()).or_insert(0) += 1;
}
let mut total_response_time_ms = 0.0;
let mut total_network_latency_ms = 0.0;
let mut total_disk_iops = 0.0;
let mut total_battery_level_percent = 0.0;
let mut total_cpu_usage_percent = 0.0;
let mut total_memory_usage_percent = 0.0;
let mut total_temperature_celsius = 0.0;
let mut total_error_count = 0u32;
let mut total_throughput = 0.0;
let mut total_uptime_seconds = 0.0;
for device in &devices {
let health_metrics = device.health_metrics();
total_response_time_ms += health_metrics.response_time_ms;
total_network_latency_ms += health_metrics.network_latency_ms;
total_disk_iops += health_metrics.disk_iops as f64;
total_battery_level_percent += health_metrics.battery_level_percent;
total_cpu_usage_percent += health_metrics.cpu_usage_percent;
total_memory_usage_percent += health_metrics.memory_usage_percent;
total_temperature_celsius += health_metrics.temperature_celsius;
total_error_count += health_metrics.error_count;
total_throughput += health_metrics.throughput as f64;
total_uptime_seconds += health_metrics.uptime_seconds as f64;
}
let device_count = devices.len() as f64;
RiResourcePoolStatistics {
total_devices: devices.len(),
available_devices: available_devices.len(),
allocated_devices: allocated_devices.len(),
utilization_rate: self.utilization_rate(),
total_compute_units,
total_memory_gb,
total_storage_gb,
total_bandwidth_gbps,
average_health_score,
device_type: self.device_type,
connection_pool_stats,
status_distribution,
average_response_time_ms: if device_count > 0.0 { total_response_time_ms / device_count } else { 0.0 },
average_network_latency_ms: if device_count > 0.0 { total_network_latency_ms / device_count } else { 0.0 },
average_disk_iops: if device_count > 0.0 { total_disk_iops / device_count } else { 0.0 },
average_battery_level_percent: if device_count > 0.0 { total_battery_level_percent / device_count } else { 0.0 },
average_cpu_usage_percent: if device_count > 0.0 { total_cpu_usage_percent / device_count } else { 0.0 },
average_memory_usage_percent: if device_count > 0.0 { total_memory_usage_percent / device_count } else { 0.0 },
average_temperature_celsius: if device_count > 0.0 { total_temperature_celsius / device_count } else { 0.0 },
total_error_count,
average_throughput: if device_count > 0.0 { total_throughput / device_count } else { 0.0 },
average_uptime_seconds: if device_count > 0.0 { total_uptime_seconds / device_count } else { 0.0 },
}
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct RiConnectionPool {
connections: FxHashMap<String, RiConnectionInfo>,
max_connections: usize,
connection_timeout: Duration,
health_check_interval: Duration,
last_health_check_secs: u64,
pub active_connections: usize,
pub failed_connections: usize,
pub total_errors: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiConnectionInfo {
pub connection_id: String,
pub device_id: String,
pub address: String,
pub established_at_secs: u64,
pub last_activity_secs: u64,
pub state: RiConnectionState,
pub health_metrics: RiConnectionHealthMetrics,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum RiConnectionState {
Connecting,
Active,
Idle,
Unhealthy,
Closing,
Closed,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RiConnectionHealthMetrics {
pub successful_operations: u64,
pub failed_operations: u64,
pub average_response_time_ms: f64,
pub last_error_secs: Option<u64>,
pub uptime_percentage: f64,
}
#[allow(dead_code)]
impl RiConnectionPool {
pub fn new(max_connections: usize, connection_timeout: Duration, health_check_interval: Duration) -> Self {
Self {
connections: FxHashMap::with_capacity(max_connections),
max_connections,
connection_timeout,
health_check_interval,
last_health_check_secs: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(Duration::from_secs(0))
.as_secs(),
active_connections: 0,
failed_connections: 0,
total_errors: 0,
}
}
pub fn add_connection(&mut self, device_id: String, address: String) {
let connection_info = RiConnectionInfo {
connection_id: device_id.clone(),
device_id: device_id.clone(),
address,
established_at_secs: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
last_activity_secs: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
state: RiConnectionState::Active,
health_metrics: RiConnectionHealthMetrics::default(),
};
self.connections.insert(device_id, connection_info);
self.active_connections += 1;
}
pub fn remove_connection(&mut self, connection_id: &str) -> bool {
self.connections.remove(connection_id).is_some()
}
pub fn get_connection(&self, connection_id: &str) -> Option<&RiConnectionInfo> {
self.connections.get(connection_id)
}
pub fn update_activity(&mut self, connection_id: &str) -> bool {
if let Some(connection) = self.connections.get_mut(connection_id) {
connection.last_activity_secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(Duration::from_secs(0))
.as_secs();
if connection.state == RiConnectionState::Idle {
connection.state = RiConnectionState::Active;
}
true
} else {
false
}
}
pub fn update_health_metrics(&mut self, connection_id: &str, success: bool, response_time_ms: f64) -> bool {
if let Some(connection) = self.connections.get_mut(connection_id) {
if success {
connection.health_metrics.successful_operations += 1;
} else {
connection.health_metrics.failed_operations += 1;
connection.health_metrics.last_error_secs = Some(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(Duration::from_secs(0))
.as_secs()
);
}
let total_ops = connection.health_metrics.successful_operations + connection.health_metrics.failed_operations;
connection.health_metrics.average_response_time_ms =
(connection.health_metrics.average_response_time_ms * (total_ops - 1) as f64 + response_time_ms) / total_ops as f64;
let total_ops = connection.health_metrics.successful_operations + connection.health_metrics.failed_operations;
connection.health_metrics.uptime_percentage =
(connection.health_metrics.successful_operations as f64 / total_ops as f64) * 100.0;
true
} else {
false
}
}
pub fn perform_health_check(&mut self) {
self.last_health_check_secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(Duration::from_secs(0))
.as_secs();
for connection in self.connections.values_mut() {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let elapsed_secs = current_time.saturating_sub(connection.last_activity_secs);
if connection.state == RiConnectionState::Active && elapsed_secs > self.connection_timeout.as_secs() {
connection.state = RiConnectionState::Idle;
}
if connection.health_metrics.uptime_percentage < 90.0 {
connection.state = RiConnectionState::Unhealthy;
} else if let Some(last_error_secs) = connection.health_metrics.last_error_secs {
let current_secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or(Duration::from_secs(0))
.as_secs();
if current_secs.saturating_sub(last_error_secs) < 60 {
connection.state = RiConnectionState::Unhealthy;
}
}
if connection.state == RiConnectionState::Unhealthy &&
connection.health_metrics.failed_operations > 10 {
connection.state = RiConnectionState::Closing;
}
}
self.connections.retain(|_, conn| conn.state != RiConnectionState::Closed);
}
pub fn active_connections(&self) -> usize {
self.connections.values()
.filter(|conn| conn.state == RiConnectionState::Active)
.count()
}
pub fn idle_connections(&self) -> usize {
self.connections.values()
.filter(|conn| conn.state == RiConnectionState::Idle)
.count()
}
pub fn unhealthy_connections(&self) -> usize {
self.connections.values()
.filter(|conn| conn.state == RiConnectionState::Unhealthy)
.count()
}
pub fn get_statistics(&self) -> RiConnectionPoolStatistics {
let total_connections = self.connections.len();
let active_connections = self.active_connections();
let idle_connections = self.idle_connections();
let unhealthy_connections = self.unhealthy_connections();
let total_successful_ops: u64 = self.connections.values()
.map(|conn| conn.health_metrics.successful_operations)
.sum();
let total_failed_ops: u64 = self.connections.values()
.map(|conn| conn.health_metrics.failed_operations)
.sum();
let avg_response_time = if !self.connections.is_empty() {
let total_response_time: f64 = self.connections.values()
.map(|conn| conn.health_metrics.average_response_time_ms)
.sum();
total_response_time / self.connections.len() as f64
} else {
0.0
};
let last_health_check_secs = self.last_health_check_secs;
RiConnectionPoolStatistics {
total_connections,
active_connections,
idle_connections,
unhealthy_connections,
available_slots: self.max_connections.saturating_sub(total_connections),
total_successful_operations: total_successful_ops,
total_failed_operations: total_failed_ops,
average_response_time_ms: avg_response_time,
health_check_interval_secs: self.health_check_interval.as_secs(),
last_health_check_secs,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass(get_all, set_all))]
pub struct RiConnectionPoolStatistics {
pub total_connections: usize,
pub active_connections: usize,
pub idle_connections: usize,
pub unhealthy_connections: usize,
pub available_slots: usize,
pub total_successful_operations: u64,
pub total_failed_operations: u64,
pub average_response_time_ms: f64,
pub health_check_interval_secs: u64,
pub last_health_check_secs: u64,
}
impl Default for RiConnectionPoolStatistics {
fn default() -> Self {
Self {
total_connections: 0,
active_connections: 0,
idle_connections: 0,
unhealthy_connections: 0,
available_slots: 0,
total_successful_operations: 0,
total_failed_operations: 0,
average_response_time_ms: 0.0,
health_check_interval_secs: 0,
last_health_check_secs: 0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass(get_all, set_all))]
pub struct RiResourcePoolStatistics {
pub total_devices: usize,
pub available_devices: usize,
pub allocated_devices: usize,
pub utilization_rate: f64,
pub total_compute_units: usize,
pub total_memory_gb: f64,
pub total_storage_gb: f64,
pub total_bandwidth_gbps: f64,
pub average_health_score: f64,
pub device_type: RiDeviceType,
pub connection_pool_stats: Option<RiConnectionPoolStatistics>,
pub status_distribution: FxHashMap<RiDeviceStatus, usize>,
pub average_response_time_ms: f64,
pub average_network_latency_ms: f64,
pub average_disk_iops: f64,
pub average_battery_level_percent: f64,
pub average_cpu_usage_percent: f64,
pub average_memory_usage_percent: f64,
pub average_temperature_celsius: f64,
pub total_error_count: u32,
pub average_throughput: f64,
pub average_uptime_seconds: f64,
}
impl Default for RiResourcePoolStatistics {
fn default() -> Self {
Self {
total_devices: 0,
available_devices: 0,
allocated_devices: 0,
utilization_rate: 0.0,
total_compute_units: 0,
total_memory_gb: 0.0,
total_storage_gb: 0.0,
total_bandwidth_gbps: 0.0,
average_health_score: 0.0,
device_type: RiDeviceType::CPU,
connection_pool_stats: None,
status_distribution: FxHashMap::with_capacity(4),
average_response_time_ms: 0.0,
average_network_latency_ms: 0.0,
average_disk_iops: 0.0,
average_battery_level_percent: 0.0,
average_cpu_usage_percent: 0.0,
average_memory_usage_percent: 0.0,
average_temperature_celsius: 0.0,
total_error_count: 0,
average_throughput: 0.0,
average_uptime_seconds: 0.0,
}
}
}
#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
pub struct RiResourcePoolManager {
pools: FxHashMap<String, Arc<RiResourcePool>>,
}
impl Default for RiResourcePoolManager {
fn default() -> Self {
Self::new()
}
}
impl RiResourcePoolManager {
pub fn new() -> Self {
Self {
pools: FxHashMap::with_capacity(8),
}
}
pub fn create_pool(&mut self, config: RiResourcePoolConfig) -> Arc<RiResourcePool> {
let pool = Arc::new(RiResourcePool::new(config));
self.pools.insert(pool.name().to_string(), pool.clone());
pool
}
pub fn get_pool(&self, name: &str) -> Option<Arc<RiResourcePool>> {
self.pools.get(name).cloned()
}
pub fn remove_pool(&mut self, name: &str) -> Option<Arc<RiResourcePool>> {
self.pools.remove(name)
}
pub fn get_all_pools(&self) -> Vec<Arc<RiResourcePool>> {
self.pools.values().cloned().collect()
}
pub fn get_pools_by_type(&self, device_type: RiDeviceType) -> Vec<Arc<RiResourcePool>> {
self.pools.values()
.filter(|pool| pool.device_type() == device_type)
.cloned()
.collect()
}
pub fn get_overall_statistics(&self) -> RiResourcePoolStatistics {
let pools = self.get_all_pools();
let total_devices: usize = pools.iter().map(|p| p.get_statistics().total_devices).sum();
let allocated_devices: usize = pools.iter().map(|p| p.get_statistics().allocated_devices).sum();
let total_compute_units: usize = pools.iter()
.flat_map(|p| p.get_devices())
.filter_map(|d| d.capabilities().compute_units)
.sum();
let total_memory_gb: f64 = pools.iter()
.flat_map(|p| p.get_devices())
.filter_map(|d| d.capabilities().memory_gb)
.sum();
let total_storage_gb: f64 = pools.iter()
.flat_map(|p| p.get_devices())
.filter_map(|d| d.capabilities().storage_gb)
.sum();
let total_bandwidth_gbps: f64 = pools.iter()
.flat_map(|p| p.get_devices())
.filter_map(|d| d.capabilities().bandwidth_gbps)
.sum();
let overall_utilization = if total_devices > 0 {
allocated_devices as f64 / total_devices as f64
} else {
0.0
};
let total_health_score: f64 = pools.iter()
.map(|p| p.get_statistics().average_health_score)
.sum();
let average_health_score = if !pools.is_empty() {
total_health_score / pools.len() as f64
} else {
0.0
};
RiResourcePoolStatistics {
total_devices,
available_devices: total_devices - allocated_devices,
allocated_devices,
utilization_rate: overall_utilization,
total_compute_units,
total_memory_gb,
total_storage_gb,
total_bandwidth_gbps,
average_health_score,
device_type: RiDeviceType::Custom, connection_pool_stats: None,
status_distribution: FxHashMap::with_capacity(4),
average_response_time_ms: 0.0,
average_network_latency_ms: 0.0,
average_disk_iops: 0.0,
average_battery_level_percent: 0.0,
average_cpu_usage_percent: 0.0,
average_memory_usage_percent: 0.0,
average_temperature_celsius: 0.0,
total_error_count: 0,
average_throughput: 0.0,
average_uptime_seconds: 0.0,
}
}
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiResourcePoolConfig {
#[new]
fn py_new() -> Self {
Self::default()
}
#[staticmethod]
fn py_new_with_name(name: String, device_type: RiDeviceType) -> Self {
Self {
name,
device_type,
max_concurrent_allocations: 10,
allocation_timeout_secs: 60,
health_check_interval_secs: 30,
}
}
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiResourcePoolStatistics {
#[new]
fn py_new() -> Self {
Self::default()
}
}
#[cfg(feature = "pyo3")]
#[pyo3::prelude::pymethods]
impl RiResourcePoolManager {
#[new]
fn py_new() -> Self {
Self::new()
}
}