1mod fragment;
8mod migration;
9mod proposal;
10mod value;
11mod wire;
12
13use crate::{
14 EntityFragment, EntitySourceKey, FieldFragment, FieldSourceKey, MAX_SCHEMA_FRAGMENT_BYTES,
15 MAX_SCHEMA_MIGRATION_PLAN_BYTES, MAX_SCHEMA_PROPOSAL_BYTES, NamedTypeFragment,
16 SchemaContractError, SchemaFragment, SchemaMigrationPlan, SchemaProposal,
17};
18
19use self::{
20 fragment::{
21 decode_fragment_payload, encode_entity, encode_field, encode_fragment_payload,
22 encode_named_type,
23 },
24 migration::{decode_migration_payload, encode_migration_payload},
25 proposal::{decode_proposal_payload, encode_proposal_payload},
26 wire::{WireReader, WireWriter},
27};
28
29const FRAGMENT_HEADER: &[u8; 5] = b"ICYF\x01";
30const PROPOSAL_HEADER: &[u8; 5] = b"ICYP\x01";
31const MIGRATION_HEADER: &[u8; 5] = b"ICYM\x01";
32const SOURCE_MEANING_HEADER: &[u8; 5] = b"ICYS\x01";
33
34pub fn encode_schema_fragment(fragment: &SchemaFragment) -> Result<Vec<u8>, SchemaContractError> {
41 fragment.validate()?;
42 let mut writer = WireWriter::new(MAX_SCHEMA_FRAGMENT_BYTES);
43 writer.push_raw(FRAGMENT_HEADER)?;
44 encode_fragment_payload(&mut writer, fragment)?;
45 Ok(writer.finish())
46}
47
48pub fn decode_schema_fragment(bytes: &[u8]) -> Result<SchemaFragment, SchemaContractError> {
54 ensure_input_bound(bytes, MAX_SCHEMA_FRAGMENT_BYTES)?;
55 let mut reader = WireReader::new(bytes);
56 reader.expect_raw(FRAGMENT_HEADER)?;
57 let fragment = decode_fragment_payload(&mut reader)?;
58 reader.finish()?;
59 fragment.validate()?;
60 ensure_canonical(bytes, encode_schema_fragment(&fragment)?)?;
61 Ok(fragment)
62}
63
64pub fn encode_schema_proposal(proposal: &SchemaProposal) -> Result<Vec<u8>, SchemaContractError> {
71 proposal.validate_current()?;
72 let mut writer = WireWriter::new(MAX_SCHEMA_PROPOSAL_BYTES);
73 writer.push_raw(PROPOSAL_HEADER)?;
74 encode_proposal_payload(&mut writer, proposal)?;
75 Ok(writer.finish())
76}
77
78pub fn decode_schema_proposal(bytes: &[u8]) -> Result<SchemaProposal, SchemaContractError> {
85 ensure_input_bound(bytes, MAX_SCHEMA_PROPOSAL_BYTES)?;
86 let mut reader = WireReader::new(bytes);
87 reader.expect_raw(PROPOSAL_HEADER)?;
88 let proposal = decode_proposal_payload(&mut reader)?;
89 reader.finish()?;
90 proposal.validate_current()?;
91 ensure_canonical(bytes, encode_schema_proposal(&proposal)?)?;
92 Ok(proposal)
93}
94
95pub fn encode_schema_migration_plan(
101 plan: &SchemaMigrationPlan,
102) -> Result<Vec<u8>, SchemaContractError> {
103 plan.validate()?;
104 let mut writer = WireWriter::new(MAX_SCHEMA_MIGRATION_PLAN_BYTES);
105 writer.push_raw(MIGRATION_HEADER)?;
106 encode_migration_payload(&mut writer, plan)?;
107 Ok(writer.finish())
108}
109
110pub fn decode_schema_migration_plan(
117 bytes: &[u8],
118) -> Result<SchemaMigrationPlan, SchemaContractError> {
119 ensure_input_bound(bytes, MAX_SCHEMA_MIGRATION_PLAN_BYTES)?;
120 let mut reader = WireReader::new(bytes);
121 reader.expect_raw(MIGRATION_HEADER)?;
122 let plan = decode_migration_payload(&mut reader)?;
123 reader.finish()?;
124 plan.validate()?;
125 ensure_canonical(bytes, encode_schema_migration_plan(&plan)?)?;
126 Ok(plan)
127}
128
129pub(crate) use migration::encode_migration_transitions_for_digest;
130
131pub(crate) fn encode_entity_source_meaning(
132 entity: &EntityFragment,
133 relation_targets: &[(EntitySourceKey, Vec<(FieldSourceKey, FieldFragment)>)],
134 reachable_types: &[NamedTypeFragment],
135) -> Result<Vec<u8>, SchemaContractError> {
136 let mut writer = WireWriter::new(MAX_SCHEMA_PROPOSAL_BYTES);
137 writer.push_raw(SOURCE_MEANING_HEADER)?;
138 encode_entity(&mut writer, entity)?;
139 writer.push_len(relation_targets.len())?;
140 for (entity, fields) in relation_targets {
141 value::encode_source_key(&mut writer, entity.as_str())?;
142 writer.push_len(fields.len())?;
143 for (_, field) in fields {
144 encode_field(&mut writer, field)?;
145 }
146 }
147 writer.push_len(reachable_types.len())?;
148 for definition in reachable_types {
149 encode_named_type(&mut writer, definition)?;
150 }
151 Ok(writer.finish())
152}
153
154const fn ensure_input_bound(bytes: &[u8], max: usize) -> Result<(), SchemaContractError> {
155 if bytes.len() > max {
156 return Err(SchemaContractError::EncodedTooLarge {
157 len: bytes.len(),
158 max,
159 });
160 }
161 Ok(())
162}
163
164fn ensure_canonical(bytes: &[u8], canonical: Vec<u8>) -> Result<(), SchemaContractError> {
165 if canonical == bytes {
166 Ok(())
167 } else {
168 Err(SchemaContractError::NonCanonical)
169 }
170}