Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
heartwood crates radicle-cli src warning.rs
use std::collections::HashMap;
use std::sync::LazyLock;

use radicle::node::config::ConnectAddress;
use radicle::node::{Address, HostName};
use radicle::profile::Config;

const IRIS: &str = "iris.radicle.network";
const ROSA: &str = "rosa.radicle.network";

static NODES_RENAMED: LazyLock<HashMap<HostName, HostName>> = LazyLock::new(|| {
    HashMap::from([
        (
            HostName::Dns("seed.radicle.garden".to_string()),
            HostName::Dns(IRIS.to_string()),
        ),
        (
            HostName::Dns("iris.radicle.xyz".to_string()),
            HostName::Dns(IRIS.to_string()),
        ),
        (
            HostName::Dns("ash.radicle.garden".to_string()),
            HostName::Dns(ROSA.to_string()),
        ),
        (
            HostName::Dns("rosa.radicle.xyz".to_string()),
            HostName::Dns(ROSA.to_string()),
        ),
    ])
});

fn nodes_renamed_for_option(
    option: &'static str,
    iter: impl IntoIterator<Item = ConnectAddress>,
) -> Vec<String> {
    iter.into_iter().enumerate().fold(Vec::new(), |mut warnings, (i, value)| {
        let old: Address = value.into();
        let old = old.host();
        if let Some(new) = NODES_RENAMED.get(old) {
            warnings.push(format!(
                "Value of configuration option `{option}` at index {i} mentions node with hostname '{old}', which has been renamed to '{new}'. Please edit your configuration file to use the new address."
            ));
        }
        warnings
    })
}

fn nodes_renamed(config: &Config) -> Vec<String> {
    let mut warnings = nodes_renamed_for_option("node.connect", config.node.connect.clone());
    warnings.extend(nodes_renamed_for_option(
        "preferredSeeds",
        config.preferred_seeds.clone(),
    ));

    warnings
}

fn implicit_seeding_policy_allow_scope(config: &Config) -> Vec<String> {
    use radicle::node::config::DefaultSeedingPolicy;
    use radicle::node::policy::Scope::*;

    let DefaultSeedingPolicy::Allow { scope } = config.node.seeding_policy else {
        return vec![];
    };

    if !scope.is_implicit() {
        return vec![];
    }

    vec![format!(
        "Configuration option 'node.seedingPolicy.scope' is not set, and thus takes the value '{}' by default. The default value will change to '{}' in a future release. Please edit your configuration file, and set it to one of ['{}', '{}'] explicitly.",
        scope.into_inner(),
        Followed,
        All,
        Followed,
    )]
}

pub(crate) fn config_warnings(config: &Config) -> Vec<String> {
    let mut warnings = nodes_renamed(config);
    warnings.extend(implicit_seeding_policy_allow_scope(config));

    warnings
}

/// Prints a deprecation warning to standard error.
pub(crate) fn deprecated(old: impl std::fmt::Display, new: impl std::fmt::Display) {
    eprintln!(
        "{} {} The command/option `{old}` is deprecated and will be removed. Please use `{new}` instead.",
        radicle_term::PREFIX_WARNING,
        radicle_term::Paint::yellow("Deprecated:").bold(),
    );
}

/// Prints an obsoletion warning to standard error.
pub(crate) fn obsolete(command: impl std::fmt::Display) {
    eprintln!(
        "{} {} The command `{command}` is obsolete and will be removed. Please stop using it.",
        radicle_term::PREFIX_WARNING,
        radicle_term::Paint::yellow("Obsolete:").bold(),
    );
}