use std::collections::HashMap;
use serde::Deserialize;
use crate::Category;
#[derive(Debug, Clone)]
pub struct Hierarchy {
children_map: HashMap<u64, Vec<u64>>,
parent_map: HashMap<u64, u64>,
ancestors_map: HashMap<u64, Vec<u64>>,
pub virtual_names: HashMap<u64, String>,
}
impl Hierarchy {
pub fn from_parent_map(parent_map: HashMap<u64, u64>) -> Self {
let mut children_map: HashMap<u64, Vec<u64>> = HashMap::new();
for (&child, &parent) in &parent_map {
children_map.entry(parent).or_default().push(child);
}
let mut all_ids: Vec<u64> = parent_map.keys().copied().collect();
for &parent in parent_map.values() {
if !parent_map.contains_key(&parent) {
all_ids.push(parent);
}
}
all_ids.sort_unstable();
all_ids.dedup();
let mut ancestors_map: HashMap<u64, Vec<u64>> = HashMap::new();
for &id in &all_ids {
let mut ancestors = vec![id];
let mut current = id;
while let Some(&parent) = parent_map.get(¤t) {
ancestors.push(parent);
current = parent;
}
ancestors_map.insert(id, ancestors);
}
Hierarchy {
children_map,
parent_map,
ancestors_map,
virtual_names: HashMap::new(),
}
}
pub fn from_categories(categories: &[Category]) -> Self {
let name_to_id: HashMap<&str, u64> =
categories.iter().map(|c| (c.name.as_str(), c.id)).collect();
let mut parent_map: HashMap<u64, u64> = HashMap::new();
let mut virtual_id = u64::MAX - 1;
let mut virtual_names: HashMap<String, u64> = HashMap::new();
for cat in categories {
if let Some(ref supercat) = cat.supercategory {
if supercat == &cat.name {
continue;
}
let parent_id = if let Some(&id) = name_to_id.get(supercat.as_str()) {
id
} else if let Some(&id) = virtual_names.get(supercat) {
id
} else {
let id = virtual_id;
virtual_names.insert(supercat.clone(), id);
virtual_id = virtual_id.wrapping_sub(1);
id
};
parent_map.insert(cat.id, parent_id);
}
}
let mut h = Self::from_parent_map(parent_map);
h.virtual_names = virtual_names.into_iter().map(|(k, v)| (v, k)).collect();
h
}
pub fn from_oid_json(
json: &str,
label_to_id: &HashMap<String, u64>,
) -> Result<Self, serde_json::Error> {
let root: OidNode = serde_json::from_str(json)?;
let mut parent_map: HashMap<u64, u64> = HashMap::new();
let mut virtual_id = u64::MAX - 1;
let mut virtual_labels: HashMap<String, u64> = HashMap::new();
fn resolve_id(
label: &str,
label_to_id: &HashMap<String, u64>,
virtual_labels: &mut HashMap<String, u64>,
virtual_id: &mut u64,
) -> u64 {
if let Some(&id) = label_to_id.get(label) {
id
} else if let Some(&id) = virtual_labels.get(label) {
id
} else {
let id = *virtual_id;
virtual_labels.insert(label.to_string(), id);
*virtual_id = virtual_id.wrapping_sub(1);
id
}
}
fn walk(
node: &OidNode,
parent_map: &mut HashMap<u64, u64>,
label_to_id: &HashMap<String, u64>,
virtual_labels: &mut HashMap<String, u64>,
virtual_id: &mut u64,
) {
let parent_id = resolve_id(&node.label_name, label_to_id, virtual_labels, virtual_id);
for child in &node.subcategory {
let child_id =
resolve_id(&child.label_name, label_to_id, virtual_labels, virtual_id);
parent_map.insert(child_id, parent_id);
walk(child, parent_map, label_to_id, virtual_labels, virtual_id);
}
}
walk(
&root,
&mut parent_map,
label_to_id,
&mut virtual_labels,
&mut virtual_id,
);
let mut h = Self::from_parent_map(parent_map);
h.virtual_names = virtual_labels.into_iter().map(|(k, v)| (v, k)).collect();
Ok(h)
}
pub fn ancestors(&self, cat_id: u64) -> &[u64] {
self.ancestors_map
.get(&cat_id)
.map_or(&[], std::vec::Vec::as_slice)
}
pub fn children(&self, cat_id: u64) -> &[u64] {
self.children_map
.get(&cat_id)
.map_or(&[], std::vec::Vec::as_slice)
}
pub fn parent(&self, cat_id: u64) -> Option<u64> {
self.parent_map.get(&cat_id).copied()
}
pub fn name_of(&self, id: u64) -> Option<&str> {
self.virtual_names.get(&id).map(std::string::String::as_str)
}
pub fn all_ids(&self) -> Vec<u64> {
let mut ids: Vec<u64> = self.ancestors_map.keys().copied().collect();
ids.sort_unstable();
ids
}
}
#[derive(Deserialize)]
struct OidNode {
#[serde(rename = "LabelName")]
label_name: String,
#[serde(rename = "Subcategory", default)]
subcategory: Vec<OidNode>,
}