feat(domain): stepwise Visibility state machine (transition_to + IllegalTransition)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,5 +13,5 @@ pub use authority::{Authority, AuthorityKind, AuthorityRef, NewAuthority};
|
|||||||
pub use field_definition::{FieldDefinition, FieldType, NewFieldDefinition};
|
pub use field_definition::{FieldDefinition, FieldType, NewFieldDefinition};
|
||||||
pub use id::{AuthorityId, FieldDefinitionId, ObjectId, OrgId, TermId, VocabularyId};
|
pub use id::{AuthorityId, FieldDefinitionId, ObjectId, OrgId, TermId, VocabularyId};
|
||||||
pub use label::{LocalizedLabel, pick_label};
|
pub use label::{LocalizedLabel, pick_label};
|
||||||
pub use object::{CatalogueObject, ObjectInput, Visibility};
|
pub use object::{CatalogueObject, IllegalTransition, ObjectInput, Visibility};
|
||||||
pub use vocabulary::{NewTerm, Term, TermRef, Vocabulary};
|
pub use vocabulary::{NewTerm, Term, TermRef, Vocabulary};
|
||||||
|
|||||||
@@ -35,6 +35,52 @@ impl Visibility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Visibility {
|
||||||
|
/// Whether `self` may move directly to `target`. Legal single steps are
|
||||||
|
/// `draft↔internal` and `internal↔public`; `draft↔public` is not one step.
|
||||||
|
pub fn can_transition_to(self, target: Visibility) -> bool {
|
||||||
|
use Visibility::*;
|
||||||
|
|
||||||
|
matches!(
|
||||||
|
(self, target),
|
||||||
|
(Draft, Internal) | (Internal, Draft) | (Internal, Public) | (Public, Internal)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate a stepwise transition to `target`. Setting to the current value is an
|
||||||
|
/// idempotent no-op (`Ok`). A forbidden jump returns [`IllegalTransition`].
|
||||||
|
pub fn transition_to(self, target: Visibility) -> Result<Visibility, IllegalTransition> {
|
||||||
|
if self == target || self.can_transition_to(target) {
|
||||||
|
Ok(target)
|
||||||
|
} else {
|
||||||
|
Err(IllegalTransition {
|
||||||
|
from: self,
|
||||||
|
to: target,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An attempted visibility change the state machine forbids.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct IllegalTransition {
|
||||||
|
pub from: Visibility,
|
||||||
|
pub to: Visibility,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for IllegalTransition {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"illegal visibility transition: {} -> {}",
|
||||||
|
self.from.as_str(),
|
||||||
|
self.to.as_str()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for IllegalTransition {}
|
||||||
|
|
||||||
/// The mutable inventory-minimum fields of a catalogue object.
|
/// The mutable inventory-minimum fields of a catalogue object.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct ObjectInput {
|
pub struct ObjectInput {
|
||||||
@@ -107,4 +153,39 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stepwise_transitions_are_legal() {
|
||||||
|
use Visibility::*;
|
||||||
|
assert_eq!(Draft.transition_to(Internal), Ok(Internal));
|
||||||
|
assert_eq!(Internal.transition_to(Public), Ok(Public));
|
||||||
|
assert_eq!(Public.transition_to(Internal), Ok(Internal));
|
||||||
|
assert_eq!(Internal.transition_to(Draft), Ok(Draft));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn skipping_a_step_is_illegal() {
|
||||||
|
use Visibility::*;
|
||||||
|
assert_eq!(
|
||||||
|
Draft.transition_to(Public),
|
||||||
|
Err(IllegalTransition {
|
||||||
|
from: Draft,
|
||||||
|
to: Public
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Public.transition_to(Draft),
|
||||||
|
Err(IllegalTransition {
|
||||||
|
from: Public,
|
||||||
|
to: Draft
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn setting_to_current_value_is_a_noop_ok() {
|
||||||
|
for v in [Visibility::Draft, Visibility::Internal, Visibility::Public] {
|
||||||
|
assert_eq!(v.transition_to(v), Ok(v));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user