//! Configuring the tunnel.

use crate::errors::{TunnelError, TunnelResult};
use arti_client::config::{BoolOrAuto, BridgeConfigBuilder, CfgPath, TorClientConfigBuilder};
use arti_client::TorClientConfig;
use std::path::PathBuf;
use std::str::FromStr;

/// Configuration for an `OnionTunnel`.
///
/// # Usage notes
///
/// This struct is marked `#[non_exhaustive]` so future fields can be added, which means it's
/// slightly harder to make one by hand. You can make use of the `Default` impl, though:
///
/// ```rust
/// use onion_tunnel::config::TunnelConfig;
///
/// let mut cfg = TunnelConfig::default();
/// // for example
/// cfg.state_dir = Some("/path/to/state".into());
/// ```
#[derive(Clone, Debug, PartialEq, Default)]
#[non_exhaustive]
pub struct TunnelConfig {
    /// Where to store Arti's persistent state.
    pub state_dir: Option<PathBuf>,
    /// Where to store Arti's cache.
    pub cache_dir: Option<PathBuf>,
    /// Where to store persistent data for Pluggable Transports.
    ///
    /// If not specified, a subdirectory of `state_dir` (or its default value)
    /// will be used.
    pub pt_dir: Option<PathBuf>,
    /// A list of bridge lines to use.
    ///
    /// If this is empty, bridges will be disabled.
    pub bridge_lines: Vec<String>,
    /// A path to write a pcap file to for debugging.
    pub pcap_path: Option<PathBuf>,
}

impl TunnelConfig {
    /// Make an Arti `TorClientConfig` using the values in this `TunnelConfig`.
    pub(crate) fn arti_config(&self) -> TunnelResult<TorClientConfig> {
        let mut builder = TorClientConfigBuilder::default();

        if let Some(ref state_dir) = self.state_dir {
            builder
                .storage()
                .state_dir(CfgPath::new_literal(state_dir.to_owned()));
        }

        if let Some(ref cache_dir) = self.cache_dir {
            builder
                .storage()
                .cache_dir(CfgPath::new_literal(cache_dir.to_owned()));
        }

        if !self.bridge_lines.is_empty() {
            for line in self.bridge_lines.iter() {
                let bcb =
                    BridgeConfigBuilder::from_str(line).map_err(TunnelError::BadBridgeLine)?;

                builder.bridges().bridges().push(bcb);
            }
            builder.bridges().enabled(BoolOrAuto::Explicit(true));
        }

        builder.build().map_err(TunnelError::ArtiConfig)
    }
}
