Text Projection: every graph id projects, because graph_id! says so
`TransactionId` had no `TextValue`, which blocked the operation layer: it is a core type, so epiphany-ops cannot implement a core trait for it. The fix is not the missing line. `bytes_text_value!` was a hand-maintained list of thirty ids sitting parallel to the `graph_id!` invocations that declare them -- the same shape that has cost this project four bugs -- and `TransactionId`'s absence was that latent bug already biting. `graph_id!` now generates the `TextValue` impl beside the id itself: every graph id is a byte-string leaf by definition, so the list that declares them is the list that projects them. `bytes_text_value!` keeps only the leaves that are genuinely not graph ids -- `ContentHash`, `TypedObjectId`, `ReplicaId`, `OperationId` -- shrinking from thirty entries to four. A graph id added tomorrow gets its projection for free and cannot be forgotten. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cf81074ca0
commit
b6728da5c6
|
|
@ -244,6 +244,34 @@ macro_rules! graph_id {
|
|||
}
|
||||
// Canonical order *is* byte order for typed identifiers (Appendix D).
|
||||
impl CanonicalByteOrder for $name {}
|
||||
|
||||
impl crate::textvalue::TextValue for $name {
|
||||
fn project(&self) -> crate::textvalue::Sexp {
|
||||
crate::textvalue::Sexp::Bytes(
|
||||
<Self as CanonicalEncode>::to_canonical_bytes(self),
|
||||
)
|
||||
}
|
||||
|
||||
fn parse(
|
||||
s: &crate::textvalue::Sexp,
|
||||
) -> Result<Self, crate::textvalue::TextError> {
|
||||
let crate::textvalue::Sexp::Bytes(bytes) = s else {
|
||||
return Err(crate::textvalue::TextError::Expected {
|
||||
expected: stringify!($name),
|
||||
found: crate::textvalue_impls::class_of(s),
|
||||
});
|
||||
};
|
||||
let value = <Self as CanonicalDecode>::decode_canonical(bytes)
|
||||
.map_err(|_| crate::textvalue::TextError::NotCanonical(stringify!($name)))?;
|
||||
if value.to_canonical_bytes() != *bytes {
|
||||
return Err(crate::textvalue::TextError::NotCanonical(concat!(
|
||||
stringify!($name),
|
||||
" is not canonically encoded"
|
||||
)));
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,34 +77,8 @@ pub(crate) fn class_of(s: &Sexp) -> &'static str {
|
|||
bytes_text_value! {
|
||||
crate::ids::ReplicaId => "ReplicaId",
|
||||
crate::ids::OperationId => "OperationId",
|
||||
crate::ids::EventId => "EventId",
|
||||
crate::ids::PitchId => "PitchId",
|
||||
crate::ids::VoiceId => "VoiceId",
|
||||
crate::ids::StaffId => "StaffId",
|
||||
crate::ids::StaffInstanceId => "StaffInstanceId",
|
||||
crate::ids::StaffGroupId => "StaffGroupId",
|
||||
crate::ids::RegionId => "RegionId",
|
||||
crate::ids::InstrumentId => "InstrumentId",
|
||||
crate::ids::PartDefinitionId => "PartDefinitionId",
|
||||
crate::ids::MeasureId => "MeasureId",
|
||||
crate::ids::BarlineAlignmentGroupId => "BarlineAlignmentGroupId",
|
||||
crate::ids::SlurId => "SlurId",
|
||||
crate::ids::TieId => "TieId",
|
||||
crate::ids::BeamId => "BeamId",
|
||||
crate::ids::SpannerId => "SpannerId",
|
||||
crate::ids::TupletId => "TupletId",
|
||||
crate::ids::MarkerId => "MarkerId",
|
||||
crate::ids::AnalyticalAnnotationId => "AnalyticalAnnotationId",
|
||||
crate::ids::CommentId => "CommentId",
|
||||
crate::ids::RepeatStructureId => "RepeatStructureId",
|
||||
crate::ids::LyricLineId => "LyricLineId",
|
||||
crate::ids::ChordSymbolId => "ChordSymbolId",
|
||||
crate::ids::GraphicObjectId => "GraphicObjectId",
|
||||
crate::ids::GraphicGestureId => "GraphicGestureId",
|
||||
crate::ids::TimeSignatureId => "TimeSignatureId",
|
||||
crate::ids::AnalysisLayerId => "AnalysisLayerId",
|
||||
crate::ids::ViewId => "ViewId",
|
||||
ContentHash => "ContentHash",
|
||||
crate::ids::TypedObjectId => "TypedObjectId",
|
||||
}
|
||||
|
||||
/// A `CanonicalF64` is its eight canonical little-endian IEEE 754 bytes, never a
|
||||
|
|
|
|||
Loading…
Reference in New Issue