use std::collections::{HashMap, HashSet};
use crate::ar::{ResolvedPath, Resolver};
use crate::sdf::schema::FieldKey;
use crate::sdf::{self, AbstractData, LayerOffset, Path, RelocateList, Value};
use super::mapping::MapFunction;
use super::prim_index::find_layer_id;
use super::relocates::{chain_through_relocates, validate_layer_relocates};
use super::{effective_time_codes_per_second, Error};
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
pub struct LayerId(u32);
impl LayerId {
pub(crate) const INVALID: LayerId = LayerId(u32::MAX);
pub(crate) const fn from_raw(raw: u32) -> Self {
Self(raw)
}
}
pub(crate) struct LayerNode {
pub layer: sdf::Layer,
pub children: Vec<(LayerId, LayerOffset)>,
pub relocates: RelocateList,
}
impl LayerNode {
fn new(layer: sdf::Layer) -> Self {
Self {
layer,
children: Vec::new(),
relocates: Vec::new(),
}
}
}
pub(crate) struct LayerGraph {
nodes: HashMap<LayerId, LayerNode>,
by_identifier: HashMap<String, LayerId>,
next_id: u32,
order: Vec<LayerId>,
session_layer_count: usize,
root: Option<LayerId>,
sublayer_stacks: HashMap<LayerId, Vec<(LayerId, LayerOffset)>>,
root_stack: Vec<(LayerId, LayerOffset)>,
has_relocates: bool,
cycle_errors: Vec<Error>,
relocate_errors: Vec<Error>,
resolver: Box<dyn Resolver>,
load_payloads: bool,
}
impl LayerGraph {
pub(crate) fn from_layers(
layers: Vec<sdf::Layer>,
session_layer_count: usize,
resolver: Box<dyn Resolver>,
load_payloads: bool,
) -> Self {
let mut graph = Self {
nodes: HashMap::new(),
by_identifier: HashMap::new(),
next_id: 0,
order: Vec::with_capacity(layers.len()),
session_layer_count: 0,
root: None,
sublayer_stacks: HashMap::new(),
root_stack: Vec::new(),
has_relocates: false,
cycle_errors: Vec::new(),
relocate_errors: Vec::new(),
resolver,
load_payloads,
};
let mut root = None;
let mut session_count = 0;
for (i, layer) in layers.into_iter().enumerate() {
let (id, fresh) = graph.intern(layer);
if i == session_layer_count {
root = Some(id);
}
if fresh && i < session_layer_count {
session_count += 1;
}
}
graph.session_layer_count = session_count;
graph.root = root;
graph.build_sublayer_edges();
graph.recompute_relocates();
graph
}
fn intern(&mut self, layer: sdf::Layer) -> (LayerId, bool) {
if let Some(&id) = self.by_identifier.get(layer.identifier()) {
return (id, false);
}
let id = LayerId::from_raw(self.next_id);
self.next_id += 1;
self.by_identifier.insert(layer.identifier().to_string(), id);
self.nodes.insert(id, LayerNode::new(layer));
self.order.push(id);
(id, true)
}
fn build_sublayer_edges(&mut self) {
for node in self.nodes.values_mut() {
node.children.clear();
}
let root_path = Path::abs_root();
let mut all_edges: Vec<(LayerId, Vec<(LayerId, LayerOffset)>)> = Vec::new();
for &id in &self.order {
let node = &self.nodes[&id];
let parent_tcps = effective_time_codes_per_second(&node.layer);
let Ok(Value::StringVec(sub_paths)) = node
.layer
.get(&root_path, FieldKey::SubLayers.as_str())
.map(|v| v.into_owned())
else {
continue;
};
let offsets: Vec<LayerOffset> = node
.layer
.get(&root_path, FieldKey::SubLayerOffsets.as_str())
.ok()
.and_then(|v| match v.into_owned() {
Value::LayerOffsetVec(v) => Some(v),
_ => None,
})
.unwrap_or_default();
let mut edges = Vec::new();
for (i, sub_path) in sub_paths.into_iter().enumerate() {
let Some(sub_id) = self.find(&sub_path) else {
continue;
};
let ratio = parent_tcps / effective_time_codes_per_second(&self.nodes[&sub_id].layer);
let effective = offsets
.get(i)
.copied()
.unwrap_or_default()
.sanitized()
.concatenate(&LayerOffset::scale_only(ratio));
edges.push((sub_id, effective));
}
all_edges.push((id, edges));
}
for (id, edges) in all_edges {
self.nodes.get_mut(&id).expect("edge source exists").children = edges;
}
self.rebuild_sublayer_stacks();
let mut errors = Vec::new();
if let Some(root) = self.root {
let mut ancestors = HashSet::new();
self.detect_cycles(root, &mut ancestors, &mut errors);
}
self.cycle_errors = errors;
}
fn rebuild_sublayer_stacks(&mut self) {
let stacks: HashMap<LayerId, Vec<(LayerId, LayerOffset)>> = self
.order
.iter()
.map(|&id| {
let mut stack = Vec::new();
let mut ancestors = HashSet::new();
self.collect_sublayers(id, LayerOffset::IDENTITY, &mut stack, &mut ancestors);
(id, stack)
})
.collect();
self.sublayer_stacks = stacks;
let mut root_stack: Vec<(LayerId, LayerOffset)> = self
.session_layers()
.iter()
.map(|&id| (id, LayerOffset::IDENTITY))
.collect();
if let Some(root) = self.root {
root_stack.extend_from_slice(self.sublayer_stack(root));
}
self.root_stack = root_stack;
}
fn detect_cycles(&self, id: LayerId, ancestors: &mut HashSet<LayerId>, errors: &mut Vec<Error>) {
ancestors.insert(id);
for &(child, _) in &self.nodes[&id].children {
if ancestors.contains(&child) {
errors.push(Error::SublayerCycle {
root_layer: self.nodes[&id].layer.identifier().to_string(),
seen_layer: self.nodes[&child].layer.identifier().to_string(),
});
continue;
}
self.detect_cycles(child, ancestors, errors);
}
ancestors.remove(&id);
}
pub(crate) fn get(&self, id: LayerId) -> Option<&LayerNode> {
self.nodes.get(&id)
}
pub(crate) fn get_mut(&mut self, id: LayerId) -> Option<&mut LayerNode> {
self.nodes.get_mut(&id)
}
pub(crate) fn layer(&self, id: LayerId) -> &sdf::Layer {
&self.nodes[&id].layer
}
pub(crate) fn identifier(&self, id: LayerId) -> &str {
self.nodes[&id].layer.identifier()
}
pub(crate) fn contains(&self, id: LayerId) -> bool {
self.nodes.contains_key(&id)
}
pub(crate) fn resolver(&self) -> &dyn Resolver {
self.resolver.as_ref()
}
pub(crate) fn anchor_location(&self, anchor: Option<LayerId>) -> Option<ResolvedPath> {
anchor.and_then(|layer| self.resolver().resolve(self.identifier(layer)))
}
pub(crate) fn load_payloads(&self) -> bool {
self.load_payloads
}
pub(crate) fn has_relocates(&self) -> bool {
self.has_relocates
}
pub(crate) fn len(&self) -> usize {
self.nodes.len()
}
#[allow(dead_code)]
pub(crate) fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub(crate) fn session_layer_count(&self) -> usize {
self.session_layer_count
}
pub(crate) fn session_layers(&self) -> &[LayerId] {
&self.order[..self.session_layer_count]
}
pub(crate) fn root_id(&self) -> Option<LayerId> {
self.root
}
pub(crate) fn root_layer(&self) -> Option<&sdf::Layer> {
self.root.map(|id| self.layer(id))
}
pub(crate) fn all_ids(&self) -> &[LayerId] {
&self.order
}
pub(crate) fn identifiers(&self) -> Vec<String> {
self.identifiers_of(self.order.iter().copied())
}
pub(crate) fn identifiers_of(&self, ids: impl IntoIterator<Item = LayerId>) -> Vec<String> {
ids.into_iter().map(|id| self.identifier(id).to_string()).collect()
}
pub(crate) fn root_layer_stack_identifiers(&self) -> Vec<String> {
self.identifiers_of(self.root_layer_stack().iter().map(|&(id, _)| id))
}
pub(crate) fn find(&self, asset_path: &str) -> Option<LayerId> {
find_layer_id(asset_path, &self.order, |id| self.layer(id), self.resolver.as_ref())
}
pub(crate) fn sublayer_stack(&self, root_layer: LayerId) -> &[(LayerId, LayerOffset)] {
self.sublayer_stacks
.get(&root_layer)
.map(Vec::as_slice)
.unwrap_or_default()
}
fn collect_sublayers(
&self,
id: LayerId,
effective: LayerOffset,
stack: &mut Vec<(LayerId, LayerOffset)>,
ancestors: &mut HashSet<LayerId>,
) {
stack.push((id, effective));
ancestors.insert(id);
for &(child, edge_offset) in &self.nodes[&id].children {
if ancestors.contains(&child) {
continue;
}
self.collect_sublayers(child, effective.concatenate(&edge_offset), stack, ancestors);
}
ancestors.remove(&id);
}
pub(crate) fn root_layer_stack(&self) -> &[(LayerId, LayerOffset)] {
&self.root_stack
}
pub(crate) fn local_layers(&self) -> HashSet<LayerId> {
self.root_layer_stack().iter().map(|&(id, _)| id).collect()
}
pub(crate) fn recompute_sublayers(&mut self) {
self.build_sublayer_edges();
self.recompute_relocates();
}
pub(crate) fn recompute_relocates(&mut self) {
let (validated, errors) = validate_layer_relocates(self);
self.has_relocates = false;
for &id in &self.order {
let pairs = validated.get(&id).cloned().unwrap_or_default();
self.has_relocates |= !pairs.is_empty();
self.nodes.get_mut(&id).expect("layer node exists").relocates = pairs;
}
self.relocate_errors = errors;
}
pub(crate) fn errors(&self) -> Vec<Error> {
self.cycle_errors.iter().chain(&self.relocate_errors).cloned().collect()
}
pub(crate) fn relocate_conflict_scopes(&self) -> Vec<HashSet<LayerId>> {
let mut scopes: Vec<HashSet<LayerId>> = self
.order
.iter()
.map(|&id| self.sublayer_stack(id).iter().map(|&(l, _)| l).collect())
.collect();
scopes.push(self.local_layers());
scopes
}
pub(crate) fn relocation_source(&self, ambient: &[(LayerId, LayerOffset)], target: &Path) -> Option<Path> {
for &(layer, _) in ambient {
let Some(node) = self.nodes.get(&layer) else {
continue;
};
if let Some((source, _)) = node.relocates.iter().find(|(_, t)| !t.is_empty() && t == target) {
return Some(source.clone());
}
}
None
}
pub(crate) fn relocates_expression_at(
&self,
ambient: &[(LayerId, LayerOffset)],
path: &Path,
exclude_source: Option<&Path>,
) -> Option<MapFunction> {
let mut pairs: Vec<(Path, Path)> = self
.combined_relocates(ambient)
.into_iter()
.filter(|(source, target)| !target.is_empty() && source.has_prefix(path) && Some(source) != exclude_source)
.collect();
if pairs.is_empty() {
return None;
}
pairs.push((Path::abs_root(), Path::abs_root()));
Some(MapFunction::new(pairs))
}
fn incremental_relocates(&self, ambient: &[(LayerId, LayerOffset)]) -> RelocateList {
let mut pairs: RelocateList = Vec::new();
for &(layer, _) in ambient {
let Some(node) = self.nodes.get(&layer) else {
continue;
};
for (source, target) in &node.relocates {
if !pairs.iter().any(|(s, _)| s == source) {
pairs.push((source.clone(), target.clone()));
}
}
}
pairs
}
pub(crate) fn combined_relocates(&self, ambient: &[(LayerId, LayerOffset)]) -> RelocateList {
let mut pairs = self.incremental_relocates(ambient);
let snapshot = pairs.clone();
for (source, target) in pairs.iter_mut() {
if target.is_empty() {
continue;
}
*target = chain_through_relocates(target, &snapshot, Some(source));
}
for _ in 0..pairs.len() {
let mut added: Vec<(Path, Path)> = Vec::new();
for (s1, t1) in &pairs {
if t1.is_empty() {
continue;
}
for (s2, t2) in &pairs {
if s2 == s1 {
continue;
}
let Some(combined) = s2.replace_prefix(t1, s1) else {
continue;
};
if !pairs.iter().any(|(s, _)| *s == combined) && !added.iter().any(|(s, _)| *s == combined) {
added.push((combined, t2.clone()));
}
}
}
if added.is_empty() {
break;
}
pairs.extend(added);
}
pairs
}
pub(crate) fn is_relocation_source(&self, ambient: &[(LayerId, LayerOffset)], source: &Path) -> bool {
self.relocation_source_layer(ambient, source).is_some()
}
pub(crate) fn relocation_source_layer(&self, ambient: &[(LayerId, LayerOffset)], source: &Path) -> Option<&str> {
ambient.iter().find_map(|&(layer, _)| {
self.nodes
.get(&layer)
.filter(|node| node.relocates.iter().any(|(s, _)| s == source))
.map(|node| node.layer.identifier())
})
}
pub(crate) fn ensure_layer(&mut self, layer: sdf::Layer) -> LayerId {
self.intern(layer).0
}
pub(crate) fn id_of(&self, identifier: &str) -> Option<LayerId> {
self.by_identifier.get(identifier).copied()
}
}