multiio 0.2.3

A unified I/O orchestration library for CLI/server applications
Documentation
use std::sync::Arc;

use crate::format::FormatKind;
use crate::io::{InputProvider, OutputTarget};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FileExistsPolicy {
    /// Overwrite existing files
    Overwrite,
    /// Append to existing files
    Append,
    #[default]
    /// Return an error if file exists
    Error,
}

impl std::str::FromStr for FileExistsPolicy {
    type Err = ();

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let policy = match s.to_ascii_lowercase().as_str() {
            "overwrite" => FileExistsPolicy::Overwrite,
            "append" => FileExistsPolicy::Append,
            "error" => FileExistsPolicy::Error,
            _ => return Err(()),
        };
        Ok(policy)
    }
}

#[derive(Debug, Clone)]
pub struct InputSpec {
    /// Raw input argument or configuration string
    pub raw: String,
    /// The input provider implementation
    pub provider: Arc<dyn InputProvider>,
    /// Explicitly specified format (if any)
    pub explicit_format: Option<FormatKind>,
    /// Candidate formats to try (in order)
    pub format_candidates: Vec<FormatKind>,
}

impl InputSpec {
    pub fn new(raw: impl Into<String>, provider: Arc<dyn InputProvider>) -> Self {
        Self {
            raw: raw.into(),
            provider,
            explicit_format: None,
            format_candidates: Vec::new(),
        }
    }

    /// Set the explicit format.
    pub fn with_format(mut self, format: FormatKind) -> Self {
        self.explicit_format = Some(format);
        self
    }

    /// Set the format candidates.
    pub fn with_candidates(mut self, candidates: Vec<FormatKind>) -> Self {
        self.format_candidates = candidates;
        self
    }
}

#[derive(Debug, Clone)]
pub struct OutputSpec {
    /// Raw output argument or configuration string
    pub raw: String,
    /// The output target implementation
    pub target: Arc<dyn OutputTarget>,
    /// Explicitly specified format (if any)
    pub explicit_format: Option<FormatKind>,
    /// Candidate formats to try (in order)
    pub format_candidates: Vec<FormatKind>,
    /// Policy for handling existing files
    pub file_exists_policy: FileExistsPolicy,
}

impl OutputSpec {
    pub fn new(raw: impl Into<String>, target: Arc<dyn OutputTarget>) -> Self {
        Self {
            raw: raw.into(),
            target,
            explicit_format: None,
            format_candidates: Vec::new(),
            file_exists_policy: FileExistsPolicy::default(),
        }
    }

    /// Set the explicit format.
    pub fn with_format(mut self, format: FormatKind) -> Self {
        self.explicit_format = Some(format);
        self
    }

    /// Set the format candidates.
    pub fn with_candidates(mut self, candidates: Vec<FormatKind>) -> Self {
        self.format_candidates = candidates;
        self
    }

    /// Set the file exists policy.
    pub fn with_file_exists_policy(mut self, policy: FileExistsPolicy) -> Self {
        self.file_exists_policy = policy;
        self
    }
}