chunky-bits 0.2.0

Chunky Bits is simple, unmanaged, distributed HTTP object store tool.
use std::{
    cmp::Ordering,
    collections::{
        BTreeMap,
        BTreeSet,
    },
};

use serde::{
    Deserialize,
    Serialize,
};

use crate::file::WeightedLocation;

#[derive(Clone, Serialize, Deserialize)]
#[serde(from = "ClusterNodesDeserializer")]
pub struct ClusterNodes(pub Vec<ClusterNode>);

impl From<ClusterNodes> for BTreeSet<ClusterNode> {
    fn from(c: ClusterNodes) -> BTreeSet<ClusterNode> {
        c.0.into_iter().collect()
    }
}

#[derive(Deserialize)]
#[serde(untagged)]
enum ClusterNodesDeserializer {
    Single(ClusterNode),
    Set(Vec<ClusterNodesDeserializer>),
    Map(BTreeMap<String, ClusterNodesDeserializer>),
}

impl From<ClusterNodesDeserializer> for ClusterNodes {
    fn from(des: ClusterNodesDeserializer) -> ClusterNodes {
        use ClusterNodesDeserializer::*;
        ClusterNodes(match des {
            Single(node) => {
                vec![node]
            },
            Set(mut nodes) => {
                let mut nodes_out = Vec::<ClusterNode>::new();
                for sub_nodes in nodes.drain(..) {
                    let mut nodes: ClusterNodes = sub_nodes.into();
                    nodes_out.append(&mut nodes.0);
                }
                nodes_out
            },
            Map(nodes) => {
                let mut nodes_out = Vec::<ClusterNode>::new();
                for (name, sub_nodes) in nodes.into_iter() {
                    let nodes: ClusterNodes = sub_nodes.into();
                    for sub_node in nodes.0 {
                        let mut sub_node = sub_node.clone();
                        sub_node.zones.insert(name.clone());
                        nodes_out.push(sub_node);
                    }
                }
                nodes_out
            },
        })
    }
}

#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ClusterNode {
    #[serde(flatten)]
    pub location: WeightedLocation,
    #[serde(default)]
    pub zones: BTreeSet<String>,
    #[serde(default)]
    pub repeat: usize,
}

impl Ord for ClusterNode {
    fn cmp(&self, other: &Self) -> Ordering {
        if self.zones == other.zones {
            self.location.cmp(&other.location)
        } else {
            self.zones.cmp(&other.zones)
        }
    }
}

impl PartialOrd for ClusterNode {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}