spf-milter 0.6.0

Milter for SPF verification
Documentation
// SPF Milter – milter for SPF verification
// Copyright © 2020–2022 David Bürgin <dbuergin@gluet.ch>
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <https://www.gnu.org/licenses/>.

use crate::config::model::{LogDestination, LogLevel, Socket, SyslogFacility};
use std::path::{Path, PathBuf};

// Allow overriding the default configuration file path during the build.
const DEFAULT_CONFIG_FILE: &str = match option_env!("SPF_MILTER_CONFIG_FILE") {
    Some(s) => s,
    None => "/etc/spf-milter.conf",
};

/// A set of CLI options.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CliOptions {
    // The configuration file path defaults to `/etc/spf-milter.conf` and is
    // always present, even if not set on the command-line.
    config_file: PathBuf,
    dry_run: bool,
    log_destination: Option<LogDestination>,
    log_level: Option<LogLevel>,
    socket: Option<Socket>,
    syslog_facility: Option<SyslogFacility>,
}

impl CliOptions {
    pub fn builder() -> CliOptionsBuilder {
        CliOptionsBuilder::new()
    }

    pub fn config_file(&self) -> &Path {
        &self.config_file
    }

    pub fn dry_run(&self) -> bool {
        self.dry_run
    }

    pub fn log_destination(&self) -> Option<LogDestination> {
        self.log_destination
    }

    pub fn log_level(&self) -> Option<LogLevel> {
        self.log_level
    }

    pub fn socket(&self) -> Option<&Socket> {
        self.socket.as_ref()
    }

    pub fn syslog_facility(&self) -> Option<SyslogFacility> {
        self.syslog_facility
    }
}

impl Default for CliOptions {
    fn default() -> Self {
        Self::builder().build()
    }
}

/// A builder for CLI options.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct CliOptionsBuilder {
    config_file: Option<PathBuf>,
    dry_run: bool,
    log_destination: Option<LogDestination>,
    log_level: Option<LogLevel>,
    socket: Option<Socket>,
    syslog_facility: Option<SyslogFacility>,
}

impl CliOptionsBuilder {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn config_file<P: Into<PathBuf>>(mut self, value: P) -> Self {
        self.config_file = Some(value.into());
        self
    }

    pub fn dry_run(mut self, value: bool) -> Self {
        self.dry_run = value;
        self
    }

    pub fn log_destination(mut self, value: LogDestination) -> Self {
        self.log_destination = Some(value);
        self
    }

    pub fn log_level(mut self, value: LogLevel) -> Self {
        self.log_level = Some(value);
        self
    }

    pub fn socket(mut self, value: Socket) -> Self {
        self.socket = Some(value);
        self
    }

    pub fn syslog_facility(mut self, value: SyslogFacility) -> Self {
        self.syslog_facility = Some(value);
        self
    }

    pub fn build(self) -> CliOptions {
        CliOptions {
            config_file: self
                .config_file
                .unwrap_or_else(|| DEFAULT_CONFIG_FILE.into()),
            dry_run: self.dry_run,
            log_destination: self.log_destination,
            log_level: self.log_level,
            socket: self.socket,
            syslog_facility: self.syslog_facility,
        }
    }
}

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

    #[test]
    fn cli_options_builder_ok() {
        let opts = CliOptions::builder()
            .socket("inet:localhost:3000".parse().unwrap())
            .build();

        assert_eq!(opts.socket(), Some(&Socket::Inet("localhost:3000".into())));
        assert_eq!(opts.config_file(), Path::new("/etc/spf-milter.conf"));
    }
}