use alpha2_media::SourceId;
use alpha2_model::{
ClipBlendMode, ClipEffect, ClipId, CompositionId, ProjectId, TrackId, TrackKind,
VisualProperties,
};
use alpha2_time::TimeRange;
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum EvalError {
InvalidProject(alpha2_model::ModelError),
UnmappableClip {
clip: ClipId,
},
IncompleteComposition {
clip: ClipId,
composition: CompositionId,
reason: crate::PlanError,
},
IndexProjectMismatch {
index_project: ProjectId,
evaluated_project: ProjectId,
},
}
impl std::error::Error for EvalError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::InvalidProject(error) => Some(error),
_ => None,
}
}
}
impl fmt::Display for EvalError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidProject(error) => {
write!(formatter, "cannot index invalid project: {error}")
}
Self::UnmappableClip { clip } => {
write!(
formatter,
"clip {clip} cannot be mapped to a source timestamp"
)
}
Self::IncompleteComposition {
clip,
composition,
reason,
} => match reason {
crate::PlanError::NoOutput => write!(
formatter,
"composition {composition} of clip {clip} has no output"
),
crate::PlanError::MissingInput { node, input } => write!(
formatter,
"input {input} of node {node} in composition {composition} of clip {clip} is not connected"
),
crate::PlanError::UnboundGroupInput { node } => write!(
formatter,
"group input {node} in composition {composition} of clip {clip} is fed by no group node"
),
crate::PlanError::ClipWithoutClip { node } => write!(
formatter,
"clip node {node} in composition {composition}, drawn as clip {clip}'s source, shows no clip"
),
crate::PlanError::NestedTooDeep { node } => write!(
formatter,
"node {node} in composition {composition} of clip {clip} nests compositions more than {} deep",
alpha2_model::MAX_COMPOSITION_DEPTH
),
crate::PlanError::TooLarge => write!(
formatter,
"composition {composition} of clip {clip} draws more than {} nodes, or the project's compositions hold more than {} bytes once inlined",
alpha2_model::MAX_INLINED_NODES,
crate::MAX_PLAN_BYTES
),
},
Self::IndexProjectMismatch {
index_project,
evaluated_project,
} => {
write!(
formatter,
"index belongs to project {index_project}, not {evaluated_project}"
)
}
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct IndexedClip {
pub(crate) clip_id: ClipId,
pub(crate) source_id: SourceId,
pub(crate) track_id: TrackId,
pub(crate) track_kind: TrackKind,
pub(crate) timeline_range: TimeRange,
pub(crate) source_range: TimeRange,
pub(crate) time_map: alpha2_model::TimeMap,
pub(crate) visual: VisualProperties,
pub(crate) composition: Option<crate::IndexedComposition>,
pub(crate) blend_mode: ClipBlendMode,
pub(crate) cache: alpha2_model::CacheIntent,
pub(crate) masks: Vec<alpha2_model::ShapeMask>,
pub(crate) matte: Option<alpha2_model::Matte>,
pub(crate) is_matte: bool,
pub(crate) audio: alpha2_model::AudioProperties,
pub(crate) source_is_audible: bool,
pub(crate) generator: Option<alpha2_model::Generator>,
pub(crate) plan: Option<std::sync::Arc<crate::GeneratorPlan>>,
pub(crate) source_composition: Option<(alpha2_model::CompositionId, crate::index::PlanSlot)>,
pub(crate) track_order: usize,
pub(crate) clip_order: usize,
}
#[derive(Clone, Debug)]
pub(crate) struct IndexedTransition {
pub(crate) from_clip: ClipId,
pub(crate) to_clip: ClipId,
pub(crate) timeline_range: TimeRange,
pub(crate) easing: alpha2_model::Easing,
pub(crate) effect: ClipEffect,
pub(crate) track_order: usize,
}
#[derive(Clone, Debug)]
pub(crate) struct IndexedCaptions {
pub(crate) track_id: TrackId,
pub(crate) track_order: usize,
pub(crate) captions: alpha2_model::Captions,
}