Skip to content

Commit

Permalink
op: Add Operation::Alt.
Browse files Browse the repository at this point in the history
This allows operations to react to conditions in a limited fashion, by
trying a sequence of child operations and taking the first successful
one. There are no tests because, right now, it’s tricky to make an
operation fail, which is what we need.
  • Loading branch information
kpreid committed Aug 20, 2024
1 parent 5a2fa26 commit 7179f43
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- `block::Block::with_inventory` attaches inventory to a block.
- `inv::InvInBlock`, stored in `block::BlockAttributes::inventory`, describes the size and rendering such inventories should have.
- `block::Modifier::Attributes` allows overriding block attributes.
- `op::Operation::Alt` allows operations to try alternatives.

### Changed

Expand Down
1 change: 1 addition & 0 deletions all-is-cubes/src/inv/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ impl From<op::OperationError> for ToolError {
// TODO: should not forget source()s but we can't do that generically in no_std for now;
// need more custom user-facing error processing.
op::OperationError::InternalConflict(c) => ToolError::Internal(c.to_string()),
op::OperationError::Unmatching => ToolError::NotUsable,
}
}
}
Expand Down
25 changes: 24 additions & 1 deletion all-is-cubes/src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ type OpTxn = (SpaceTransaction, InventoryTransaction);
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub enum Operation {
/// Try applying each of the contained [`Operation`]s and use the first successful one.
//---
// TODO: provide a way to control what error report is presented
Alt(Arc<[Operation]>),

/// Replace the cube's block with the given block.
///
/// In addition to this specific behavior, this has the semantics that it is “this block
Expand Down Expand Up @@ -98,6 +103,15 @@ impl Operation {
transform: Gridgid,
) -> Result<OpTxn, OperationError> {
match self {
Operation::Alt(ops) => {
for op in ops.iter() {
match op.apply(space, inventory, transform) {
Ok(txns) => return Ok(txns),
Err(_error) => {} // TODO: propagate certain fatal errors?
}
}
Err(OperationError::Unmatching)
}
Operation::Become(block) => {
let target_cube = transform.transform_cube(Cube::ORIGIN);
let space_txn = CubeTransaction::replacing(
Expand Down Expand Up @@ -184,6 +198,7 @@ impl Operation {
pub(crate) fn rotationally_symmetric(&self) -> bool {
match self {
// TODO: should ask if contained blocks are relevantly symmetric
Operation::Alt(ops) => ops.iter().all(Operation::rotationally_symmetric),
Operation::Become(_) => false,
Operation::DestroyTo(_) => false,
Operation::AddModifiers(_) => true, // TODO: there is not a general notion of rotating a modifier, but probably there should be
Expand All @@ -198,6 +213,9 @@ impl Operation {
}
match self {
// TODO: need to provide a way for blocks to opt out and have only one rotation
Operation::Alt(ops) => {
Operation::Alt(ops.iter().map(|op| op.clone().rotate(rotation)).collect())
}
Operation::Become(block) => Operation::Become(block.rotate(rotation)),
Operation::DestroyTo(block) => Operation::DestroyTo(block.rotate(rotation)),
// TODO: there is not a general notion of rotating a modifier, but probably there should be
Expand All @@ -223,6 +241,7 @@ impl Operation {
impl VisitHandles for Operation {
fn visit_handles(&self, visitor: &mut dyn crate::universe::HandleVisitor) {
match self {
Operation::Alt(ops) => ops[..].visit_handles(visitor),
Operation::Become(block) | Operation::DestroyTo(block) => block.visit_handles(visitor),
Operation::AddModifiers(modifier) => modifier.visit_handles(visitor),
Operation::StartMove(modifier) => modifier.visit_handles(visitor),
Expand All @@ -240,14 +259,18 @@ impl VisitHandles for Operation {
pub(crate) enum OperationError {
/// conflict between parts of the operation
InternalConflict(<OpTxn as Merge>::Conflict),

/// no rule of this operation matched
// TODO: include at least one nested error
Unmatching,
}

crate::util::cfg_should_impl_error! {
impl crate::util::ErrorIfStd for OperationError {
fn source(&self) -> Option<&(dyn crate::util::ErrorIfStd + 'static)> {
match self {
Self::InternalConflict(e) => Some(e),
// _ => None,
OperationError::Unmatching => None,
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions all-is-cubes/src/save/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,9 @@ mod op {
S: Serializer,
{
match self {
op::Operation::Alt(ops) => schema::OperationSer::AltV1 {
ops: Cow::Borrowed(&ops[..]),
},
op::Operation::Become(block) => schema::OperationSer::BecomeV1 {
block: block.clone(),
},
Expand Down Expand Up @@ -771,6 +774,7 @@ mod op {
D: Deserializer<'de>,
{
Ok(match schema::OperationSer::deserialize(deserializer)? {
schema::OperationSer::AltV1 { ops } => op::Operation::Alt(ops.into()),
schema::OperationSer::BecomeV1 { block } => op::Operation::Become(block),
schema::OperationSer::DestroyToV1 { block } => op::Operation::DestroyTo(block),
schema::OperationSer::AddModifiersV1 { modifiers } => op::Operation::AddModifiers(
Expand Down
3 changes: 3 additions & 0 deletions all-is-cubes/src/save/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,9 @@ type RgbaSer = [NotNan<f32>; 4];
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "type")]
pub(crate) enum OperationSer<'a> {
AltV1 {
ops: Cow<'a, [op::Operation]>,
},
BecomeV1 {
block: Block,
},
Expand Down

0 comments on commit 7179f43

Please sign in to comment.