Skip to main content

coven_format/
write.rs

1//! A write's merge-owned changes, split into audience parts (§4, §15.6).
2//!
3//! `WriteRecord` and `WritePart` are in-memory values, not additional encodings.
4//! `write_stream::WriteEncoder` emits the header and per-audience frame streams.
5
6use crate::error::{require, Error, Rule};
7use crate::value::{name, positive, row, Value};
8use crate::wire::{wire_struct, Decoder, Encoder, Wire};
9use crate::write_sources::ChangeId;
10use coven_merge::{Audience, Change, Operation, RowId, Timestamp, WriteId};
11use std::collections::BTreeMap;
12
13/// Identity, timestamp, read-through slot and schema of a transaction.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct WriteHeader {
16    /// Which device wrote it and its original local write number.
17    pub position: WriteId,
18    /// The author's timestamp, whose device matches the write identity.
19    pub timestamp: Timestamp,
20    /// Last applied slot when authored; own earlier writes are implicit reads.
21    pub read_through: u64,
22    /// The app schema version (§18).
23    pub schema_version: u32,
24    /// An ordinary write or a breaking migration marker; exclusion is decided on receipt.
25    pub disposition: WriteDisposition,
26}
27wire_struct!(WriteHeader, position, timestamp, read_through, schema_version, disposition);
28impl WriteHeader {
29    pub(crate) fn validate(&self) -> Result<(), Error> {
30        positive(self.position.number)?;
31        require(
32            self.timestamp.device() == self.position.device,
33            "write timestamp",
34            Rule::TimestampDevice,
35        )?;
36        Ok(())
37    }
38
39    /// The header with its parts' audiences: as many parts as its
40    /// disposition allows, in strictly increasing audience order, store
41    /// first. A whole record and a streamed header frame both hold this.
42    pub(crate) fn validate_parts<'a>(
43        &self,
44        audiences: impl ExactSizeIterator<Item = &'a Audience>,
45    ) -> Result<(), Error> {
46        self.validate()?;
47        self.disposition.validate_parts(audiences.len())?;
48        let mut previous = None;
49        for audience in audiences {
50            require(previous.is_none_or(|p| p < audience), "write parts", Rule::Order)?;
51            previous = Some(audience);
52        }
53        Ok(())
54    }
55}
56
57/// An ordinary write or a breaking migration's empty marker (D5). Schema
58/// exclusion is a received interpretation, never transmitted (§18.6).
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
60pub enum WriteDisposition {
61    /// Apply its changes under the merge rules.
62    Apply,
63    /// Its changes travel only in the breaking change's snapshot; the log has no parts.
64    Migration,
65}
66impl WriteDisposition {
67    pub(crate) fn validate_parts(self, count: usize) -> Result<(), Error> {
68        match self {
69            Self::Migration => require(count == 0, "migration write parts", Rule::StreamLength),
70            Self::Apply => require(count > 0, "write parts", Rule::Required),
71        }
72    }
73}
74impl Wire for WriteDisposition {
75    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
76        match self {
77            Self::Apply => 0u8.put(out),
78            Self::Migration => 2u8.put(out),
79        }
80    }
81    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
82        match u8::get(input)? {
83            0 => Ok(Self::Apply),
84            2 => Ok(Self::Migration),
85            tag => Err(Error::UnknownTag { field: "write disposition", tag }),
86        }
87    }
88}
89
90impl Wire for Operation<Value> {
91    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
92        match self {
93            Self::Insert(values) => {
94                0u8.put(out)?;
95                values.put(out)
96            }
97            Self::Update(values) => {
98                1u8.put(out)?;
99                values.put(out)
100            }
101            Self::Delete => 2u8.put(out),
102        }
103    }
104    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
105        match u8::get(input)? {
106            0 => Ok(Self::Insert(crate::wire::get_name_map(input)?)),
107            1 => Ok(Self::Update(crate::wire::get_name_map(input)?)),
108            2 => Ok(Self::Delete),
109            tag => Err(Error::UnknownTag { field: "row operation", tag }),
110        }
111    }
112}
113wire_struct!(Change<Value>, generation, operation);
114
115/// Merge's change to one row, together with the cells a delete replaced (§4),
116/// the original creation it copies and the SQL operations that produced it.
117#[derive(Clone, Debug, PartialEq, Eq)]
118pub struct RowChange {
119    /// Table, encoded primary key and audience.
120    pub row: RowId,
121    /// The pre-change generation and merge operation, including parent metadata.
122    pub change: Change<Value>,
123    /// A delete's replaced cells, each with the value its setter wrote; empty
124    /// for an insert or an update, which carry no replaced value (D5.5). SQL
125    /// NULL is a value, not absence.
126    pub old: BTreeMap<String, Value>,
127    /// Authenticated original generation and cell sources, independent of scalars.
128    pub sources: crate::write_sources::WriteSources,
129    /// For an insert copying a row from another audience (§15.4), the copied
130    /// row's original creation, whose author keeps owning it (§9.6).
131    pub creator: Option<ChangeId>,
132    /// The statement, shared-trigger and foreign-key operations on this row,
133    /// in execution order, including a dismissal's deletes and the halves
134    /// of a move the row's own operation made. A row a move carried along
135    /// with its ancestor, and a migration difference, have no steps.
136    pub execution: Execution,
137}
138impl Wire for RowChange {
139    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
140        self.row.put(out)?;
141        self.change.put(out)?;
142        if matches!(self.change.operation, Operation::Delete) {
143            self.old.put(out)?;
144        }
145        self.sources.put(out)?;
146        self.creator.put(out)?;
147        self.execution.put_against(out, Some(&self.change))
148    }
149    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
150        let row = Wire::get(input)?;
151        let change: Change<Value> = Wire::get(input)?;
152        let old = match change.operation {
153            Operation::Delete => crate::wire::get_name_map(input)?,
154            Operation::Insert(_) | Operation::Update(_) => BTreeMap::new(),
155        };
156        let sources = Wire::get(input)?;
157        let creator = Wire::get(input)?;
158        let execution = Execution::get_against(input, Some(&change))?;
159        Ok(Self { row, change, old, sources, creator, execution })
160    }
161}
162impl RowChange {
163    /// Validate and encode one bounded row-change frame (kind 2).
164    pub fn encode(&self) -> Result<Vec<u8>, Error> {
165        self.validate()?;
166        crate::encode_frame(crate::kinds::ROW_CHANGE, self)
167    }
168
169    /// Decode exactly one row-change frame.
170    pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
171        let row: Self =
172            crate::wire::decode_frame_value(bytes, crate::kinds::ROW_CHANGE, "row change kind")?;
173        row.validate()?;
174        Ok(row)
175    }
176
177    pub(crate) fn validate(&self) -> Result<(), Error> {
178        row(&self.row)?;
179        self.change.validate(&self.row).map_err(Error::Merge)?;
180        match &self.change.operation {
181            Operation::Insert(values) | Operation::Update(values) => {
182                require(!values.is_empty(), "changed columns", Rule::Required)?;
183                crate::merge_wire::columns(values)?;
184                require(self.old.is_empty(), "old columns", Rule::ColumnOperation)?;
185            }
186            Operation::Delete => {}
187        }
188        for (n, value) in &self.old {
189            name(n)?;
190            value.validate()?;
191        }
192        self.sources.validate(self.change.generation)?;
193        let valid = match &self.change.operation {
194            Operation::Insert(_) => self.sources.setters.is_empty(),
195            Operation::Update(columns) => {
196                self.sources.setters.keys().all(|n| columns.contains_key(n))
197            }
198            Operation::Delete => true,
199        };
200        require(valid, "replaced setters", Rule::ColumnOperation)?;
201        if let Some(creator) = &self.creator {
202            // Only a generation-zero insert in another audience copies a row.
203            require(
204                matches!(self.change.operation, Operation::Insert(_))
205                    && self.change.generation == 0
206                    && creator.row.table == self.row.table
207                    && creator.row.key == self.row.key
208                    && creator.row.audience != self.row.audience,
209                "copied creation",
210                Rule::ColumnOperation,
211            )?;
212            positive(creator.write.number)?;
213        }
214        self.execution.validate(&self.row)
215    }
216
217    /// Check source reads against the consumer's resolved authored past.
218    pub fn validate_past(&self, past: &impl coven_merge::WritePast) -> Result<(), Error> {
219        self.sources.validate_past(past)?;
220        self.execution.sources.validate_past(past)?;
221        if let Some(creator) = &self.creator {
222            require(past.contains(&creator.write), "copied creation read", Rule::Coverage)?;
223        }
224        Ok(())
225    }
226}
227
228/// One row operation at its place in the write's execution (§9.6, D5).
229/// Statements, shared triggers, foreign-key actions and dismissal deletes
230/// record steps; no step says which of them produced it.
231#[derive(Clone, Debug, PartialEq, Eq)]
232pub struct Step {
233    /// The operation's position in the write. Only the steps of one
234    /// operation on several rows share a position: the halves of a
235    /// primary-key update, or of the moves to another audience of a row and
236    /// of the rows that follow its audience.
237    pub sequence: u32,
238    /// The row's generation before the operation, and the columns it set,
239    /// with the parents its references named at that moment.
240    pub change: Change<Value>,
241    /// Which half of an operation on two rows the step is, if either.
242    pub half: Half,
243}
244
245/// The halves of one operation that changes two merge rows at once (D5).
246#[derive(Clone, Copy, Debug, PartialEq, Eq)]
247pub enum Half {
248    /// An operation on one row.
249    Whole,
250    /// The delete of a primary-key update's old key: its foreign keys follow
251    /// their ON UPDATE actions, not ON DELETE (§9.2).
252    OldKey,
253    /// The delete in the old audience, or the insert in the new one, of a
254    /// row whose audience an operation changed: its own update, or one of an
255    /// ancestor it follows (§15.4). SQLite still holds the row, so no foreign
256    /// key acts on its delete.
257    Move,
258}
259
260impl Half {
261    fn put(self, out: &mut Encoder) -> Result<(), Error> {
262        let tag: u8 = match self {
263            Self::Whole => 0,
264            Self::OldKey => 1,
265            Self::Move => 2,
266        };
267        tag.put(out)
268    }
269
270    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
271        match u8::get(input)? {
272            0 => Ok(Self::Whole),
273            1 => Ok(Self::OldKey),
274            2 => Ok(Self::Move),
275            tag => Err(Error::UnknownTag { field: "step half", tag }),
276        }
277    }
278}
279
280impl Step {
281    /// The position after every one of `steps`, where an operation the
282    /// write adds after them goes: 0 with none.
283    pub fn after<'a>(steps: impl IntoIterator<Item = &'a Step>) -> u32 {
284        steps.into_iter().map(|step| step.sequence + 1).max().unwrap_or(0)
285    }
286}
287
288impl crate::wire::Wire for Step {
289    fn put(&self, out: &mut crate::wire::Encoder) -> Result<(), Error> {
290        self.sequence.put(out)?;
291        self.change.put(out)?;
292        self.half.put(out)
293    }
294    fn get(input: &mut crate::wire::Decoder<'_>) -> Result<Self, Error> {
295        let sequence = Wire::get(input)?;
296        let change = Wire::get(input)?;
297        Ok(Self { sequence, change, half: Half::get(input)? })
298    }
299}
300/// A row's operations in execution order, with the sources they replaced
301/// that its net change, if any, does not already name (§9.5, D5).
302#[derive(Clone, Debug, Default, PartialEq, Eq)]
303pub struct Execution {
304    /// In increasing sequence order, each starting at the previous one's
305    /// resulting generation.
306    pub steps: Vec<Step>,
307    /// The generation source and replaced setters the steps used beyond the
308    /// net change's own sources, in the same role order.
309    pub sources: crate::write_sources::WriteSources,
310}
311impl Execution {
312    /// Each step is `sequence | change | half`, with `change` as
313    /// `0` for exactly the row change's own change, written once, or as
314    /// `1 | change` otherwise (D5). An execution record has no own change.
315    fn put_against(&self, out: &mut Encoder, own: Option<&Change<Value>>) -> Result<(), Error> {
316        crate::error::bound(self.steps.len(), crate::wire::MAX_ITEMS, "collection")?;
317        out.charge_items(self.steps.len())?;
318        (self.steps.len() as u32).put(out)?;
319        for step in &self.steps {
320            step.sequence.put(out)?;
321            if own == Some(&step.change) {
322                0u8.put(out)?;
323            } else {
324                1u8.put(out)?;
325                step.change.put(out)?;
326            }
327            step.half.put(out)?;
328        }
329        self.sources.put(out)
330    }
331
332    fn get_against(input: &mut Decoder<'_>, own: Option<&Change<Value>>) -> Result<Self, Error> {
333        let steps = crate::wire::get_sequence(input, |input| {
334            let sequence = Wire::get(input)?;
335            let change = match (u8::get(input)?, own) {
336                (0, Some(own)) => own.clone(),
337                (1, own) => {
338                    let change: Change<Value> = Wire::get(input)?;
339                    // The row change's own change is only ever written once.
340                    require(own != Some(&change), "step change", Rule::Required)?;
341                    change
342                }
343                (tag, _) => return Err(Error::UnknownTag { field: "step change", tag }),
344            };
345            Ok(Step { sequence, change, half: Half::get(input)? })
346        })?;
347        Ok(Self { steps, sources: Wire::get(input)? })
348    }
349
350    pub(crate) fn validate(&self, row_id: &RowId) -> Result<(), Error> {
351        for step in &self.steps {
352            step.change.validate(row_id).map_err(Error::Merge)?;
353            if let Operation::Insert(values) | Operation::Update(values) = &step.change.operation {
354                crate::merge_wire::columns(values)?;
355            }
356            require(
357                step.half != Half::OldKey || matches!(step.change.operation, Operation::Delete),
358                "key update step",
359                Rule::ColumnOperation,
360            )?;
361        }
362        for pair in self.steps.windows(2) {
363            require(pair[0].sequence < pair[1].sequence, "execution steps", Rule::Order)?;
364            require(
365                pair[0].change.after().ok() == Some(pair[1].change.generation),
366                "execution step generation",
367                Rule::Generation,
368            )?;
369        }
370        for source in self.sources.changes() {
371            positive(source.write.number)?;
372            row(&source.row)?;
373        }
374        for (column, source) in &self.sources.setters {
375            name(column)?;
376            name(&source.column)?;
377        }
378        Ok(())
379    }
380}
381
382/// A row the write's SQL changed with no net change: it was inserted and
383/// deleted again, or ends with the values it began with (kind 13). Its
384/// operations can still cause foreign-key actions on other rows (§9.6).
385#[derive(Clone, Debug, PartialEq, Eq)]
386pub struct RowExecution {
387    /// Table, encoded primary key and audience.
388    pub row: RowId,
389    /// At least one operation, with every source the operations replaced.
390    pub execution: Execution,
391}
392impl Wire for RowExecution {
393    fn put(&self, out: &mut Encoder) -> Result<(), Error> {
394        self.row.put(out)?;
395        self.execution.put_against(out, None)
396    }
397    fn get(input: &mut Decoder<'_>) -> Result<Self, Error> {
398        Ok(Self { row: Wire::get(input)?, execution: Execution::get_against(input, None)? })
399    }
400}
401impl RowExecution {
402    /// Validate and encode one bounded execution frame (kind 13).
403    pub fn encode(&self) -> Result<Vec<u8>, Error> {
404        self.validate()?;
405        crate::encode_frame(crate::kinds::ROW_EXECUTION, self)
406    }
407
408    /// Decode exactly one execution frame.
409    pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
410        let row: Self = crate::wire::decode_frame_value(
411            bytes,
412            crate::kinds::ROW_EXECUTION,
413            "row execution kind",
414        )?;
415        row.validate()?;
416        Ok(row)
417    }
418
419    pub(crate) fn validate(&self) -> Result<(), Error> {
420        row(&self.row)?;
421        require(!self.execution.steps.is_empty(), "execution steps", Rule::Required)?;
422        self.execution.validate(&self.row)
423    }
424}
425
426/// All changes for one audience, sealed together (§15.6).
427#[derive(Clone, Debug, PartialEq, Eq)]
428pub struct WritePart {
429    /// The audience of every row in this part.
430    pub audience: Audience,
431    /// One change per row, in increasing merge row-identity order.
432    pub rows: Vec<RowChange>,
433    /// Rows the SQL changed with no net change, in increasing row order; no
434    /// row also has a change.
435    pub executions: Vec<RowExecution>,
436    /// Lost values acknowledged by this write, ordered by row, target and write (D5.10).
437    pub dismissals: Vec<crate::dismissal::Dismissal>,
438}
439impl WritePart {
440    pub(crate) fn validate(&self) -> Result<(), Error> {
441        self.validate_shape()?;
442        for change in &self.rows {
443            change.validate()?;
444        }
445        for execution in &self.executions {
446            execution.validate()?;
447        }
448        Ok(())
449    }
450
451    /// Everything `validate` checks but each row's own validity: the part's
452    /// order, audiences and dismissals.
453    pub(crate) fn validate_shape(&self) -> Result<(), Error> {
454        require(
455            !self.rows.is_empty() || !self.executions.is_empty() || !self.dismissals.is_empty(),
456            "write part rows",
457            Rule::Required,
458        )?;
459        require(
460            self.dismissals.windows(2).all(|pair| pair[0] < pair[1]),
461            "write part dismissals",
462            Rule::Order,
463        )?;
464        for dismissal in &self.dismissals {
465            dismissal.validate()?;
466            require(
467                dismissal.row.audience == self.audience,
468                "write part audience",
469                Rule::Audience,
470            )?;
471        }
472        require(self.rows.windows(2).all(|r| r[0].row < r[1].row), "write part rows", Rule::Order)?;
473        require(
474            self.executions.windows(2).all(|r| r[0].row < r[1].row),
475            "write part executions",
476            Rule::Order,
477        )?;
478        for change in &self.rows {
479            require(change.row.audience == self.audience, "write part audience", Rule::Audience)?;
480        }
481        for execution in &self.executions {
482            require(
483                execution.row.audience == self.audience
484                    && self.rows.binary_search_by(|change| change.row.cmp(&execution.row)).is_err(),
485                "write part executions",
486                Rule::Audience,
487            )?;
488        }
489        Ok(())
490    }
491
492    /// Each row's record, change or execution, in row order.
493    pub fn records(&self) -> impl Iterator<Item = PartRecord<'_>> + '_ {
494        let mut rows = self.rows.iter().peekable();
495        let mut executions = self.executions.iter().peekable();
496        std::iter::from_fn(move || match (rows.peek(), executions.peek()) {
497            (Some(row), Some(execution)) if row.row < execution.row => {
498                rows.next().map(PartRecord::Change)
499            }
500            (_, Some(_)) => executions.next().map(PartRecord::Execution),
501            (Some(_), None) => rows.next().map(PartRecord::Change),
502            (None, None) => None,
503        })
504    }
505
506    /// Each record's frame, in `records()` order, validated and encoded once.
507    /// `WriteEncoder::with_records` takes them in place of encoding the
508    /// records again.
509    pub fn encode_records(&self) -> Result<Vec<Vec<u8>>, Error> {
510        self.records().map(|record| record.encode()).collect()
511    }
512
513    pub(crate) fn frames(&self) -> impl Iterator<Item = Result<Vec<u8>, Error>> + '_ {
514        self.frames_with(|_, record| record.encode())
515    }
516
517    /// The part's frames, the records' as `encoded` has them.
518    pub(crate) fn frames_from<'a>(
519        &'a self,
520        encoded: &'a [Vec<u8>],
521    ) -> impl Iterator<Item = Result<Vec<u8>, Error>> + 'a {
522        self.frames_with(|index, _| Ok(encoded[index].clone()))
523    }
524
525    /// The part's frames in stream order: each record's from `record`, given
526    /// its place among the part's records, and each dismissal encoded.
527    fn frames_with<'a>(
528        &'a self,
529        record: impl Fn(usize, PartRecord<'a>) -> Result<Vec<u8>, Error> + 'a,
530    ) -> impl Iterator<Item = Result<Vec<u8>, Error>> + 'a {
531        let mut records = self.records().enumerate().peekable();
532        let mut dismissals = self.dismissals.iter().peekable();
533        std::iter::from_fn(move || match (records.peek(), dismissals.peek()) {
534            (Some((_, next)), Some(dismissal)) if next.row() <= &dismissal.row => {
535                records.next().map(|(index, next)| record(index, next))
536            }
537            (_, Some(_)) => dismissals.next().map(crate::dismissal::Dismissal::encode),
538            (Some(_), None) => records.next().map(|(index, next)| record(index, next)),
539            (None, None) => None,
540        })
541    }
542}
543
544/// One row's record in a part: its net change or its execution alone.
545#[derive(Clone, Copy, Debug, PartialEq, Eq)]
546pub enum PartRecord<'a> {
547    /// A net row change with its execution.
548    Change(&'a RowChange),
549    /// Operations with no net change.
550    Execution(&'a RowExecution),
551}
552impl<'a> PartRecord<'a> {
553    /// The row this record changes.
554    pub fn row(&self) -> &RowId {
555        match self {
556            Self::Change(change) => &change.row,
557            Self::Execution(execution) => &execution.row,
558        }
559    }
560
561    /// The row's operations.
562    pub fn execution(&self) -> &'a Execution {
563        match self {
564            Self::Change(change) => &change.execution,
565            Self::Execution(execution) => &execution.execution,
566        }
567    }
568
569    /// The record's own frame: kind 2 for a change, 13 for an execution.
570    pub fn encode(&self) -> Result<Vec<u8>, Error> {
571        match self {
572            Self::Change(change) => change.encode(),
573            Self::Execution(execution) => execution.encode(),
574        }
575    }
576}
577
578/// A committed transaction, with one header and a part per audience (§4, §15.6).
579#[derive(Clone, Debug, PartialEq, Eq)]
580pub struct WriteRecord {
581    /// Who wrote it, when, what it had read and its schema.
582    pub header: WriteHeader,
583    /// Strictly increasing audience parts, store before circles. Empty only for a migration.
584    pub parts: Vec<WritePart>,
585}
586impl WriteRecord {
587    pub(crate) fn validate(&self) -> Result<(), Error> {
588        self.header.validate_parts(self.parts.iter().map(|part| &part.audience))?;
589        for part in &self.parts {
590            part.validate()?;
591        }
592        Ok(())
593    }
594
595    /// `validate` for a record whose rows `WritePart::encode_records` already
596    /// validated.
597    pub(crate) fn validate_encoded(&self) -> Result<(), Error> {
598        self.header.validate_parts(self.parts.iter().map(|part| &part.audience))?;
599        for part in &self.parts {
600            part.validate_shape()?;
601        }
602        Ok(())
603    }
604}
605
606/// One row's original record in a write part (D5): its net change, or the
607/// SQL operations that left it with none.
608#[derive(Clone, Debug, PartialEq, Eq)]
609pub enum OriginalRow {
610    /// A net change, with its operations.
611    Change(RowChange),
612    /// Operations with no net change.
613    Execution(RowExecution),
614}
615impl OriginalRow {
616    /// The changed row.
617    pub fn row(&self) -> &RowId {
618        match self {
619            Self::Change(change) => &change.row,
620            Self::Execution(execution) => &execution.row,
621        }
622    }
623
624    /// The record as a part record.
625    pub fn record(&self) -> PartRecord<'_> {
626        match self {
627            Self::Change(change) => PartRecord::Change(change),
628            Self::Execution(execution) => PartRecord::Execution(execution),
629        }
630    }
631
632    /// The row's operations and the sources they used.
633    pub fn execution(&self) -> &Execution {
634        match self {
635            Self::Change(change) => &change.execution,
636            Self::Execution(execution) => &execution.execution,
637        }
638    }
639
640    /// Decode either frame kind.
641    pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
642        match crate::dismissal::WriteFrame::decode(bytes)? {
643            crate::dismissal::WriteFrame::Change(change) => Ok(Self::Change(change)),
644            crate::dismissal::WriteFrame::Execution(execution) => Ok(Self::Execution(execution)),
645            crate::dismissal::WriteFrame::Dismissal(_) => {
646                Err(Error::UnknownTag { field: "original row kind", tag: 3 })
647            }
648        }
649    }
650}
651
652#[cfg(test)]
653#[path = "write_tests.rs"]
654mod tests;