use itertools::Itertools;
use petgraph::{Direction, graph::NodeIndex, visit::EdgeRef};
use crate::ir::{
graph::{CookedGraph, GraphEdge},
types::{GraphContainer, GraphInlineType, GraphSchemaType, GraphType},
};
use super::{TypeView, ViewNode};
#[derive(Debug)]
pub enum ContainerView<'a> {
Array(InnerView<'a>),
Map(InnerView<'a>),
Optional(InnerView<'a>),
}
impl<'a> ContainerView<'a> {
#[inline]
pub fn ty(&self) -> TypeView<'a> {
TypeView::new(self.cooked(), self.index())
}
}
impl<'a> ViewNode<'a> for ContainerView<'a> {
#[inline]
fn cooked(&self) -> &'a CookedGraph<'a> {
let (Self::Array(c) | Self::Map(c) | Self::Optional(c)) = self;
c.cooked
}
#[inline]
fn index(&self) -> NodeIndex<usize> {
let (Self::Array(c) | Self::Map(c) | Self::Optional(c)) = self;
c.container
}
}
#[derive(Debug)]
pub struct InnerView<'a> {
cooked: &'a CookedGraph<'a>,
container: NodeIndex<usize>,
inner: NodeIndex<usize>,
}
impl<'a> InnerView<'a> {
#[inline]
pub fn ty(&self) -> TypeView<'a> {
TypeView::new(self.cooked, self.inner)
}
#[inline]
pub fn description(&self) -> Option<&'a str> {
match self.cooked.graph[self.container] {
GraphType::Schema(GraphSchemaType::Container(
_,
GraphContainer::Array { description }
| GraphContainer::Map { description }
| GraphContainer::Optional { description },
))
| GraphType::Inline(GraphInlineType::Container(
_,
GraphContainer::Array { description }
| GraphContainer::Map { description }
| GraphContainer::Optional { description },
)) => description,
_ => None,
}
}
}
impl<'a> ContainerView<'a> {
#[inline]
pub(in crate::ir) fn new(
cooked: &'a CookedGraph<'a>,
index: NodeIndex<usize>,
container: GraphContainer<'a>,
) -> Self {
let inner = cooked
.graph
.edges_directed(index, Direction::Outgoing)
.filter(|e| matches!(e.weight(), GraphEdge::Contains))
.map(|e| e.target())
.exactly_one()
.unwrap();
let inner = InnerView {
cooked,
container: index,
inner,
};
match container {
GraphContainer::Array { .. } => Self::Array(inner),
GraphContainer::Map { .. } => Self::Map(inner),
GraphContainer::Optional { .. } => Self::Optional(inner),
}
}
}