sd-switch 0.6.3

A systemd unit reload/restart utility for Home Manager
Documentation
use std::{borrow::Cow, path::Path, sync::LazyLock};

use unic_langid::LanguageIdentifier;

struct EnUs;
struct SvSe;

pub(crate) enum UnitAction {
    Start,
    Stop,
    Restart,
    Reload,
}

pub static MSG: LazyLock<Box<dyn Messages + Send + Sync>> =
    LazyLock::new(|| init_messages(sys_locale::get_locales()));

fn init_messages(locales: impl Iterator<Item = String>) -> Box<dyn Messages + Send + Sync> {
    let mut available_langs: Vec<(&str, Box<dyn Messages + Send + Sync>)> =
        vec![("en", Box::new(EnUs)), ("sv", Box::new(SvSe))];

    for preferred_lang in locales.filter_map(|t| t.parse::<LanguageIdentifier>().ok()) {
        for (lang, msgs) in available_langs.drain(..) {
            if preferred_lang.matches(
                &lang
                    .parse::<LanguageIdentifier>()
                    .expect("should be valid locale string"),
                false,
                true,
            ) {
                return msgs;
            }
        }
    }

    Box::new(EnUs)
}

/// Trait covering all translatable strings in the sd-switch library.
///
/// The default implementation for each trait function matches the
/// implementation for `EnUs`.
pub(crate) trait Messages {
    fn reloading_systemd(&self) -> Cow<'static, str> {
        EnUs.reloading_systemd()
    }

    fn err_reloading_systemd(&self) -> Cow<'static, str> {
        EnUs.err_reloading_systemd()
    }

    fn resetting_failed_units(&self) -> Cow<'static, str> {
        EnUs.resetting_failed_units()
    }

    fn err_resetting_failed_units(&self) -> Cow<'static, str> {
        EnUs.err_resetting_failed_units()
    }

    fn err_listing_active_units(&self) -> Cow<'static, str> {
        EnUs.err_listing_active_units()
    }

    fn err_read_dir_entry(&self, path: &Path) -> Cow<'static, str> {
        EnUs.err_read_dir_entry(path)
    }

    fn stopping_units(&self, units: &str) -> Cow<'static, str> {
        EnUs.stopping_units(units)
    }

    fn starting_units(&self, units: &str) -> Cow<'static, str> {
        EnUs.starting_units(units)
    }

    fn restarting_units(&self, units: &str) -> Cow<'static, str> {
        EnUs.restarting_units(units)
    }

    fn reloading_units(&self, units: &str) -> Cow<'static, str> {
        EnUs.reloading_units(units)
    }

    fn keeping_old_units(&self, units: &str) -> Cow<'static, str> {
        EnUs.keeping_old_units(units)
    }

    fn unchanged_units(&self, units: &str) -> Cow<'static, str> {
        EnUs.unchanged_units(units)
    }

    fn err_unit_action_failed(&self, unit: &str, action: UnitAction) -> Cow<'static, str> {
        EnUs.err_unit_action_failed(unit, action)
    }
}

impl Messages for EnUs {
    fn reloading_systemd(&self) -> Cow<'static, str> {
        "Reloading systemd".into()
    }

    fn err_reloading_systemd(&self) -> Cow<'static, str> {
        "Failed to reload systemd".into()
    }

    fn resetting_failed_units(&self) -> Cow<'static, str> {
        "Resetting failed units".into()
    }

    fn err_resetting_failed_units(&self) -> Cow<'static, str> {
        "Error resetting failed units".into()
    }

    fn err_listing_active_units(&self) -> Cow<'static, str> {
        "Failed to list active and activating units".into()
    }

    fn err_read_dir_entry(&self, path: &Path) -> Cow<'static, str> {
        format!("Failed to read directory entry in {}", path.display()).into()
    }

    fn stopping_units(&self, units: &str) -> Cow<'static, str> {
        format!("Stopping units: {units}").into()
    }

    fn starting_units(&self, units: &str) -> Cow<'static, str> {
        format!("Starting units: {units}").into()
    }

    fn restarting_units(&self, units: &str) -> Cow<'static, str> {
        format!("Restarting units: {units}").into()
    }

    fn reloading_units(&self, units: &str) -> Cow<'static, str> {
        format!("Reloading units: {units}").into()
    }

    fn keeping_old_units(&self, units: &str) -> Cow<'static, str> {
        format!("Keeping old units: {units}").into()
    }

    fn unchanged_units(&self, units: &str) -> Cow<'static, str> {
        format!("Keeping unchanged units: {units}").into()
    }

    fn err_unit_action_failed(&self, unit: &str, action: UnitAction) -> Cow<'static, str> {
        let action = match action {
            UnitAction::Start => "start",
            UnitAction::Stop => "stop",
            UnitAction::Restart => "restart",
            UnitAction::Reload => "reload",
        };
        format!("Failed to {action} unit {unit}").into()
    }
}

impl Messages for SvSe {
    fn reloading_systemd(&self) -> Cow<'static, str> {
        "Laddar om systemd".into()
    }

    fn err_reloading_systemd(&self) -> Cow<'static, str> {
        "Lyckades inte ladda om systemd".into()
    }

    fn resetting_failed_units(&self) -> Cow<'static, str> {
        "Återställer felmarkerade enheter".into()
    }

    fn err_resetting_failed_units(&self) -> Cow<'static, str> {
        "Lyckades inte återställa felmarkerade enheter".into()
    }

    fn err_listing_active_units(&self) -> Cow<'static, str> {
        "Lyckades inte lista aktiva och aktiverande enheter".into()
    }

    fn err_read_dir_entry(&self, path: &Path) -> Cow<'static, str> {
        format!("Lyckades inte läsa post i katalogen {}", path.display()).into()
    }

    fn stopping_units(&self, units: &str) -> Cow<'static, str> {
        format!("Stoppar enheter: {units}").into()
    }

    fn starting_units(&self, units: &str) -> Cow<'static, str> {
        format!("Startar enheter: {units}").into()
    }

    fn restarting_units(&self, units: &str) -> Cow<'static, str> {
        format!("Startar om enheter: {units}").into()
    }

    fn reloading_units(&self, units: &str) -> Cow<'static, str> {
        format!("Laddar om enheter: {units}").into()
    }

    fn keeping_old_units(&self, units: &str) -> Cow<'static, str> {
        format!("Behåller gamla enheter: {units}").into()
    }

    fn unchanged_units(&self, units: &str) -> Cow<'static, str> {
        format!("Behåller oförändrade enheter: {units}").into()
    }

    fn err_unit_action_failed(&self, unit: &str, action: UnitAction) -> Cow<'static, str> {
        let action = match action {
            UnitAction::Start => "starta",
            UnitAction::Stop => "stoppa",
            UnitAction::Restart => "starta om",
            UnitAction::Reload => "ladda om",
        };
        format!("Lyckades inte {action} enheten {unit}").into()
    }
}

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

    #[test]
    fn can_handle_no_locale() {
        let langs = vec![].into_iter();

        assert_eq!(
            init_messages(langs).reloading_systemd(),
            EnUs.reloading_systemd()
        );
    }

    #[test]
    fn can_initialize_known_locale() {
        let langs = vec!["sv-SE".to_string()].into_iter();

        assert_eq!(
            init_messages(langs).reloading_systemd(),
            SvSe.reloading_systemd()
        );
    }

    #[test]
    fn ignores_unparseable_locale() {
        let langs = vec!["C".to_string()].into_iter();

        assert_eq!(
            init_messages(langs).reloading_systemd(),
            EnUs.reloading_systemd()
        );
    }
}