pray-cli 1.14.0

Package manager for the language placed before inference — Prayfile CLI (binary: pray)
use pray_core::{PrayError, PrayResult};
use pray_transport::{PeerInfo, TransportError};
use std::collections::BTreeMap;
use std::fs;
use std::path::Path;

pub(crate) fn load_known_peer_records(root: &Path) -> PrayResult<BTreeMap<String, PeerInfo>> {
    let path = root.join("v1/peers.json");
    let Ok(text) = fs::read_to_string(&path) else {
        return Ok(BTreeMap::new());
    };
    let peers: Vec<PeerInfo> = serde_json::from_str(&text).map_err(|error| PrayError::Parse {
        kind: "peer list",
        message: error.to_string(),
    })?;
    let mut records = BTreeMap::new();
    for peer in peers {
        let peer = normalize_peer_info(peer)?;
        records.insert(peer.url.clone(), peer);
    }
    Ok(records)
}

pub(crate) fn write_known_peer_records(
    root: &Path,
    peers: &BTreeMap<String, PeerInfo>,
) -> PrayResult<()> {
    let path = root.join("v1/peers.json");
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let values: Vec<PeerInfo> = peers.values().cloned().collect();
    fs::write(
        path,
        serde_json::to_string_pretty(&values)
            .map_err(|error| PrayError::Manifest(error.to_string()))?,
    )?;
    Ok(())
}

pub(crate) fn normalize_peer_info(mut peer: PeerInfo) -> PrayResult<PeerInfo> {
    if peer.url.trim().is_empty() {
        return Err(PrayError::Resolution(
            "peer list contains an entry with an empty url".to_string(),
        ));
    }
    if peer.name.trim().is_empty() {
        peer.name = peer.url.clone();
    }
    Ok(peer)
}

pub(crate) fn upsert_known_peer(records: &mut BTreeMap<String, PeerInfo>, peer: PeerInfo) {
    let url = peer.url.clone();
    match records.get_mut(&url) {
        Some(existing) => {
            if (existing.name == existing.url && peer.name != peer.url)
                || (!existing.public && peer.public)
            {
                *existing = peer;
            }
        }
        None => {
            records.insert(url, peer);
        }
    }
}

pub(crate) fn map_transport_error(error: TransportError) -> PrayError {
    match error {
        TransportError::InvalidResponse(message) => PrayError::Parse {
            kind: "federation response",
            message,
        },
        TransportError::Json(error) => PrayError::Parse {
            kind: "federation response",
            message: error.to_string(),
        },
        TransportError::Network(message) | TransportError::Timeout(message) => {
            PrayError::Network(message)
        }
        TransportError::Io(error) => PrayError::Network(error.to_string()),
        other => PrayError::Resolution(other.to_string()),
    }
}