use std::collections::BTreeMap;
use std::convert::Infallible;
use std::fmt;
use std::str::FromStr;
use miette::SourceSpan;
use serde::{Deserialize, Serialize};
use crate::description::{DescriptionTarget, TargetLink};
use crate::model::{ByteRange, Edge, EdgeId, Element, PlacementHint, Position, Shape};
use crate::path::ElementPath;
use crate::properties::{ComponentProperties, SpannedText};
use crate::unique_key_map::UniqueKeyMapVisitor;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct WorkflowId(String);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct StepId(String);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StepIndex(usize);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum StepItem {
Edge(EdgeId),
Element(ElementPath),
}
#[derive(Clone, Debug, PartialEq)]
pub struct ListedItem {
pub(crate) item: StepItem,
pub(crate) span: Option<ByteRange>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Step {
id: StepId,
title: String,
pub(crate) description: Option<String>,
pub(crate) items: Vec<ListedItem>,
span: Option<ByteRange>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Workflow {
id: WorkflowId,
title: String,
subtitle: Option<String>,
pub(crate) description: Option<String>,
pub(crate) overrides: BTreeMap<ElementPath, ElementOverride>,
pub(crate) edges: BTreeMap<EdgeId, Edge>,
pub(crate) steps: Vec<Step>,
span: Option<ByteRange>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(from = "ElementOverrideDocument", into = "ElementOverrideDocument")]
pub struct ElementOverride {
pub(crate) properties: ComponentProperties,
child_placement: Option<PlacementHint>,
position: Option<Position>,
span: Option<ByteRange>,
}
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct ElementOverrideDocument {
#[serde(default, skip_serializing_if = "Option::is_none")]
caption: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
shape: Option<Shape>,
#[serde(default, skip_serializing_if = "Option::is_none")]
height: Option<f64>,
#[serde(
default,
skip_serializing_if = "BTreeMap::is_empty",
deserialize_with = "UniqueKeyMapVisitor::deserialize"
)]
style: BTreeMap<String, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
child_placement: Option<PlacementHint>,
#[serde(default, skip_serializing_if = "Option::is_none")]
position: Option<Position>,
#[serde(default, skip_serializing_if = "Option::is_none")]
link: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
link_span: Option<ByteRange>,
#[serde(default, skip_serializing_if = "Option::is_none")]
code: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
code_span: Option<ByteRange>,
#[serde(
default,
skip_serializing_if = "BTreeMap::is_empty",
deserialize_with = "UniqueKeyMapVisitor::deserialize"
)]
metadata: BTreeMap<String, String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
span: Option<ByteRange>,
}
impl WorkflowId {
pub fn new(id: impl Into<String>) -> Self {
let id = id.into();
Self(id)
}
pub fn is_well_formed(&self) -> bool {
ElementPath::is_component_id(&self.0)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl StepId {
pub fn new(id: impl Into<String>) -> Self {
let id = id.into();
Self(id)
}
pub fn is_well_formed(&self) -> bool {
ElementPath::is_component_id(&self.0)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl StepIndex {
pub fn new(position: usize) -> Self {
Self(position)
}
pub fn position(self) -> usize {
self.0
}
}
impl ListedItem {
pub fn item(&self) -> &StepItem {
&self.item
}
pub fn source_span(&self) -> Option<SourceSpan> {
self.span.map(SourceSpan::from)
}
}
impl Step {
pub fn new(id: StepId, title: impl Into<String>) -> Self {
let title = title.into();
Self {
id,
title,
description: None,
items: Vec::new(),
span: None,
}
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
let description = description.into();
self.description = Some(description);
self
}
pub fn with_item(mut self, item: StepItem) -> Self {
let span = None;
let listed_item = ListedItem { item, span };
self.items.push(listed_item);
self
}
pub fn with_span(mut self, span: ByteRange) -> Self {
self.span = Some(span);
self
}
pub fn deepest_listed_depth(&self) -> usize {
let listed_paths = self.items().flat_map(|item| match item {
StepItem::Edge(id) => vec![id.from(), id.to()],
StepItem::Element(path) => vec![path],
});
let listed_depths = listed_paths.map(ElementPath::depth);
listed_depths.max().unwrap_or(0)
}
pub fn id(&self) -> &StepId {
&self.id
}
pub fn title(&self) -> &str {
&self.title
}
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn items(&self) -> impl Iterator<Item = &StepItem> {
self.items.iter().map(ListedItem::item)
}
pub fn listed_items(&self) -> &[ListedItem] {
&self.items
}
pub fn span(&self) -> Option<ByteRange> {
self.span
}
pub fn source_span(&self) -> Option<SourceSpan> {
self.span.map(SourceSpan::from)
}
}
impl Workflow {
pub fn new(id: WorkflowId, title: impl Into<String>) -> Self {
let title = title.into();
Self {
id,
title,
subtitle: None,
description: None,
overrides: BTreeMap::new(),
edges: BTreeMap::new(),
steps: Vec::new(),
span: None,
}
}
pub fn with_subtitle(mut self, subtitle: impl Into<String>) -> Self {
let subtitle = subtitle.into();
self.subtitle = Some(subtitle);
self
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
let description = description.into();
self.description = Some(description);
self
}
pub fn with_overrides(mut self, overrides: BTreeMap<ElementPath, ElementOverride>) -> Self {
self.overrides = overrides;
self
}
pub fn with_edges(mut self, edges: BTreeMap<EdgeId, Edge>) -> Self {
self.edges = edges;
self
}
pub fn with_step(mut self, step: Step) -> Self {
self.steps.push(step);
self
}
pub fn with_span(mut self, span: ByteRange) -> Self {
self.span = Some(span);
self
}
pub fn numbering_edge_targets_of(&self, description: &str) -> String {
TargetLink::respell_all(description, |target_link| {
let target = DescriptionTarget::parse(target_link.target)?;
let DescriptionTarget::UnnumberedEdge { from, to } = target else {
return None;
};
let only_edge = self.only_edge_between(&from, &to)?;
let numbered_target = DescriptionTarget::Edge(only_edge.clone());
let numbered_spelling = numbered_target.to_string();
Some(numbered_spelling)
})
}
pub fn only_edge_between(&self, from: &ElementPath, to: &ElementPath) -> Option<&EdgeId> {
let mut joining_edges = self.edges_between(from, to);
let only_edge = joining_edges.next()?;
let has_several_edges = joining_edges.next().is_some();
if has_several_edges {
return None;
}
Some(only_edge)
}
pub(crate) fn edges_between<'w>(
&'w self,
from: &ElementPath,
to: &ElementPath,
) -> impl Iterator<Item = &'w EdgeId> + use<'w> {
let from_path = from.clone();
let to_path = to.clone();
let parallel_ids = EdgeId::every_parallel_id(from_path, to_path);
let parallel_edges = self.edges.range(parallel_ids);
parallel_edges.map(|(id, _edge)| id)
}
pub fn step_index(&self, id: &StepId) -> Option<StepIndex> {
let mut steps = self.steps.iter();
let position = steps.position(|step| step.id() == id)?;
let index = StepIndex::new(position);
Some(index)
}
pub fn id(&self) -> &WorkflowId {
&self.id
}
pub fn title(&self) -> &str {
&self.title
}
pub fn subtitle(&self) -> Option<&str> {
self.subtitle.as_deref()
}
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn override_of(&self, path: &ElementPath) -> Option<&ElementOverride> {
self.overrides.get(path)
}
pub fn overrides(&self) -> &BTreeMap<ElementPath, ElementOverride> {
&self.overrides
}
pub fn edge(&self, id: &EdgeId) -> Option<&Edge> {
self.edges.get(id)
}
pub fn edges(&self) -> &BTreeMap<EdgeId, Edge> {
&self.edges
}
pub fn steps(&self) -> &[Step] {
&self.steps
}
pub fn step(&self, index: StepIndex) -> Option<&Step> {
let position = index.position();
self.steps.get(position)
}
pub fn span(&self) -> Option<ByteRange> {
self.span
}
pub fn source_span(&self) -> Option<SourceSpan> {
self.span.map(SourceSpan::from)
}
}
impl ElementOverride {
pub fn new() -> Self {
Self::default()
}
pub fn with_properties(mut self, properties: ComponentProperties) -> Self {
self.properties = properties;
self
}
pub fn with_child_placement(mut self, child_placement: PlacementHint) -> Self {
self.child_placement = Some(child_placement);
self
}
pub fn with_position(mut self, position: Position) -> Self {
self.position = Some(position);
self
}
pub fn with_span(mut self, span: ByteRange) -> Self {
self.span = Some(span);
self
}
pub(crate) fn applied_to(&self, element: &Element) -> Element {
let mut overridden = element.clone();
overridden.properties = element.properties.overridden_by(&self.properties);
overridden
}
pub fn properties(&self) -> &ComponentProperties {
&self.properties
}
pub fn child_placement(&self) -> Option<PlacementHint> {
self.child_placement
}
pub fn position(&self) -> Option<Position> {
self.position
}
pub fn span(&self) -> Option<ByteRange> {
self.span
}
pub fn source_span(&self) -> Option<SourceSpan> {
self.span.map(SourceSpan::from)
}
}
impl From<ElementOverrideDocument> for ElementOverride {
fn from(document: ElementOverrideDocument) -> Self {
let link = SpannedText::written(document.link, document.link_span);
let code = SpannedText::written(document.code, document.code_span);
let properties = ComponentProperties {
caption: document.caption,
shape: document.shape,
height: document.height,
style: document.style,
link,
code,
metadata: document.metadata,
};
Self {
properties,
child_placement: document.child_placement,
position: document.position,
span: document.span,
}
}
}
impl From<ElementOverride> for ElementOverrideDocument {
fn from(element_override: ElementOverride) -> Self {
let ComponentProperties {
caption,
shape,
height,
style,
link,
code,
metadata,
} = element_override.properties;
let (link, link_span) = SpannedText::unpaired(link);
let (code, code_span) = SpannedText::unpaired(code);
Self {
caption,
shape,
height,
style,
child_placement: element_override.child_placement,
position: element_override.position,
link,
link_span,
code,
code_span,
metadata,
span: element_override.span,
}
}
}
impl fmt::Display for WorkflowId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
impl FromStr for WorkflowId {
type Err = Infallible;
fn from_str(id: &str) -> Result<Self, Self::Err> {
let workflow_id = Self::new(id);
Ok(workflow_id)
}
}
impl fmt::Display for StepId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
impl fmt::Display for StepItem {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Edge(id) => write!(formatter, "edge `{id}`"),
Self::Element(path) => write!(formatter, "element `{path}`"),
}
}
}