use monospace_diagram::{
Anchor, Delta, Diagram, Direction, End, Reference, Shape, ShapeId, Stroke, Terminal, Text,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Command {
Move {
id: ShapeId,
by: Delta,
},
Resize {
id: ShapeId,
by: Delta,
},
RotateLine {
id: ShapeId,
},
SetStroke {
id: ShapeId,
stroke: Stroke,
},
SetText {
id: ShapeId,
text: Option<Text>,
},
InvertTerminals {
id: ShapeId,
},
MoveEndpoint {
id: ShapeId,
end: End,
by: Delta,
out: i16,
},
ResetEndpoint {
id: ShapeId,
end: End,
},
SetEndpointTerminal {
id: ShapeId,
end: End,
terminal: Terminal,
},
DetachEndpoint {
id: ShapeId,
end: End,
},
SetEndpointLeaving {
id: ShapeId,
end: End,
leaving: Direction,
},
Hang {
id: ShapeId,
end: End,
target: ShapeId,
anchor: Anchor,
},
Forward {
id: ShapeId,
},
Backward {
id: ShapeId,
},
ToFront {
id: ShapeId,
},
ToBack {
id: ShapeId,
},
Add {
shape: Shape,
},
Remove {
id: ShapeId,
},
}
impl Command {
pub(crate) fn apply_to(&self, diagram: &Diagram) -> Option<Diagram> {
match *self {
Self::Add { ref shape } => Some(diagram.add(shape.clone()).0),
Self::Move { id, by } => changed(diagram, id, |shape| shape.displaced_by(by)),
Self::Resize { id, by } => changed(diagram, id, |shape| shape.resized_by(by)),
Self::RotateLine { id } => changed(diagram, id, Shape::with_rotated_orientation),
Self::SetStroke { id, ref stroke } => {
let stroke = stroke.clone();
changed(diagram, id, |shape| shape.with_stroke(stroke))
}
Self::SetText { id, ref text } => {
let text = text.clone();
changed(diagram, id, |shape| shape.with_text(text))
}
Self::InvertTerminals { id } => changed(diagram, id, Shape::with_inverted_terminals),
Self::MoveEndpoint { id, end, by, out } => {
changed(diagram, id, |shape| shape.with_endpoint_moved(end, by, out))
}
Self::ResetEndpoint { id, end } => {
changed(diagram, id, |shape| shape.with_endpoint_reset(end))
}
Self::SetEndpointTerminal {
id,
end,
ref terminal,
} => {
let terminal = terminal.clone();
changed(diagram, id, |shape| {
shape.with_endpoint_terminal(end, terminal)
})
}
Self::DetachEndpoint { id, end } => changed(diagram, id, |shape| {
shape.with_endpoint_detached(end, diagram)
}),
Self::SetEndpointLeaving { id, end, leaving } => changed(diagram, id, |shape| {
shape.with_endpoint_leaving(end, leaving)
}),
Self::Hang {
id,
end,
target,
anchor,
} => changed(diagram, id, |shape| {
shape.with_endpoint_attached(
end,
Reference::new(target, anchor, Delta::ZERO, 0),
diagram,
)
}),
Self::Forward { id } => diagram.can_forward(id).then(|| diagram.forward(id)),
Self::Backward { id } => diagram.can_backward(id).then(|| diagram.backward(id)),
Self::ToFront { id } => diagram.can_forward(id).then(|| diagram.to_front(id)),
Self::ToBack { id } => diagram.can_backward(id).then(|| diagram.to_back(id)),
Self::Remove { id } => diagram.get(id).map(|_| diagram.remove(id)),
}
}
}
fn changed(
diagram: &Diagram,
id: ShapeId,
change: impl FnOnce(&Shape) -> Shape,
) -> Option<Diagram> {
let before = diagram.get(id)?;
let after = change(before);
(after != *before).then(|| diagram.replace(id, after))
}