ff0af1ee unknown 1 month ago
1
2
3
4
5
6
7
8
9
10
use crate :: cbor:: { self , Value} ;
11
use crate :: hash:: { hash_object, ChangeHash, KeyId, ObjectRef, ObjectType} ;
12
use crate :: ids:: { ChangeId, NodeId} ;
01a47cd7 Andy McCall 1 month ago ↶
13
use crate :: lines:: Lines;
ff0af1ee unknown 1 month ago
14
use std:: collections:: BTreeMap;
15
16
pub const FORMAT_VERSION : u64 = 1 ;
17
6f8aeb99 Andy McCall 1 month ago ↶
18
0f93f49c Andy McCall 1 month ago ↶
19
20
21
22
23
pub const CHANGE_VERSION : u64 = 3 ;
6f8aeb99 Andy McCall 1 month ago ↶
24
0f93f49c Andy McCall 1 month ago ↶
25
26
# [ derive ( Clone, Copy, Debug, PartialEq, Eq, Hash ) ]
27
pub enum TextForm {
28
29
Cbor,
30
31
Raw,
32
33
34
CborFallback,
35
}
36
6f8aeb99 Andy McCall 1 month ago ↶
37
38
39
40
pub const TEXT_BLOB_MIN : usize = 4096 ;
41
ff0af1ee unknown 1 month ago
42
# [ derive ( Debug, Clone, PartialEq, Eq, thiserror::Error ) ]
43
pub enum ObjectError {
44
# [ error ( " cbor: {0}" ) ]
45
Cbor( # [ from ] cbor:: CborError) ,
46
# [ error ( " schema: {0}" ) ]
47
Schema( String ) ,
48
}
49
50
pub ( crate ) fn schema < T> ( msg : impl Into< String> ) -> Result< T, ObjectError> {
51
Err ( ObjectError:: Schema( msg.into ( ) ) )
52
}
53
54
pub ( crate ) type Map = BTreeMap< u64 , Value> ;
55
56
pub ( crate ) fn field < 'a > ( m : & 'a Map, k : u64 , what : & str ) -> Result< & 'a Value, ObjectError> {
75167672 Andy McCall 1 month ago
57
m.get ( & k)
58
.ok_or_else ( | | ObjectError:: Schema( format! ( " missing field {k} ({what} )" ) ) )
ff0af1ee unknown 1 month ago
59
}
60
pub ( crate ) fn uint ( v : & Value, what : & str ) -> Result< u64 , ObjectError> {
75167672 Andy McCall 1 month ago
61
v.as_uint ( )
62
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected unsigned integer" ) ) )
ff0af1ee unknown 1 month ago
63
}
64
pub ( crate ) fn u32_ ( v : & Value, what : & str ) -> Result< u32 , ObjectError> {
65
u32 :: try_from( uint ( v, what) ? ) .map_err ( | _| ObjectError:: Schema( format! ( " {what} : exceeds u32" ) ) )
66
}
67
pub ( crate ) fn text < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a str , ObjectError> {
75167672 Andy McCall 1 month ago
68
v.as_text ( )
69
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected text" ) ) )
ff0af1ee unknown 1 month ago
70
}
71
pub ( crate ) fn array < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a [ Value] , ObjectError> {
75167672 Andy McCall 1 month ago
72
v.as_array ( )
73
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected array" ) ) )
ff0af1ee unknown 1 month ago
74
}
75
pub ( crate ) fn map < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a Map, ObjectError> {
75167672 Andy McCall 1 month ago
76
v.as_map ( )
77
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected map" ) ) )
ff0af1ee unknown 1 month ago
78
}
79
pub ( crate ) fn bytes < 'a > ( v : & 'a Value, what : & str ) -> Result< & 'a [ u8 ] , ObjectError> {
75167672 Andy McCall 1 month ago
80
v.as_bytes ( )
81
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected bytes" ) ) )
ff0af1ee unknown 1 month ago
82
}
83
pub ( crate ) fn oref ( v : & Value, what : & str ) -> Result< ObjectRef, ObjectError> {
75167672 Andy McCall 1 month ago
84
ObjectRef:: from_slice( bytes ( v, what) ? )
85
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected 32 bytes" ) ) )
ff0af1ee unknown 1 month ago
86
}
87
pub ( crate ) fn node_id ( v : & Value, what : & str ) -> Result< NodeId, ObjectError> {
75167672 Andy McCall 1 month ago
88
NodeId:: from_slice( bytes ( v, what) ? )
89
.ok_or_else ( | | ObjectError:: Schema( format! ( " {what} : expected 16 bytes" ) ) )
ff0af1ee unknown 1 month ago
90
}
91
pub ( crate ) fn check_version ( m : & Map) -> Result< ( ) , ObjectError> {
92
let v = uint ( field ( m, 0 , " version" ) ? , " version" ) ? ;
93
if v ! = FORMAT_VERSION {
14edbaaf Andy McCall 1 month ago ↶
94
let exe = std:: env:: current_exe( )
95
.map ( | p | format! ( " at {} " , p.display ( ) ) )
96
.unwrap_or_default ( ) ;
97
let which = format! ( " oxy {} {exe} " , crate :: VERSION ) ;
98
return schema ( if v > FORMAT_VERSION {
99
format! (
100
" this data is format {v} , written by a newer oxy, and {which} reads format {FORMAT_VERSION} : install the current oxy, and check that no older copy comes first on PATH"
101
)
102
} else {
103
format! (
104
" this data is format {v} , written by an older oxy, and {which} reads format {FORMAT_VERSION} only"
105
)
106
} ) ;
ff0af1ee unknown 1 month ago
107
}
108
Ok ( ( ) )
109
}
110
111
112
113
114
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default ) ]
115
pub struct Hlc {
116
pub wall_ms : u64 ,
117
pub logical : u32 ,
118
}
119
120
impl Hlc {
121
pub fn now ( ) -> Hlc {
122
let wall_ms = std:: time:: SystemTime:: now( )
123
.duration_since ( std:: time:: UNIX_EPOCH )
124
.map ( | d | d.as_millis ( ) as u64 )
125
.unwrap_or ( 0 ) ;
75167672 Andy McCall 1 month ago
126
Hlc {
127
wall_ms,
128
logical: 0 ,
129
}
ff0af1ee unknown 1 month ago
130
}
131
pub ( crate ) fn to_value ( self ) -> Value {
75167672 Andy McCall 1 month ago
132
Value:: map( [
133
( 0 , Value:: Uint( self .wall_ms) ) ,
134
( 1 , Value:: Uint( self .logical as u64 ) ) ,
135
] )
ff0af1ee unknown 1 month ago
136
}
137
pub ( crate ) fn from_value ( v : & Value) -> Result< Hlc, ObjectError> {
138
let m = map ( v, " hlc" ) ? ;
139
Ok ( Hlc {
140
wall_ms: uint ( field ( m, 0 , " hlc.wall_ms" ) ? , " hlc.wall_ms" ) ? ,
141
logical: u32_ ( field ( m, 1 , " hlc.logical" ) ? , " hlc.logical" ) ? ,
142
} )
143
}
144
}
145
146
147
148
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
149
pub enum NodeKind {
150
File,
151
Dir,
152
Symlink,
4dee277c Andy McCall 1 month ago ↶
153
154
Subrepo,
ff0af1ee unknown 1 month ago
155
}
156
157
impl NodeKind {
252e8cb2 Andy McCall 1 month ago ↶
158
pub ( crate ) fn tag ( self ) -> u64 {
ff0af1ee unknown 1 month ago
159
match self {
160
NodeKind:: File => 0 ,
161
NodeKind:: Dir => 1 ,
162
NodeKind:: Symlink => 2 ,
4dee277c Andy McCall 1 month ago ↶
163
NodeKind:: Subrepo => 3 ,
ff0af1ee unknown 1 month ago
164
}
165
}
252e8cb2 Andy McCall 1 month ago ↶
166
pub ( crate ) fn from_tag ( t : u64 ) -> Result< NodeKind, ObjectError> {
ff0af1ee unknown 1 month ago
167
Ok ( match t {
168
0 => NodeKind:: File,
169
1 => NodeKind:: Dir,
170
2 => NodeKind:: Symlink,
4dee277c Andy McCall 1 month ago ↶
171
3 => NodeKind:: Subrepo,
ff0af1ee unknown 1 month ago
172
_ => return schema ( format! ( " unknown node kind {t} " ) ) ,
173
} )
174
}
175
}
176
177
178
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash, Default, PartialOrd, Ord ) ]
179
pub struct Mode {
180
pub exec : bool ,
181
pub opaque : bool ,
182
}
183
184
impl Mode {
75167672 Andy McCall 1 month ago
185
pub const TEXT : Mode = Mode {
186
exec: false ,
187
opaque: false ,
188
} ;
189
pub const OPAQUE : Mode = Mode {
190
exec: false ,
191
opaque: true ,
192
} ;
252e8cb2 Andy McCall 1 month ago ↶
193
pub ( crate ) fn bits ( self ) -> u64 {
ff0af1ee unknown 1 month ago
194
( self .exec as u64 ) | ( ( self .opaque as u64 ) < < 1 )
195
}
252e8cb2 Andy McCall 1 month ago ↶
196
pub ( crate ) fn from_bits ( b : u64 ) -> Result< Mode, ObjectError> {
ff0af1ee unknown 1 month ago
197
if b > 3 {
198
return schema ( format! ( " unknown mode bits {b} " ) ) ;
199
}
75167672 Andy McCall 1 month ago
200
Ok ( Mode {
201
exec: b & 1 ! = 0 ,
202
opaque: b & 2 ! = 0 ,
203
} )
ff0af1ee unknown 1 month ago
204
}
205
}
206
207
208
209
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord ) ]
210
pub struct OpRef {
211
pub change : ChangeHash,
212
pub index : u32 ,
213
}
214
215
impl OpRef {
216
fn to_value ( self ) -> Value {
75167672 Andy McCall 1 month ago
217
Value:: Array( vec! [
218
Value:: Bytes( self .change.0. to_vec ( ) ) ,
219
Value:: Uint( self .index as u64 ) ,
220
] )
ff0af1ee unknown 1 month ago
221
}
222
fn from_value ( v : & Value, what : & str ) -> Result< OpRef, ObjectError> {
223
let a = array ( v, what) ? ;
224
if a.len ( ) ! = 2 {
225
return schema ( format! ( " {what} : expected [hash, index]" ) ) ;
226
}
75167672 Andy McCall 1 month ago
227
Ok ( OpRef {
228
change: oref ( & a[ 0 ] , what) ? ,
229
index: u32_ ( & a[ 1 ] , what) ? ,
230
} )
ff0af1ee unknown 1 month ago
231
}
232
233
pub fn resolve ( self , me : ChangeHash) -> OpRef {
75167672 Andy McCall 1 month ago
234
if self .change.is_zero ( ) {
235
OpRef { change: me, .. self }
236
} else {
237
self
238
}
ff0af1ee unknown 1 month ago
239
}
240
}
241
242
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
243
pub enum TreeOp {
75167672 Andy McCall 1 month ago
244
AddNode {
245
node: NodeId,
246
kind: NodeKind,
247
mode: Mode,
248
parent: NodeId,
249
name: String ,
250
} ,
251
RemoveNode {
252
node: NodeId,
253
placement: OpRef,
254
} ,
255
Move {
256
node: NodeId,
257
old: OpRef,
258
parent: NodeId,
259
name: String ,
260
} ,
261
SetMode {
262
node: NodeId,
263
old: OpRef,
264
mode: Mode,
265
} ,
ff0af1ee unknown 1 month ago
266
}
267
268
269
pub fn validate_name ( name : & str ) -> Result< ( ) , ObjectError> {
270
if name.is_empty ( ) | | name = = " ." | | name = = " .." | | name.contains ( ' /' ) | | name.contains ( ' \0 ' ) {
271
return schema ( format! ( " invalid placement name {name:?} " ) ) ;
272
}
273
Ok ( ( ) )
274
}
275
276
impl TreeOp {
277
pub fn node ( & self ) -> NodeId {
278
match self {
279
TreeOp:: AddNode { node, .. }
280
| TreeOp:: RemoveNode { node, .. }
281
| TreeOp:: Move { node, .. }
282
| TreeOp:: SetMode { node, .. } => * node,
283
}
284
}
285
286
fn to_value ( & self ) -> Value {
287
let nid = | n : & NodeId| Value:: Bytes( n.0. to_vec ( ) ) ;
288
match self {
75167672 Andy McCall 1 month ago
289
TreeOp:: AddNode {
290
node,
291
kind,
292
mode,
293
parent,
294
name,
295
} => Value:: map( [
ff0af1ee unknown 1 month ago
296
( 0 , Value:: Uint( 0 ) ) ,
297
( 1 , nid ( node) ) ,
298
( 2 , Value:: Uint( kind.tag ( ) ) ) ,
299
( 3 , Value:: Uint( mode.bits ( ) ) ) ,
300
( 4 , nid ( parent) ) ,
301
( 5 , Value:: Text( name.clone ( ) ) ) ,
302
] ) ,
75167672 Andy McCall 1 month ago
303
TreeOp:: RemoveNode { node, placement } => Value:: map( [
304
( 0 , Value:: Uint( 1 ) ) ,
305
( 1 , nid ( node) ) ,
306
( 2 , placement.to_value ( ) ) ,
307
] ) ,
308
TreeOp:: Move {
309
node,
310
old,
311
parent,
312
name,
313
} => Value:: map( [
ff0af1ee unknown 1 month ago
314
( 0 , Value:: Uint( 2 ) ) ,
315
( 1 , nid ( node) ) ,
316
( 2 , old.to_value ( ) ) ,
317
( 4 , nid ( parent) ) ,
318
( 5 , Value:: Text( name.clone ( ) ) ) ,
319
] ) ,
320
TreeOp:: SetMode { node, old, mode } => Value:: map( [
321
( 0 , Value:: Uint( 3 ) ) ,
322
( 1 , nid ( node) ) ,
323
( 2 , old.to_value ( ) ) ,
324
( 3 , Value:: Uint( mode.bits ( ) ) ) ,
325
] ) ,
326
}
327
}
328
329
fn from_value ( v : & Value) -> Result< TreeOp, ObjectError> {
330
let m = map ( v, " tree_op" ) ? ;
331
let tag = uint ( field ( m, 0 , " tree_op tag" ) ? , " tree_op tag" ) ? ;
332
let node = node_id ( field ( m, 1 , " tree_op.node" ) ? , " tree_op.node" ) ? ;
333
let op = match tag {
334
0 => {
335
let name = text ( field ( m, 5 , " name" ) ? , " name" ) ? .to_owned ( ) ;
336
validate_name ( & name) ? ;
337
TreeOp:: AddNode {
338
node,
339
kind: NodeKind:: from_tag( uint ( field ( m, 2 , " kind" ) ? , " kind" ) ? ) ? ,
340
mode: Mode:: from_bits( uint ( field ( m, 3 , " mode" ) ? , " mode" ) ? ) ? ,
341
parent: node_id ( field ( m, 4 , " parent" ) ? , " parent" ) ? ,
342
name,
343
}
344
}
75167672 Andy McCall 1 month ago
345
1 => TreeOp:: RemoveNode {
346
node,
347
placement: OpRef:: from_value( field ( m, 2 , " placement" ) ? , " placement" ) ? ,
348
} ,
ff0af1ee unknown 1 month ago
349
2 => {
350
let name = text ( field ( m, 5 , " name" ) ? , " name" ) ? .to_owned ( ) ;
351
validate_name ( & name) ? ;
352
TreeOp:: Move {
353
node,
354
old: OpRef:: from_value( field ( m, 2 , " old" ) ? , " old" ) ? ,
355
parent: node_id ( field ( m, 4 , " parent" ) ? , " parent" ) ? ,
356
name,
357
}
358
}
359
3 => TreeOp:: SetMode {
360
node,
361
old: OpRef:: from_value( field ( m, 2 , " old" ) ? , " old" ) ? ,
362
mode: Mode:: from_bits( uint ( field ( m, 3 , " mode" ) ? , " mode" ) ? ) ? ,
363
} ,
364
_ => return schema ( format! ( " unknown tree op tag {tag} " ) ) ,
365
} ;
366
if node.is_root ( ) {
367
return schema ( " tree op on the root node" ) ;
368
}
369
Ok ( op)
370
}
371
}
372
373
374
375
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
376
pub enum AtomRef {
377
Start,
378
End,
379
Atom { change: ChangeHash, index: u32 } ,
380
}
381
382
impl AtomRef {
383
fn to_value ( self ) -> Value {
384
match self {
385
AtomRef:: Start => Value:: Uint( 0 ) ,
386
AtomRef:: End => Value:: Uint( 1 ) ,
75167672 Andy McCall 1 month ago
387
AtomRef:: Atom { change, index } => Value:: Array( vec! [
388
Value:: Bytes( change.0. to_vec ( ) ) ,
389
Value:: Uint( index as u64 ) ,
390
] ) ,
ff0af1ee unknown 1 month ago
391
}
392
}
393
fn from_value ( v : & Value, what : & str ) -> Result< AtomRef, ObjectError> {
394
match v {
395
Value:: Uint( 0 ) => Ok ( AtomRef:: Start) ,
396
Value:: Uint( 1 ) => Ok ( AtomRef:: End) ,
75167672 Andy McCall 1 month ago
397
Value:: Array( a) if a.len ( ) = = 2 => Ok ( AtomRef:: Atom {
398
change: oref ( & a[ 0 ] , what) ? ,
399
index: u32_ ( & a[ 1 ] , what) ? ,
400
} ) ,
ff0af1ee unknown 1 month ago
401
_ => schema ( format! ( " {what} : expected atom reference" ) ) ,
402
}
403
}
404
pub fn resolve ( self , me : ChangeHash) -> AtomRef {
405
match self {
75167672 Andy McCall 1 month ago
406
AtomRef:: Atom { change, index } if change.is_zero ( ) => {
407
AtomRef:: Atom { change: me, index }
408
}
ff0af1ee unknown 1 month ago
409
other => other,
410
}
411
}
412
}
413
414
415
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
416
pub struct AtomRange {
417
pub change : ChangeHash,
418
pub from : u32 ,
419
pub to : u32 ,
420
}
421
422
impl AtomRange {
423
fn to_value ( self ) -> Value {
424
Value:: Array( vec! [
425
Value:: Bytes( self .change.0. to_vec ( ) ) ,
426
Value:: Uint( self .from as u64 ) ,
427
Value:: Uint( self .to as u64 ) ,
428
] )
429
}
430
fn from_value ( v : & Value) -> Result< AtomRange, ObjectError> {
431
let a = array ( v, " atom range" ) ? ;
432
if a.len ( ) ! = 3 {
433
return schema ( " atom range: expected [hash, from, to]" ) ;
434
}
75167672 Andy McCall 1 month ago
435
let r = AtomRange {
436
change: oref ( & a[ 0 ] , " range.change" ) ? ,
437
from: u32_ ( & a[ 1 ] , " range.from" ) ? ,
438
to: u32_ ( & a[ 2 ] , " range.to" ) ? ,
439
} ;
ff0af1ee unknown 1 month ago
440
if r.from > = r.to {
441
return schema ( " atom range: empty or inverted" ) ;
442
}
443
Ok ( r)
444
}
445
pub fn resolve ( self , me : ChangeHash) -> AtomRange {
75167672 Andy McCall 1 month ago
446
if self .change.is_zero ( ) {
447
AtomRange { change: me, .. self }
448
} else {
449
self
450
}
ff0af1ee unknown 1 month ago
451
}
452
}
453
454
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
455
pub enum Edit {
75167672 Andy McCall 1 month ago
456
Insert {
457
node: NodeId,
458
after: AtomRef,
459
before: AtomRef,
01a47cd7 Andy McCall 1 month ago ↶
460
lines: crate :: lines:: Lines,
75167672 Andy McCall 1 month ago
461
} ,
462
Delete {
463
node: NodeId,
464
atoms: Vec< AtomRange> ,
465
} ,
466
Replace {
467
node: NodeId,
468
old: Option< ObjectRef> ,
469
new: Option< ObjectRef> ,
470
} ,
ff0af1ee unknown 1 month ago
471
}
472
473
impl Edit {
474
pub fn node ( & self ) -> NodeId {
475
match self {
75167672 Andy McCall 1 month ago
476
Edit:: Insert { node, .. } | Edit:: Delete { node, .. } | Edit:: Replace { node, .. } => {
477
* node
478
}
ff0af1ee unknown 1 month ago
479
}
6f8aeb99 Andy McCall 1 month ago ↶
480
}
481
482
483
fn stored_text ( & self , version : u64 ) -> bool {
484
matches! ( self , Edit:: Insert { lines, .. }
485
if version > = 2 & & cbor:: byte_strings_len( lines) > = TEXT_BLOB_MIN )
ff0af1ee unknown 1 month ago
486
}
487
192fad1e Andy McCall 1 month ago ↶
488
0f93f49c Andy McCall 1 month ago ↶
489
490
fn to_value ( & self , stored : Option< ( ObjectRef, TextForm) > ) -> Value {
ff0af1ee unknown 1 month ago
491
let nid = | n : & NodeId| Value:: Bytes( n.0. to_vec ( ) ) ;
492
let opt = | o : & Option< ObjectRef> | match o {
493
Some ( r) => Value:: Bytes( r.0. to_vec ( ) ) ,
494
None => Value:: Null,
495
} ;
192fad1e Andy McCall 1 month ago ↶
496
match ( self , stored) {
497
(
498
Edit:: Insert {
499
node,
500
after,
501
before,
502
lines,
503
} ,
0f93f49c Andy McCall 1 month ago ↶
504
Some ( ( r, form) ) ,
505
) => {
506
let mut fields = vec! [
507
( 0 , Value:: Uint( 0 ) ) ,
508
( 1 , nid ( node) ) ,
509
( 2 , after.to_value ( ) ) ,
510
( 3 , before.to_value ( ) ) ,
511
( 5 , Value:: Bytes( r.0. to_vec ( ) ) ) ,
512
( 6 , Value:: Uint( lines.len ( ) as u64 ) ) ,
513
] ;
514
if form = = TextForm:: CborFallback {
515
fields.push ( ( 7 , Value:: Uint( 1 ) ) ) ;
516
}
517
Value:: map( fields)
518
}
192fad1e Andy McCall 1 month ago ↶
519
(
520
Edit:: Insert {
521
node,
522
after,
523
before,
524
lines,
525
} ,
526
None ,
527
) => Value:: map( [
528
( 0 , Value:: Uint( 0 ) ) ,
529
( 1 , nid ( node) ) ,
530
( 2 , after.to_value ( ) ) ,
531
( 3 , before.to_value ( ) ) ,
75167672 Andy McCall 1 month ago
532
(
533
4 ,
01a47cd7 Andy McCall 1 month ago ↶
534
Value:: Array( lines.iter ( ) .map ( | l | Value:: Bytes( l.to_vec ( ) ) ) .collect ( ) ) ,
75167672 Andy McCall 1 month ago
535
) ,
ff0af1ee unknown 1 month ago
536
] ) ,
192fad1e Andy McCall 1 month ago ↶
537
( Edit:: Delete { node, atoms } , _ ) => Value:: map( [
ff0af1ee unknown 1 month ago
538
( 0 , Value:: Uint( 1 ) ) ,
539
( 1 , nid ( node) ) ,
75167672 Andy McCall 1 month ago
540
(
541
2 ,
542
Value:: Array( atoms.iter ( ) .map ( | r | r.to_value ( ) ) .collect ( ) ) ,
543
) ,
ff0af1ee unknown 1 month ago
544
] ) ,
192fad1e Andy McCall 1 month ago ↶
545
( Edit:: Replace { node, old, new } , _ ) => Value:: map( [
75167672 Andy McCall 1 month ago
546
( 0 , Value:: Uint( 2 ) ) ,
547
( 1 , nid ( node) ) ,
548
( 2 , opt ( old) ) ,
549
( 3 , opt ( new) ) ,
550
] ) ,
ff0af1ee unknown 1 month ago
551
}
552
}
553
6f8aeb99 Andy McCall 1 month ago ↶
554
fn from_value ( v : & Value, version : u64 , resolve : & mut Resolver) -> Result< Edit, ObjectError> {
ff0af1ee unknown 1 month ago
555
let m = map ( v, " edit" ) ? ;
556
let tag = uint ( field ( m, 0 , " edit tag" ) ? , " edit tag" ) ? ;
557
let node = node_id ( field ( m, 1 , " edit.node" ) ? , " edit.node" ) ? ;
558
let opt = | v : & Value, what : & str | -> Result< Option< ObjectRef> , ObjectError> {
75167672 Andy McCall 1 month ago
559
if v.is_null ( ) {
560
Ok ( None )
561
} else {
562
Ok ( Some ( oref ( v, what) ? ) )
563
}
ff0af1ee unknown 1 month ago
564
} ;
565
Ok ( match tag {
566
0 => {
6f8aeb99 Andy McCall 1 month ago ↶
567
let lines = match ( m.get ( & 4 ) , m.get ( & 5 ) ) {
568
( Some ( inline) , None ) => {
01a47cd7 Andy McCall 1 month ago ↶
569
let lines = array_lines ( inline) ? ;
6f8aeb99 Andy McCall 1 month ago ↶
570
if version > = 2 & & cbor:: byte_strings_len( & lines) > = TEXT_BLOB_MIN {
571
return schema ( " an insert this large is stored as a text blob in a version 2 change" ) ;
572
}
86e1824f Andy McCall 1 month ago ↶
573
if lines.is_empty ( ) {
574
return schema ( " insert with no lines" ) ;
575
}
6f8aeb99 Andy McCall 1 month ago ↶
576
lines
577
}
578
( None , Some ( text) ) if version > = 2 => {
579
let text = oref ( text, " insert text" ) ? ;
580
let count = u32_ ( field ( m, 6 , " insert line count" ) ? , " insert line count" ) ? ;
581
if count = = 0 {
582
return schema ( " insert with no lines" ) ;
583
}
0f93f49c Andy McCall 1 month ago ↶
584
let form = match ( version, m.get ( & 7 ) ) {
585
( 2 , None ) => TextForm:: Cbor,
586
( _ , None ) => TextForm:: Raw,
587
( 3 .. , Some ( Value:: Uint( 1 ) ) ) => TextForm:: CborFallback,
588
_ => return schema ( " unknown text blob form" ) ,
589
} ;
6f8aeb99 Andy McCall 1 month ago ↶
590
0f93f49c Andy McCall 1 month ago ↶
591
resolve ( & text, count, form) ?
6f8aeb99 Andy McCall 1 month ago ↶
592
}
593
_ => {
594
return schema (
595
" an insert has either its lines or, in a version 2 change, a text blob" ,
596
)
597
}
598
} ;
ff0af1ee unknown 1 month ago
599
Edit:: Insert {
600
node,
601
after: AtomRef:: from_value( field ( m, 2 , " after" ) ? , " after" ) ? ,
602
before: AtomRef:: from_value( field ( m, 3 , " before" ) ? , " before" ) ? ,
603
lines,
604
}
605
}
606
1 => {
607
let atoms = array ( field ( m, 2 , " atoms" ) ? , " atoms" ) ?
608
.iter ( )
609
.map ( AtomRange:: from_value)
610
.collect:: < Result< Vec< _ > , _ > > ( ) ? ;
611
if atoms.is_empty ( ) {
612
return schema ( " delete with no atoms" ) ;
613
}
614
Edit:: Delete { node, atoms }
615
}
75167672 Andy McCall 1 month ago
616
2 => Edit:: Replace {
617
node,
618
old: opt ( field ( m, 2 , " old" ) ? , " old" ) ? ,
619
new: opt ( field ( m, 3 , " new" ) ? , " new" ) ? ,
620
} ,
ff0af1ee unknown 1 month ago
621
_ => return schema ( format! ( " unknown edit tag {tag} " ) ) ,
622
} )
623
}
624
}
625
626
627
628
# [ derive ( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
629
pub enum ChangeKind {
630
Normal,
631
Resolution,
632
WorkingCopy,
633
}
634
635
impl ChangeKind {
252e8cb2 Andy McCall 1 month ago ↶
636
pub ( crate ) fn tag ( self ) -> u64 {
ff0af1ee unknown 1 month ago
637
match self {
638
ChangeKind:: Normal => 0 ,
639
ChangeKind:: Resolution => 1 ,
640
ChangeKind:: WorkingCopy => 2 ,
641
}
642
}
252e8cb2 Andy McCall 1 month ago ↶
643
pub ( crate ) fn from_tag ( t : u64 ) -> Result< ChangeKind, ObjectError> {
ff0af1ee unknown 1 month ago
644
Ok ( match t {
645
0 => ChangeKind:: Normal,
646
1 => ChangeKind:: Resolution,
647
2 => ChangeKind:: WorkingCopy,
648
_ => return schema ( format! ( " unknown change kind {t} " ) ) ,
649
} )
650
}
651
}
652
653
654
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
655
pub struct Change {
6f8aeb99 Andy McCall 1 month ago ↶
656
657
658
pub version : u64 ,
ff0af1ee unknown 1 month ago
659
pub change_id : ChangeId,
660
pub deps : Vec< ChangeHash> ,
661
pub author : KeyId,
662
pub hlc : Hlc,
663
pub message : String,
664
pub tree_ops : Vec< TreeOp> ,
665
pub edits : Vec< Edit> ,
666
pub kind : ChangeKind,
667
}
668
669
impl Change {
670
pub fn normalize ( & mut self ) {
671
self .deps.sort ( ) ;
672
self .deps.dedup ( ) ;
673
}
674
675
pub fn to_value ( & self ) -> Value {
17f94b13 Andy McCall 1 month ago ↶
676
self .value_with ( & mut | _ | true )
6f8aeb99 Andy McCall 1 month ago ↶
677
}
678
192fad1e Andy McCall 1 month ago ↶
679
680
681
682
0f93f49c Andy McCall 1 month ago ↶
683
fn stored_texts ( & self , sink : & mut TextSink) -> Vec< Option< ( ObjectRef, TextForm) > > {
192fad1e Andy McCall 1 month ago ↶
684
use rayon:: prelude:: * ;
0f93f49c Andy McCall 1 month ago ↶
685
let mut refs: Vec< Option< ( ObjectRef, TextForm) > > = vec! [ None ; self .edits.len ( ) ] ;
192fad1e Andy McCall 1 month ago ↶
686
let stored: Vec< usize > = ( 0 .. self .edits.len ( ) )
687
.filter ( | i | self .edits[ * i] .stored_text ( self .version) )
688
.collect ( ) ;
689
for batch in stored.chunks ( 256 ) {
690
let made: Vec< StoredText> = batch
691
.par_iter ( )
692
.map ( | i | {
693
let Edit:: Insert { lines, .. } = & self .edits[ * i] else {
694
unreachable! ( " only inserts are stored as text" ) ;
0f93f49c Andy McCall 1 month ago ↶
695
} ;
696
let form = text_form ( self .version, lines) ;
697
let content = match form {
698
TextForm:: Raw => lines.bytes ( ) .to_vec ( ) ,
699
TextForm:: Cbor | TextForm:: CborFallback => cbor:: encode_byte_strings( lines) ,
192fad1e Andy McCall 1 month ago ↶
700
} ;
701
StoredText {
702
blob: Blob:: of( & content) ,
703
content,
0f93f49c Andy McCall 1 month ago ↶
704
form,
192fad1e Andy McCall 1 month ago ↶
705
}
706
} )
707
.collect ( ) ;
17f94b13 Andy McCall 1 month ago ↶
708
for ( i, t) in batch.iter ( ) .zip ( & made) {
0f93f49c Andy McCall 1 month ago ↶
709
refs[ * i] = Some ( ( t.blob.hash ( ) , t.form) ) ;
17f94b13 Andy McCall 1 month ago ↶
710
}
711
if ! sink ( made) {
712
break ;
192fad1e Andy McCall 1 month ago ↶
713
}
714
}
715
refs
716
}
717
17f94b13 Andy McCall 1 month ago ↶
718
fn value_with ( & self , sink : & mut TextSink) -> Value {
ff0af1ee unknown 1 month ago
719
Value:: map( [
6f8aeb99 Andy McCall 1 month ago ↶
720
( 0 , Value:: Uint( self .version) ) ,
ff0af1ee unknown 1 month ago
721
( 1 , Value:: Bytes( self .change_id.0. to_vec ( ) ) ) ,
75167672 Andy McCall 1 month ago
722
(
723
2 ,
724
Value:: Array(
725
self .deps
726
.iter ( )
727
.map ( | d | Value:: Bytes( d.0. to_vec ( ) ) )
728
.collect ( ) ,
729
) ,
730
) ,
ff0af1ee unknown 1 month ago
731
( 3 , Value:: Bytes( self .author.0. to_vec ( ) ) ) ,
732
( 4 , self .hlc.to_value ( ) ) ,
733
( 5 , Value:: Text( self .message.clone ( ) ) ) ,
75167672 Andy McCall 1 month ago
734
(
735
6 ,
736
Value:: Array( self .tree_ops.iter ( ) .map ( | t | t.to_value ( ) ) .collect ( ) ) ,
737
) ,
738
(
739
7 ,
6f8aeb99 Andy McCall 1 month ago ↶
740
Value:: Array(
741
self .edits
742
.iter ( )
17f94b13 Andy McCall 1 month ago ↶
743
.zip ( self .stored_texts ( sink) )
192fad1e Andy McCall 1 month ago ↶
744
.map ( | ( e , stored ) | e.to_value ( stored) )
6f8aeb99 Andy McCall 1 month ago ↶
745
.collect ( ) ,
746
) ,
75167672 Andy McCall 1 month ago
747
) ,
ff0af1ee unknown 1 month ago
748
( 8 , Value:: Uint( self .kind.tag ( ) ) ) ,
749
( 9 , Value:: Map( BTreeMap:: new( ) ) ) ,
750
] )
751
}
752
753
pub fn encode ( & self ) -> Vec< u8 > {
75167672 Andy McCall 1 month ago
754
debug_assert! (
755
self .deps.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) ,
756
" deps must be normalized"
757
) ;
ff0af1ee unknown 1 month ago
758
cbor:: encode( & self .to_value ( ) )
759
}
760
6f8aeb99 Andy McCall 1 month ago ↶
761
762
763
pub fn encode_parts ( & self ) -> ( Vec< u8 > , Vec< StoredText> ) {
764
debug_assert! (
765
self .deps.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) ,
766
" deps must be normalized"
767
) ;
768
let mut texts = Vec :: new( ) ;
17f94b13 Andy McCall 1 month ago ↶
769
let bytes = cbor:: encode( & self .value_with ( & mut | batch| {
770
texts.extend ( batch) ;
771
true
772
} ) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
773
( bytes, texts)
17f94b13 Andy McCall 1 month ago ↶
774
}
775
776
777
778
779
pub fn encode_streaming < E> (
780
& self ,
781
mut store : impl FnMut( Vec <StoredText >) -> Result< ( ) , E> ,
782
) -> Result< Vec< u8 > , E> {
783
debug_assert! (
784
self .deps.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) ,
785
" deps must be normalized"
786
) ;
787
let mut failed = None ;
788
let value = self .value_with ( & mut | batch| match store ( batch) {
789
Ok ( ( ) ) => true ,
790
Err ( e) => {
791
failed = Some ( e) ;
792
false
793
}
794
} ) ;
795
match failed {
796
Some ( e) => Err ( e) ,
797
None => Ok ( cbor:: encode( & value) ) ,
798
}
6f8aeb99 Andy McCall 1 month ago ↶
799
}
800
801
802
pub fn has_stored_text ( & self ) -> bool {
803
self .edits.iter ( ) .any ( | e | e.stored_text ( self .version) )
804
}
805
ff0af1ee unknown 1 month ago
806
pub fn hash ( & self ) -> ChangeHash {
807
hash_object ( ObjectType:: Change, & self .encode ( ) )
808
}
809
6f8aeb99 Andy McCall 1 month ago ↶
810
811
ff0af1ee unknown 1 month ago
812
pub fn decode ( bytes : & [u8 ]) -> Result< Change, ObjectError> {
0f93f49c Andy McCall 1 month ago ↶
813
Self :: decode_with( bytes, & mut | r, _ , _ | {
6f8aeb99 Andy McCall 1 month ago ↶
814
schema ( format! (
815
" text blob {} needs a store to read it from" ,
816
r.short ( 12 )
817
) )
818
} )
819
}
820
821
822
823
pub fn decode_with ( bytes : & [u8 ], resolve : & mut Resolver) -> Result< Change, ObjectError> {
ff0af1ee unknown 1 month ago
824
let v = cbor:: decode( bytes) ? ;
6f8aeb99 Andy McCall 1 month ago ↶
825
Self :: from_value_with( & v, resolve)
ff0af1ee unknown 1 month ago
826
}
827
828
pub fn from_value ( v : & Value) -> Result< Change, ObjectError> {
0f93f49c Andy McCall 1 month ago ↶
829
Self :: from_value_with( v, & mut | r, _ , _ | {
6f8aeb99 Andy McCall 1 month ago ↶
830
schema ( format! (
831
" text blob {} needs a store to read it from" ,
832
r.short ( 12 )
833
) )
834
} )
835
}
836
a4fad5b0 Andy McCall 1 month ago ↶
837
pub ( crate ) fn from_value_with (
838
v : & Value,
839
resolve : & mut Resolver,
840
) -> Result< Change, ObjectError> {
ff0af1ee unknown 1 month ago
841
let m = map ( v, " change" ) ? ;
6f8aeb99 Andy McCall 1 month ago ↶
842
let version = uint ( field ( m, 0 , " version" ) ? , " version" ) ? ;
843
if ! ( 1 .. = CHANGE_VERSION ) .contains ( & version) {
844
return schema ( format! ( " unsupported change format version {version} " ) ) ;
845
}
ff0af1ee unknown 1 month ago
846
let change_id = ChangeId:: from_slice( bytes ( field ( m, 1 , " change_id" ) ? , " change_id" ) ? )
847
.ok_or_else ( | | ObjectError:: Schema( " change_id: expected 16 bytes" .into ( ) ) ) ? ;
848
let deps = array ( field ( m, 2 , " deps" ) ? , " deps" ) ?
849
.iter ( )
850
.map ( | d | oref ( d, " dep" ) )
851
.collect:: < Result< Vec< _ > , _ > > ( ) ? ;
852
if ! deps.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) {
853
return schema ( " deps must be sorted and unique" ) ;
854
}
855
if deps.iter ( ) .any ( | d | d.is_zero ( ) ) {
856
return schema ( " deps may not contain the self reference" ) ;
857
}
858
let extra = map ( field ( m, 9 , " extra" ) ? , " extra" ) ? ;
859
if ! extra.is_empty ( ) {
860
return schema ( " extra must be empty in format version 1" ) ;
861
}
862
Ok ( Change {
6f8aeb99 Andy McCall 1 month ago ↶
863
version,
ff0af1ee unknown 1 month ago
864
change_id,
865
deps,
866
author: oref ( field ( m, 3 , " author" ) ? , " author" ) ? ,
867
hlc: Hlc:: from_value( field ( m, 4 , " hlc" ) ? ) ? ,
868
message: text ( field ( m, 5 , " message" ) ? , " message" ) ? .to_owned ( ) ,
869
tree_ops: array ( field ( m, 6 , " tree_ops" ) ? , " tree_ops" ) ?
870
.iter ( )
871
.map ( TreeOp:: from_value)
872
.collect:: < Result< Vec< _ > , _ > > ( ) ? ,
873
edits: array ( field ( m, 7 , " edits" ) ? , " edits" ) ?
874
.iter ( )
6f8aeb99 Andy McCall 1 month ago ↶
875
.map ( | e | Edit:: from_value( e, version, resolve) )
ff0af1ee unknown 1 month ago
876
.collect:: < Result< Vec< _ > , _ > > ( ) ? ,
877
kind: ChangeKind:: from_tag( uint ( field ( m, 8 , " kind" ) ? , " kind" ) ? ) ? ,
878
} )
879
}
880
}
881
6f8aeb99 Andy McCall 1 month ago ↶
882
883
86e1824f Andy McCall 1 month ago ↶
884
885
0f93f49c Andy McCall 1 month ago ↶
886
pub type Resolver < 'a > = dyn FnMut ( & ObjectRef, u32 , TextForm) -> Result< Lines, ObjectError> + 'a ;
17f94b13 Andy McCall 1 month ago ↶
887
888
889
890
type TextSink < 'a > = dyn FnMut ( Vec< StoredText> ) -> bool + 'a ;
a4fad5b0 Andy McCall 1 month ago ↶
891
892
893
894
# [ derive ( Clone, Copy, Debug, PartialEq, Eq ) ]
895
pub enum TextRef {
0f93f49c Andy McCall 1 month ago ↶
896
897
Blob( ObjectRef, u32 , TextForm) ,
a4fad5b0 Andy McCall 1 month ago ↶
898
899
900
Inline {
901
change: ChangeHash,
902
start: u32 ,
903
end: u32 ,
904
} ,
905
}
906
907
908
01a47cd7 Andy McCall 1 month ago ↶
909
pub fn inline_lines ( array : & [u8 ]) -> Result< Lines, ObjectError> {
a4fad5b0 Andy McCall 1 month ago ↶
910
let v = cbor:: decode( array) ? ;
911
let lines = array_lines ( & v) ? ;
912
if lines.is_empty ( ) {
913
return schema ( " insert with no lines" ) ;
914
}
915
Ok ( lines)
916
}
917
01a47cd7 Andy McCall 1 month ago ↶
918
919
fn array_lines ( v : & Value) -> Result< Lines, ObjectError> {
920
Ok ( array ( v, " lines" ) ?
a4fad5b0 Andy McCall 1 month ago ↶
921
.iter ( )
01a47cd7 Andy McCall 1 month ago ↶
922
.map ( | l | bytes ( l, " line" ) )
923
.collect:: < Result< Vec< & [ u8 ] > , _ > > ( ) ?
924
.into_iter ( )
925
.collect ( ) )
a4fad5b0 Andy McCall 1 month ago ↶
926
}
927
928
impl Change {
929
930
931
932
933
934
pub fn decode_skeleton (
935
bytes : & [u8 ],
936
me : ChangeHash,
937
) -> Result< ( Change, Vec< TextRef> ) , ObjectError> {
938
let mut c = cbor:: Cursor:: new( bytes) ;
939
let ( major, n) = c.head ( ) ? ;
940
if major ! = 5 {
941
return schema ( " change: expected map" ) ;
942
}
943
let mut fields = Map:: new( ) ;
944
let mut edits = Vec :: new( ) ;
945
let mut texts = Vec :: new( ) ;
946
let mut version = None ;
947
let mut last: Option< u64 > = None ;
948
for _ in 0 .. n {
949
let k = map_key ( & mut c, & mut last) ? ;
950
if k = = 7 {
951
let Some ( v) = version else {
952
return schema ( " change: version must come first" ) ;
953
} ;
954
let ( am, an) = c.head ( ) ? ;
955
if am ! = 4 {
956
return schema ( " edits: expected array" ) ;
957
}
958
for _ in 0 .. an {
959
edits.push ( edit_skeleton ( & mut c, v, me, & mut texts) ? ) ;
960
}
961
fields.insert ( 7 , Value:: Array( Vec :: new( ) ) ) ;
962
} else {
963
let val = c.value ( ) ? ;
964
if k = = 0 {
965
version = Some ( uint ( & val, " version" ) ? ) ;
966
}
967
fields.insert ( k, val) ;
968
}
969
}
970
if ! c.at_end ( ) {
971
return Err ( ObjectError:: Cbor( cbor:: CborError:: TrailingBytes) ) ;
972
}
973
let mut change = Change:: from_value( & Value:: Map( fields) ) ? ;
974
change.edits = edits;
975
Ok ( ( change, texts) )
976
}
977
}
978
4d6c9c1b Andy McCall 1 month ago ↶
979
impl Change {
980
981
982
983
pub fn decode_meta ( bytes : & [u8 ]) -> Result< crate:: state:: ChangeMeta, ObjectError> {
984
let mut c = cbor:: Cursor:: new( bytes) ;
985
let ( major, n) = c.head ( ) ? ;
986
if major ! = 5 {
987
return schema ( " change: expected map" ) ;
988
}
989
let mut fields = Map:: new( ) ;
990
let mut last: Option< u64 > = None ;
991
for _ in 0 .. n {
992
let k = map_key ( & mut c, & mut last) ? ;
993
if k = = 6 | | k = = 7 {
994
c.skip ( ) ? ;
995
fields.insert ( k, Value:: Array( Vec :: new( ) ) ) ;
996
} else {
997
fields.insert ( k, c.value ( ) ? ) ;
998
}
999
}
1000
if ! c.at_end ( ) {
1001
return Err ( ObjectError:: Cbor( cbor:: CborError:: TrailingBytes) ) ;
1002
}
1003
Ok ( crate :: state:: ChangeMeta:: of( & Change:: from_value(
1004
& Value:: Map( fields) ,
1005
) ? ) )
1006
}
1007
}
1008
a4fad5b0 Andy McCall 1 month ago ↶
1009
1010
fn map_key ( c : & mut cbor:: Cursor, last : & mut Option< u64 > ) -> Result< u64 , ObjectError> {
1011
let ( km, k) = c.head ( ) ? ;
1012
if km ! = 0 | | last.is_some_and ( | l | k < = l ) {
1013
return Err ( ObjectError:: Cbor( cbor:: CborError:: BadMapKey) ) ;
1014
}
1015
* last = Some ( k) ;
1016
Ok ( k)
1017
}
1018
1019
1020
fn edit_skeleton (
1021
c : & mut cbor:: Cursor,
1022
version : u64 ,
1023
me : ChangeHash,
1024
texts : & mut Vec< TextRef> ,
1025
) -> Result< Edit, ObjectError> {
1026
let ( major, n) = c.head ( ) ? ;
1027
if major ! = 5 {
1028
return schema ( " edit: expected map" ) ;
1029
}
1030
let mut fields = Map:: new( ) ;
1031
let mut inline: Option< ( usize , usize , u64 ) > = None ;
1032
let mut last: Option< u64 > = None ;
1033
for _ in 0 .. n {
1034
let k = map_key ( c, & mut last) ? ;
1035
if k = = 4 {
1036
let start = c.pos ( ) ;
1037
let ( am, count) = c.head ( ) ? ;
1038
if am ! = 4 {
1039
return schema ( " lines: expected array" ) ;
1040
}
1041
for _ in 0 .. count {
1042
c.skip ( ) ? ;
1043
}
1044
inline = Some ( ( start, c.pos ( ) , count) ) ;
1045
continue ;
1046
}
1047
fields.insert ( k, c.value ( ) ? ) ;
1048
}
1049
let Some ( ( start, end, count) ) = inline else {
0f93f49c Andy McCall 1 month ago ↶
1050
return Edit:: from_value( & Value:: Map( fields) , version, & mut | text, count, form| {
1051
texts.push ( TextRef:: Blob( * text, count, form) ) ;
01a47cd7 Andy McCall 1 month ago ↶
1052
Ok ( Lines:: default( ) )
a4fad5b0 Andy McCall 1 month ago ↶
1053
} ) ;
1054
} ;
1055
if uint ( field ( & fields, 0 , " edit tag" ) ? , " edit tag" ) ? ! = 0 | | fields.contains_key ( & 5 ) {
1056
return schema ( " only an insert has inline lines, and not with a text blob" ) ;
1057
}
1058
if count = = 0 {
1059
return schema ( " insert with no lines" ) ;
1060
}
1061
if version > = 2 & & end - start > = TEXT_BLOB_MIN {
1062
return schema ( " an insert this large is stored as a text blob in a version 2 change" ) ;
1063
}
1064
let ( Ok ( start) , Ok ( end) ) = ( u32 :: try_from( start) , u32 :: try_from( end) ) else {
1065
return schema ( " change too large" ) ;
1066
} ;
1067
texts.push ( TextRef:: Inline {
1068
change: me,
1069
start,
1070
end,
1071
} ) ;
1072
Ok ( Edit:: Insert {
1073
node: node_id ( field ( & fields, 1 , " edit.node" ) ? , " edit.node" ) ? ,
1074
after: AtomRef:: from_value( field ( & fields, 2 , " after" ) ? , " after" ) ? ,
1075
before: AtomRef:: from_value( field ( & fields, 3 , " before" ) ? , " before" ) ? ,
01a47cd7 Andy McCall 1 month ago ↶
1076
lines: Lines:: default( ) ,
a4fad5b0 Andy McCall 1 month ago ↶
1077
} )
1078
}
6f8aeb99 Andy McCall 1 month ago ↶
1079
1080
1081
1082
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
1083
pub struct StoredText {
1084
pub blob : Blob,
1085
pub content : Vec< u8 > ,
0f93f49c Andy McCall 1 month ago ↶
1086
pub form : TextForm,
6f8aeb99 Andy McCall 1 month ago ↶
1087
}
1088
0f93f49c Andy McCall 1 month ago ↶
1089
1090
fn text_form ( version : u64 , lines : & Lines) -> TextForm {
1091
match version {
1092
.. = 2 => TextForm:: Cbor,
1093
_ if lines.is_split_form ( ) => TextForm:: Raw,
1094
_ => TextForm:: CborFallback,
1095
}
1096
}
1097
6f8aeb99 Andy McCall 1 month ago ↶
1098
1099
1100
0f93f49c Andy McCall 1 month ago ↶
1101
pub fn text_lines ( content : & [u8 ], count : u32 , form : TextForm) -> Result< Lines, ObjectError> {
1102
let lines = match form {
1103
TextForm:: Raw => Lines:: split( content) ,
1104
TextForm:: Cbor | TextForm:: CborFallback => array_lines ( & cbor:: decode( content) ? ) ? ,
1105
} ;
1106
1107
1108
if cbor:: byte_strings_len( & lines) < TEXT_BLOB_MIN {
6f8aeb99 Andy McCall 1 month ago ↶
1109
return schema ( format! (
1110
" a text blob of {} bytes is short enough to be inline" ,
1111
content.len ( )
1112
) ) ;
1113
}
0f93f49c Andy McCall 1 month ago ↶
1114
if form = = TextForm:: CborFallback & & lines.is_split_form ( ) {
1115
return schema ( " these lines are stored as their bytes in a version 3 change" ) ;
1116
}
6f8aeb99 Andy McCall 1 month ago ↶
1117
if lines.len ( ) ! = count as usize {
1118
return schema ( format! (
1119
" a text blob holds {} lines, the insert says {count} " ,
1120
lines.len ( )
1121
) ) ;
1122
}
1123
Ok ( lines)
1124
}
1125
ff0af1ee unknown 1 month ago
1126
1127
1128
1129
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
1130
pub struct Blob {
1131
pub length : u64 ,
1132
pub chunks : Vec< ObjectRef> ,
1133
}
1134
1135
impl Blob {
6f8aeb99 Andy McCall 1 month ago ↶
1136
1137
1138
pub fn of ( content : & [u8 ]) -> Blob {
1139
Blob {
1140
length: content.len ( ) as u64 ,
1141
chunks: crate :: chunk:: chunks( content)
1142
.into_iter ( )
1143
.map ( | c | hash_object ( ObjectType:: Chunk, c) )
1144
.collect ( ) ,
1145
}
1146
}
1147
ff0af1ee unknown 1 month ago
1148
pub fn encode ( & self ) -> Vec< u8 > {
1149
cbor:: encode( & Value:: map( [
1150
( 0 , Value:: Uint( FORMAT_VERSION ) ) ,
1151
( 1 , Value:: Uint( self .length) ) ,
75167672 Andy McCall 1 month ago
1152
(
1153
2 ,
1154
Value:: Array(
1155
self .chunks
1156
.iter ( )
1157
.map ( | c | Value:: Bytes( c.0. to_vec ( ) ) )
1158
.collect ( ) ,
1159
) ,
1160
) ,
ff0af1ee unknown 1 month ago
1161
] ) )
1162
}
1163
pub fn hash ( & self ) -> ObjectRef {
1164
hash_object ( ObjectType:: Blob, & self .encode ( ) )
1165
}
1166
pub fn decode ( bytes : & [u8 ]) -> Result< Blob, ObjectError> {
1167
let v = cbor:: decode( bytes) ? ;
1168
let m = map ( & v, " blob" ) ? ;
1169
check_version ( m) ? ;
1170
Ok ( Blob {
1171
length: uint ( field ( m, 1 , " length" ) ? , " length" ) ? ,
1172
chunks: array ( field ( m, 2 , " chunks" ) ? , " chunks" ) ?
1173
.iter ( )
1174
.map ( | c | oref ( c, " chunk" ) )
1175
.collect:: < Result< Vec< _ > , _ > > ( ) ? ,
1176
} )
1177
}
1178
}
1179
252e8cb2 Andy McCall 1 month ago ↶
1180
1181
1182
1183
1184
# [ derive ( Debug, Clone, PartialEq, Eq ) ]
1185
pub struct Checkpoint {
1186
pub heads : Vec< ChangeHash> ,
1187
pub count : u64 ,
1188
pub payload : ObjectRef,
1189
}
1190
1191
impl Checkpoint {
1192
pub fn encode ( & self ) -> Vec< u8 > {
1193
cbor:: encode( & Value:: map( [
1194
( 0 , Value:: Uint( FORMAT_VERSION ) ) ,
75167672 Andy McCall 1 month ago
1195
(
1196
1 ,
1197
Value:: Array(
1198
self .heads
1199
.iter ( )
1200
.map ( | h | Value:: Bytes( h.0. to_vec ( ) ) )
1201
.collect ( ) ,
1202
) ,
1203
) ,
252e8cb2 Andy McCall 1 month ago ↶
1204
( 2 , Value:: Uint( self .count) ) ,
1205
( 3 , Value:: Bytes( self .payload.0. to_vec ( ) ) ) ,
1206
] ) )
1207
}
1208
pub fn hash ( & self ) -> ObjectRef {
1209
hash_object ( ObjectType:: Checkpoint, & self .encode ( ) )
1210
}
1211
pub fn decode ( bytes : & [u8 ]) -> Result< Checkpoint, ObjectError> {
1212
let v = cbor:: decode( bytes) ? ;
1213
let m = map ( & v, " checkpoint" ) ? ;
1214
check_version ( m) ? ;
1215
Ok ( Checkpoint {
75167672 Andy McCall 1 month ago
1216
heads: array ( field ( m, 1 , " heads" ) ? , " heads" ) ?
1217
.iter ( )
1218
.map ( | h | oref ( h, " head" ) )
1219
.collect:: < Result< Vec< _ > , _ > > ( ) ? ,
252e8cb2 Andy McCall 1 month ago ↶
1220
count: uint ( field ( m, 2 , " count" ) ? , " count" ) ? ,
1221
payload: oref ( field ( m, 3 , " payload" ) ? , " payload" ) ? ,
1222
} )
1223
}
1224
}
1225
86e682c2 Andy McCall 1 month ago ↶
1226
1227
1228
1229
# [ derive ( Debug, Clone, PartialEq, Eq, Default ) ]
1230
pub struct StateObject {
1231
pub heads : Vec< ChangeHash> ,
1232
}
1233
1234
impl StateObject {
1235
pub fn new ( mut heads : Vec< ChangeHash> ) -> StateObject {
1236
heads.sort ( ) ;
1237
heads.dedup ( ) ;
1238
StateObject { heads }
1239
}
1240
pub fn encode ( & self ) -> Vec< u8 > {
1241
debug_assert! ( self .heads.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) ) ;
1242
cbor:: encode( & Value:: map( [
1243
( 0 , Value:: Uint( FORMAT_VERSION ) ) ,
75167672 Andy McCall 1 month ago
1244
(
1245
1 ,
1246
Value:: Array(
1247
self .heads
1248
.iter ( )
1249
.map ( | h | Value:: Bytes( h.0. to_vec ( ) ) )
1250
.collect ( ) ,
1251
) ,
1252
) ,
86e682c2 Andy McCall 1 month ago ↶
1253
] ) )
1254
}
1255
pub fn hash ( & self ) -> ObjectRef {
1256
hash_object ( ObjectType:: State, & self .encode ( ) )
1257
}
1258
pub fn decode ( bytes : & [u8 ]) -> Result< StateObject, ObjectError> {
1259
let v = cbor:: decode( bytes) ? ;
1260
let m = map ( & v, " state" ) ? ;
1261
check_version ( m) ? ;
75167672 Andy McCall 1 month ago
1262
let heads = array ( field ( m, 1 , " heads" ) ? , " heads" ) ?
1263
.iter ( )
1264
.map ( | h | oref ( h, " head" ) )
1265
.collect:: < Result< Vec< _ > , _ > > ( ) ? ;
86e682c2 Andy McCall 1 month ago ↶
1266
if ! heads.windows ( 2 ) .all ( | w | w[ 0 ] < w[ 1 ] ) {
1267
return schema ( " state heads must be sorted and unique" ) ;
1268
}
1269
Ok ( StateObject { heads } )
1270
}
1271
}
1272
ff0af1ee unknown 1 month ago
1273
# [ cfg ( test ) ]
1274
pub ( crate ) mod tests {
1275
use super :: * ;
1276
1277
pub ( crate ) fn sample_change ( ) -> Change {
1278
let n = NodeId( [ 7 ; 16 ] ) ;
1279
let dep = ObjectRef( [ 1 ; 32 ] ) ;
1280
let mut c = Change {
6f8aeb99 Andy McCall 1 month ago ↶
1281
version: crate :: objects:: CHANGE_VERSION ,
ff0af1ee unknown 1 month ago
1282
change_id: ChangeId( [ 9 ; 16 ] ) ,
1283
deps: vec! [ ObjectRef( [ 2 ; 32 ] ) , dep] ,
1284
author: ObjectRef( [ 3 ; 32 ] ) ,
75167672 Andy McCall 1 month ago
1285
hlc: Hlc {
1286
wall_ms: 1_700_000_000_000 ,
1287
logical: 4 ,
1288
} ,
ff0af1ee unknown 1 month ago
1289
message: " Add greeting" .into ( ) ,
1290
tree_ops: vec! [
75167672 Andy McCall 1 month ago
1291
TreeOp:: AddNode {
1292
node: n,
1293
kind: NodeKind:: File,
1294
mode: Mode:: TEXT ,
1295
parent: NodeId:: ROOT ,
1296
name: " hello.txt" .into ( ) ,
1297
} ,
1298
TreeOp:: SetMode {
1299
node: n,
1300
old: OpRef {
1301
change: ObjectRef:: ZERO ,
1302
index: 0 ,
1303
} ,
1304
mode: Mode {
1305
exec: true ,
1306
opaque: false ,
1307
} ,
1308
} ,
ff0af1ee unknown 1 month ago
1309
] ,
1310
edits: vec! [
75167672 Andy McCall 1 month ago
1311
Edit:: Insert {
1312
node: n,
1313
after: AtomRef:: Start,
1314
before: AtomRef:: End,
01a47cd7 Andy McCall 1 month ago ↶
1315
lines: vec! [ b " hello\n " .to_vec ( ) , b " world\n " .to_vec ( ) ] .into ( ) ,
75167672 Andy McCall 1 month ago
1316
} ,
1317
Edit:: Delete {
1318
node: n,
1319
atoms: vec! [ AtomRange {
1320
change: dep,
1321
from: 0 ,
1322
to: 2 ,
1323
} ] ,
1324
} ,
1325
Edit:: Replace {
1326
node: n,
1327
old: None ,
1328
new: Some ( ObjectRef( [ 5 ; 32 ] ) ) ,
1329
} ,
ff0af1ee unknown 1 month ago
1330
] ,
1331
kind: ChangeKind:: Normal,
1332
} ;
1333
c.normalize ( ) ;
1334
c
1335
}
1336
1337
# [ test ]
1338
fn change_round_trips_and_hash_is_stable ( ) {
1339
let c = sample_change ( ) ;
1340
let bytes = c.encode ( ) ;
1341
let d = Change:: decode( & bytes) .unwrap ( ) ;
1342
assert_eq! ( c, d) ;
1343
assert_eq! ( c.hash ( ) , d.hash ( ) ) ;
1344
assert_eq! ( d.encode ( ) , bytes) ;
1345
1346
let mut e = c.clone ( ) ;
1347
e.message.push ( ' !' ) ;
1348
assert_ne! ( e.hash ( ) , c.hash ( ) ) ;
1349
}
1350
1351
# [ test ]
1352
fn decode_rejects_bad_changes ( ) {
1353
let c = sample_change ( ) ;
1354
let mut v = c.to_value ( ) ;
1355
if let Value:: Map( m) = & mut v {
0f93f49c Andy McCall 1 month ago ↶
1356
m.insert ( 0 , Value:: Uint( 4 ) ) ;
ff0af1ee unknown 1 month ago
1357
}
75167672 Andy McCall 1 month ago
1358
assert! (
1359
matches! ( Change:: from_value( & v) , Err ( ObjectError:: Schema( s) ) if s.contains ( " version" ) )
1360
) ;
ff0af1ee unknown 1 month ago
1361
1362
let mut v = c.to_value ( ) ;
1363
if let Value:: Map( m) = & mut v {
75167672 Andy McCall 1 month ago
1364
m.insert (
1365
2 ,
1366
Value:: Array( vec! [ Value:: Bytes( vec! [ 2 ; 32 ] ) , Value:: Bytes( vec! [ 1 ; 32 ] ) ] ) ,
1367
) ;
ff0af1ee unknown 1 month ago
1368
}
75167672 Andy McCall 1 month ago
1369
assert! (
1370
matches! ( Change:: from_value( & v) , Err ( ObjectError:: Schema( s) ) if s.contains ( " sorted" ) )
1371
) ;
ff0af1ee unknown 1 month ago
1372
1373
let mut v = c.to_value ( ) ;
1374
if let Value:: Map( m) = & mut v {
1375
m.insert ( 9 , Value:: map( [ ( 0 , Value:: Null) ] ) ) ;
1376
}
75167672 Andy McCall 1 month ago
1377
assert! (
1378
matches! ( Change:: from_value( & v) , Err ( ObjectError:: Schema( s) ) if s.contains ( " extra" ) )
1379
) ;
6f8aeb99 Andy McCall 1 month ago ↶
1380
}
1381
01a47cd7 Andy McCall 1 month ago ↶
1382
fn big_lines ( n : usize ) -> Lines {
6f8aeb99 Andy McCall 1 month ago ↶
1383
( 0 .. n)
1384
.map ( | i | format! ( " line {i} of a large insert\n " ) .into_bytes ( ) )
1385
.collect ( )
1386
}
1387
01a47cd7 Andy McCall 1 month ago ↶
1388
fn with_insert ( mut c : Change, lines : Lines) -> Change {
6f8aeb99 Andy McCall 1 month ago ↶
1389
c.edits[ 0 ] = Edit:: Insert {
1390
node: NodeId( [ 7 ; 16 ] ) ,
1391
after: AtomRef:: Start,
1392
before: AtomRef:: End,
1393
lines,
1394
} ;
1395
c
1396
}
1397
1398
# [ test ]
1399
fn large_inserts_are_stored_as_text_blobs ( ) {
1400
assert! (
1401
! sample_change ( ) .has_stored_text ( ) ,
1402
" a small insert stays inline"
1403
) ;
1404
let lines = big_lines ( 500 ) ;
1405
let c = with_insert ( sample_change ( ) , lines.clone ( ) ) ;
1406
assert! ( c.has_stored_text ( ) ) ;
1407
let ( bytes, texts) = c.encode_parts ( ) ;
1408
assert_eq! ( bytes, c.encode ( ) ) ;
1409
assert! ( bytes.len ( ) < 1024 , " {}" , bytes.len ( ) ) ;
1410
assert_eq! ( texts.len ( ) , 1 ) ;
0f93f49c Andy McCall 1 month ago ↶
1411
1412
assert_eq! ( texts[ 0 ] .form, TextForm:: Raw) ;
1413
assert_eq! ( texts[ 0 ] .content, lines.bytes ( ) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
1414
assert_eq! ( texts[ 0 ] .blob, Blob:: of( & texts[ 0 ] .content) ) ;
1415
assert! ( Change:: decode( & bytes) .is_err ( ) , " reading it needs the text" ) ;
0f93f49c Andy McCall 1 month ago ↶
1416
let d = Change:: decode_with( & bytes, & mut | r, n, form| {
6f8aeb99 Andy McCall 1 month ago ↶
1417
assert_eq! ( * r, texts[ 0 ] .blob.hash ( ) ) ;
0f93f49c Andy McCall 1 month ago ↶
1418
assert_eq! ( form, TextForm:: Raw) ;
1419
text_lines ( & texts[ 0 ] .content, n, form)
6f8aeb99 Andy McCall 1 month ago ↶
1420
} )
1421
.unwrap ( ) ;
1422
assert_eq! ( d, c) ;
1423
assert_eq! ( d.hash ( ) , c.hash ( ) ) ;
0f93f49c Andy McCall 1 month ago ↶
1424
1425
let mut v2 = c.clone ( ) ;
1426
v2.version = 2 ;
1427
let ( b2, t2) = v2.encode_parts ( ) ;
1428
assert_eq! ( t2[ 0 ] .form, TextForm:: Cbor) ;
1429
assert_eq! ( t2[ 0 ] .content, cbor:: encode_byte_strings( & lines) ) ;
1430
let d2 = Change:: decode_with( & b2, & mut | _ , n, form| text_lines ( & t2[ 0 ] .content, n, form) )
1431
.unwrap ( ) ;
1432
assert_eq! ( d2, v2) ;
1433
1434
1435
let odd: Lines = ( 0 .. 500 )
1436
.map ( | i | format! ( " no newline {i} " ) .into_bytes ( ) )
1437
.collect ( ) ;
1438
let c3 = with_insert ( sample_change ( ) , odd.clone ( ) ) ;
1439
let ( b3, t3) = c3.encode_parts ( ) ;
1440
assert_eq! ( t3[ 0 ] .form, TextForm:: CborFallback) ;
1441
let d3 = Change:: decode_with( & b3, & mut | _ , n, form| text_lines ( & t3[ 0 ] .content, n, form) )
1442
.unwrap ( ) ;
1443
assert_eq! ( d3, c3) ;
1444
let marked = cbor:: encode_byte_strings( & lines) ;
1445
assert! ( text_lines ( & marked, 500 , TextForm:: CborFallback) .is_err ( ) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
1446
}
1447
1448
# [ test ]
1449
fn version_1_changes_keep_their_inline_inserts_and_hashes ( ) {
1450
let mut c = with_insert ( sample_change ( ) , big_lines ( 500 ) ) ;
1451
c.version = 1 ;
1452
let bytes = c.encode ( ) ;
1453
assert! ( bytes.len ( ) > TEXT_BLOB_MIN ) ;
1454
let ( again, texts) = c.encode_parts ( ) ;
1455
assert! ( texts.is_empty ( ) ) ;
1456
assert_eq! ( again, bytes) ;
1457
let d = Change:: decode( & bytes) .unwrap ( ) ;
1458
assert_eq! ( d.version, 1 ) ;
1459
assert_eq! ( d.encode ( ) , bytes) ;
1460
1461
let mut v = c.to_value ( ) ;
1462
if let Value:: Map( m) = & mut v {
1463
m.insert ( 0 , Value:: Uint( 2 ) ) ;
1464
}
1465
assert! (
1466
matches! ( Change:: from_value( & v) , Err ( ObjectError:: Schema( s) ) if s.contains ( " text blob" ) )
1467
) ;
a4fad5b0 Andy McCall 1 month ago ↶
1468
}
1469
1470
# [ test ]
1471
fn a_skeleton_leaves_every_insert_out_and_says_where_its_lines_are ( ) {
1472
let mut c = with_insert ( sample_change ( ) , big_lines ( 500 ) ) ;
1473
c.edits.push ( Edit:: Insert {
1474
node: NodeId( [ 8 ; 16 ] ) ,
1475
after: AtomRef:: Start,
1476
before: AtomRef:: End,
01a47cd7 Andy McCall 1 month ago ↶
1477
lines: vec! [ b " small\n " .to_vec ( ) , b " insert\n " .to_vec ( ) ] .into ( ) ,
a4fad5b0 Andy McCall 1 month ago ↶
1478
} ) ;
1479
let ( bytes, stored) = c.encode_parts ( ) ;
1480
let me = ObjectRef( [ 4 ; 32 ] ) ;
1481
let ( skel, texts) = Change:: decode_skeleton( & bytes, me) .unwrap ( ) ;
1482
assert_eq! ( texts.len ( ) , 2 ) ;
1483
let mut filled = skel.clone ( ) ;
1484
let mut next = texts.iter ( ) ;
1485
for e in filled.edits.iter_mut ( ) {
1486
if let Edit:: Insert { lines, .. } = e {
1487
assert! ( lines.is_empty ( ) , " no line kept" ) ;
1488
* lines = match next.next ( ) .unwrap ( ) {
0f93f49c Andy McCall 1 month ago ↶
1489
TextRef:: Blob( r, n, form) => {
a4fad5b0 Andy McCall 1 month ago ↶
1490
assert_eq! ( * r, stored[ 0 ] .blob.hash ( ) ) ;
0f93f49c Andy McCall 1 month ago ↶
1491
text_lines ( & stored[ 0 ] .content, * n, * form) .unwrap ( )
a4fad5b0 Andy McCall 1 month ago ↶
1492
}
1493
TextRef:: Inline { change, start, end } => {
1494
assert_eq! ( * change, me) ;
1495
inline_lines ( & bytes[ * start as usize .. * end as usize ] ) .unwrap ( )
1496
}
1497
} ;
1498
}
1499
}
1500
assert_eq! ( filled, c, " filled in, it is the change" ) ;
1501
1502
let mut v1 = with_insert ( sample_change ( ) , big_lines ( 500 ) ) ;
1503
v1.version = 1 ;
1504
assert! ( Change:: decode_skeleton( & v1.encode ( ) , me) .is_ok ( ) ) ;
1505
let mut v = v1.to_value ( ) ;
1506
if let Value:: Map( m) = & mut v {
1507
m.insert ( 0 , Value:: Uint( 2 ) ) ;
1508
}
1509
assert! ( Change:: decode_skeleton( & cbor:: encode( & v) , me) .is_err ( ) ) ;
4d6c9c1b Andy McCall 1 month ago ↶
1510
}
1511
1512
# [ test ]
1513
fn metadata_reads_without_the_operations ( ) {
1514
let c = with_insert ( sample_change ( ) , big_lines ( 500 ) ) ;
1515
let ( bytes, _ ) = c.encode_parts ( ) ;
1516
let m = Change:: decode_meta( & bytes) .unwrap ( ) ;
1517
assert_eq! ( m, crate :: state:: ChangeMeta:: of( & c) ) ;
6f8aeb99 Andy McCall 1 month ago ↶
1518
}
1519
1520
# [ test ]
1521
fn text_blobs_are_checked ( ) {
1522
let lines = big_lines ( 500 ) ;
1523
let content = cbor:: encode_byte_strings( & lines) ;
1524
assert_eq! ( content.len ( ) , cbor:: byte_strings_len( & lines) ) ;
1525
assert_eq! (
1526
content,
1527
cbor:: encode( & Value:: Array(
01a47cd7 Andy McCall 1 month ago ↶
1528
lines.iter ( ) .map ( | l | Value:: Bytes( l.to_vec ( ) ) ) .collect ( )
6f8aeb99 Andy McCall 1 month ago ↶
1529
) )
0f93f49c Andy McCall 1 month ago ↶
1530
) ;
1531
assert_eq! ( text_lines ( & content, 500 , TextForm:: Cbor) .unwrap ( ) , lines) ;
1532
assert! ( text_lines ( & content, 499 , TextForm:: Cbor) .is_err ( ) ) ;
1533
assert_eq! (
1534
text_lines ( lines.bytes ( ) , 500 , TextForm:: Raw) .unwrap ( ) ,
1535
lines
6f8aeb99 Andy McCall 1 month ago ↶
1536
) ;
0f93f49c Andy McCall 1 month ago ↶
1537
assert! ( text_lines ( lines.bytes ( ) , 501 , TextForm:: Raw) .is_err ( ) ) ;
01a47cd7 Andy McCall 1 month ago ↶
1538
let small = cbor:: encode_byte_strings( & vec! [ b " x\n " .to_vec ( ) ] .into ( ) ) ;
0f93f49c Andy McCall 1 month ago ↶
1539
assert! (
1540
text_lines ( & small, 1 , TextForm:: Cbor) .is_err ( ) ,
1541
" short enough to be inline"
1542
) ;
1543
assert! (
1544
text_lines ( b " x\n " , 1 , TextForm:: Raw) .is_err ( ) ,
1545
" short enough to be inline"
1546
) ;
ff0af1ee unknown 1 month ago
1547
}
1548
1549
# [ test ]
1550
fn names_are_validated ( ) {
1551
for bad in [ " " , " ." , " .." , " a/b" , " a\0 b" ] {
1552
assert! ( validate_name ( bad) .is_err ( ) , " {bad:?}" ) ;
1553
}
1554
assert! ( validate_name ( " Foo.c" ) .is_ok ( ) ) ;
1555
assert! ( validate_name ( " ..." ) .is_ok ( ) ) ;
1556
}
1557
1558
# [ test ]
1559
fn mode_and_kind_tags ( ) {
75167672 Andy McCall 1 month ago
1560
for m in [
1561
Mode:: TEXT ,
1562
Mode:: OPAQUE ,
1563
Mode {
1564
exec: true ,
1565
opaque: true ,
1566
} ,
1567
] {
ff0af1ee unknown 1 month ago
1568
assert_eq! ( Mode:: from_bits( m.bits ( ) ) .unwrap ( ) , m) ;
1569
}
1570
assert! ( Mode:: from_bits( 4 ) .is_err ( ) ) ;
75167672 Andy McCall 1 month ago
1571
for k in [
1572
NodeKind:: File,
1573
NodeKind:: Dir,
1574
NodeKind:: Symlink,
1575
NodeKind:: Subrepo,
1576
] {
ff0af1ee unknown 1 month ago
1577
assert_eq! ( NodeKind:: from_tag( k.tag ( ) ) .unwrap ( ) , k) ;
1578
}
1579
}
1580
1581
# [ test ]
1582
fn self_references_resolve ( ) {
1583
let me = ObjectRef( [ 8 ; 32 ] ) ;
75167672 Andy McCall 1 month ago
1584
assert_eq! (
1585
AtomRef:: Atom {
1586
change: ObjectRef:: ZERO ,
1587
index: 3
1588
}
1589
.resolve ( me) ,
1590
AtomRef:: Atom {
1591
change: me,
1592
index: 3
1593
}
1594
) ;
ff0af1ee unknown 1 month ago
1595
assert_eq! ( AtomRef:: Start.resolve ( me) , AtomRef:: Start) ;
1596
let other = ObjectRef( [ 1 ; 32 ] ) ;
75167672 Andy McCall 1 month ago
1597
assert_eq! (
1598
OpRef {
1599
change: other,
1600
index: 1
1601
}
1602
.resolve ( me)
1603
.change,
1604
other
1605
) ;
ff0af1ee unknown 1 month ago
1606
}
1607
1608
# [ test ]
86e682c2 Andy McCall 1 month ago ↶
1609
fn state_object_round_trip ( ) {
75167672 Andy McCall 1 month ago
1610
let st = StateObject:: new( vec! [
1611
ObjectRef( [ 2 ; 32 ] ) ,
1612
ObjectRef( [ 1 ; 32 ] ) ,
1613
ObjectRef( [ 2 ; 32 ] ) ,
1614
] ) ;
86e682c2 Andy McCall 1 month ago ↶
1615
assert_eq! ( st.heads, vec! [ ObjectRef( [ 1 ; 32 ] ) , ObjectRef( [ 2 ; 32 ] ) ] ) ;
1616
assert_eq! ( StateObject:: decode( & st.encode ( ) ) .unwrap ( ) , st) ;
1617
assert_eq! ( StateObject:: new( vec! [ ] ) .heads, vec! [ ] ) ;
1618
assert_ne! ( StateObject:: new( vec! [ ] ) .hash ( ) , st.hash ( ) ) ;
1619
}
1620
1621
# [ test ]
ff0af1ee unknown 1 month ago
1622
fn blob_round_trip ( ) {
75167672 Andy McCall 1 month ago
1623
let b = Blob {
1624
length: 10 ,
1625
chunks: vec! [ ObjectRef( [ 1 ; 32 ] ) , ObjectRef( [ 2 ; 32 ] ) ] ,
1626
} ;
ff0af1ee unknown 1 month ago
1627
assert_eq! ( Blob:: decode( & b.encode ( ) ) .unwrap ( ) , b) ;
1628
}
1629
}