ferripfs-config 0.1.0

IPFS node configuration types, compatible with Kubo config format
Documentation
// Ported from: kubo/config/swarm.go
// Kubo version: v0.39.0
// Original: https://github.com/ipfs/kubo/blob/v0.39.0/config/swarm.go
//
// Original work: Copyright (c) Protocol Labs, Inc.
// Port: Copyright (c) 2026 ferripfs contributors
// SPDX-License-Identifier: MIT OR Apache-2.0

//! Swarm configuration for libp2p networking.

use crate::{Flag, OptionalDuration, OptionalInteger, OptionalString, Priority};
use serde::{Deserialize, Serialize};

/// Swarm configuration section
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SwarmConfig {
    /// Address filters (multiaddr filters to block)
    #[serde(default)]
    pub addr_filters: Vec<String>,

    /// Disable bandwidth metrics collection
    #[serde(default)]
    pub disable_bandwidth_metrics: bool,

    /// Disable NAT port mapping
    #[serde(default)]
    pub disable_nat_port_map: bool,

    /// Relay client configuration
    #[serde(default)]
    pub relay_client: RelayClient,

    /// Relay service configuration
    #[serde(default)]
    pub relay_service: RelayService,

    /// Enable hole punching
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub enable_hole_punching: Option<Flag>,

    /// Transport configuration
    #[serde(default)]
    pub transports: Transports,

    /// Connection manager configuration
    #[serde(default)]
    pub conn_mgr: ConnMgr,

    /// Resource manager configuration
    #[serde(default)]
    pub resource_mgr: ResourceMgr,
}

/// Relay client configuration
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct RelayClient {
    /// Enable relay client
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub enabled: Option<Flag>,

    /// Static relay addresses
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub static_relays: Option<Vec<String>>,
}

/// Relay service configuration
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct RelayService {
    /// Enable relay service
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub enabled: Option<Flag>,

    /// Connection duration limit
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub connection_duration_limit: Option<OptionalDuration>,

    /// Connection data limit
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub connection_data_limit: Option<OptionalInteger>,

    /// Reservation TTL
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        rename = "ReservationTTL"
    )]
    pub reservation_ttl: Option<OptionalDuration>,

    /// Maximum reservations
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_reservations: Option<OptionalInteger>,

    /// Maximum circuits
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_circuits: Option<OptionalInteger>,

    /// Buffer size
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub buffer_size: Option<OptionalInteger>,

    /// Maximum reservations per IP
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        rename = "MaxReservationsPerIP"
    )]
    pub max_reservations_per_ip: Option<OptionalInteger>,

    /// Maximum reservations per ASN
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        rename = "MaxReservationsPerASN"
    )]
    pub max_reservations_per_asn: Option<OptionalInteger>,
}

/// Transport configuration
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Transports {
    /// Network transports
    #[serde(default)]
    pub network: NetworkTransports,

    /// Security transports
    #[serde(default)]
    pub security: SecurityTransports,

    /// Multiplexer transports
    #[serde(default)]
    pub multiplexers: MultiplexerTransports,
}

/// Network transport configuration
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct NetworkTransports {
    /// QUIC transport
    #[serde(default, skip_serializing_if = "Option::is_none", rename = "QUIC")]
    pub quic: Option<Flag>,

    /// TCP transport
    #[serde(default, skip_serializing_if = "Option::is_none", rename = "TCP")]
    pub tcp: Option<Flag>,

    /// WebSocket transport
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub websocket: Option<Flag>,

    /// Relay transport
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub relay: Option<Flag>,

    /// WebTransport
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub web_transport: Option<Flag>,

    /// WebRTC Direct
    #[serde(
        default,
        skip_serializing_if = "Option::is_none",
        rename = "WebRTCDirect"
    )]
    pub web_rtc_direct: Option<Flag>,
}

/// Security transport configuration
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SecurityTransports {
    /// TLS priority (default: 100)
    #[serde(default, skip_serializing_if = "Option::is_none", rename = "TLS")]
    pub tls: Option<Priority>,

    /// Noise priority (default: 300)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub noise: Option<Priority>,
}

/// Multiplexer transport configuration
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct MultiplexerTransports {
    /// Yamux priority (default: 100)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub yamux: Option<Priority>,
}

/// Connection manager configuration
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ConnMgr {
    /// Connection manager type
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub r#type: Option<OptionalString>,

    /// Low water mark for connections
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub low_water: Option<OptionalInteger>,

    /// High water mark for connections
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub high_water: Option<OptionalInteger>,

    /// Grace period before disconnecting
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub grace_period: Option<OptionalDuration>,

    /// Silence period
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub silence_period: Option<OptionalDuration>,
}

impl ConnMgr {
    /// Default low water mark
    pub const DEFAULT_LOW_WATER: i64 = 32;

    /// Default high water mark
    pub const DEFAULT_HIGH_WATER: i64 = 96;

    /// Default grace period in seconds
    pub const DEFAULT_GRACE_PERIOD: &'static str = "1m";
}

/// Resource manager configuration
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ResourceMgr {
    /// Enable resource manager
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub enabled: Option<Flag>,

    /// Maximum memory
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_memory: Option<OptionalString>,

    /// Maximum file descriptors
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub max_file_descriptors: Option<OptionalInteger>,

    /// Allowlist of peer IDs
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub allowlist: Option<Vec<String>>,
}

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

    #[test]
    fn test_swarm_config_default() {
        let swarm = SwarmConfig::default();
        assert!(!swarm.disable_bandwidth_metrics);
        assert!(!swarm.disable_nat_port_map);
    }

    #[test]
    fn test_conn_mgr_defaults() {
        assert_eq!(ConnMgr::DEFAULT_LOW_WATER, 32);
        assert_eq!(ConnMgr::DEFAULT_HIGH_WATER, 96);
    }
}