farm_crdt.rs (22904B)
1 #![forbid(unsafe_code)] 2 3 use crate::farm_workspace::RadrootsFarmWorkspaceRef; 4 use crate::kinds::KIND_FARM_CRDT_CHANGE as KIND_FARM_CRDT_CHANGE_EVENT; 5 6 #[cfg(not(feature = "std"))] 7 use alloc::{ 8 string::{String, ToString}, 9 vec::Vec, 10 }; 11 12 pub const KIND_FARM_CRDT_CHANGE: u32 = KIND_FARM_CRDT_CHANGE_EVENT; 13 pub const RADROOTS_FARM_CRDT_CHANGE_SCHEMA: &str = "radroots.farm.crdt.change.v1"; 14 pub const RADROOTS_FARM_CRDT_TAG: &str = "radroots:farm:crdt"; 15 16 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 17 #[derive(Clone, Debug, PartialEq, Eq)] 18 pub struct RadrootsFarmCrdtChange { 19 pub schema: String, 20 pub workspace: RadrootsFarmWorkspaceRef, 21 pub farm_group_id: String, 22 pub document_id: String, 23 pub document_kind: RadrootsFarmCrdtDocumentKind, 24 pub crdt_backend: RadrootsCrdtBackend, 25 pub crdt_backend_version: Option<String>, 26 pub actor_id: String, 27 pub change_hash: String, 28 pub dependencies: Vec<String>, 29 pub encoded_change: String, 30 pub semantic_kind: RadrootsFarmSemanticKind, 31 pub business_time_ms: u64, 32 pub author_member_id: Option<String>, 33 pub app_version: Option<String>, 34 } 35 36 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 37 #[cfg_attr(feature = "serde", serde(from = "String", into = "String"))] 38 #[derive(Clone, Debug, PartialEq, Eq)] 39 pub enum RadrootsFarmCrdtDocumentKind { 40 FarmMembership, 41 FarmRolePolicy, 42 FarmTask, 43 FarmWorkSession, 44 FarmActivity, 45 FarmHarvestRecord, 46 FarmLocation, 47 FarmCrop, 48 FarmCropVariety, 49 FarmCropCycle, 50 FarmAttachment, 51 FarmPayPeriod, 52 FarmInventoryItem, 53 FarmMediaAsset, 54 FarmObservation, 55 Other { value: String }, 56 } 57 58 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 59 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 60 pub enum RadrootsCrdtBackend { 61 Automerge, 62 Yjs, 63 Loro, 64 } 65 66 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 67 #[cfg_attr(feature = "serde", serde(from = "String", into = "String"))] 68 #[derive(Clone, Debug, PartialEq, Eq)] 69 pub enum RadrootsFarmSemanticKind { 70 FarmTaskCreate, 71 FarmTaskAssign, 72 FarmTaskJoin, 73 FarmTaskStatusSet, 74 FarmTaskChecklistItemAdd, 75 FarmTaskCommentAdd, 76 FarmTaskAttachmentAttach, 77 FarmTaskFollowUpCreate, 78 FarmTaskUpdate, 79 FarmTaskComplete, 80 FarmWorkSessionStart, 81 FarmWorkSessionStop, 82 FarmWorkSessionSubmit, 83 FarmWorkSessionManualEntryCreate, 84 FarmWorkSessionApprove, 85 FarmWorkSessionReject, 86 FarmWorkSessionCorrect, 87 FarmWorkSessionUpdate, 88 FarmWorkSessionEnd, 89 FarmHarvestRecordCreate, 90 FarmHarvestLineAdd, 91 FarmHarvestLineCorrect, 92 FarmHarvestLineVoid, 93 FarmHarvestAttachmentAttach, 94 FarmHarvestRecordUpdate, 95 FarmActivityCreate, 96 FarmNoteCreate, 97 FarmLocationCreate, 98 FarmCropCreate, 99 FarmCropVarietyCreate, 100 FarmCropCycleCreate, 101 FarmMemberInviteCreate, 102 FarmMemberApprove, 103 FarmMemberRoleSet, 104 FarmMemberDeactivate, 105 FarmPayPeriodOpen, 106 FarmPayPeriodClose, 107 FarmReportExportMark, 108 FarmInventoryItemUpdate, 109 FarmMediaAssetAttach, 110 FarmObservationCreate, 111 FarmWorkspaceUpdate, 112 Other { value: String }, 113 } 114 115 impl RadrootsFarmCrdtDocumentKind { 116 pub fn as_str(&self) -> &str { 117 match self { 118 Self::FarmMembership => "FarmMembership", 119 Self::FarmRolePolicy => "FarmRolePolicy", 120 Self::FarmTask => "FarmTask", 121 Self::FarmWorkSession => "FarmWorkSession", 122 Self::FarmActivity => "FarmActivity", 123 Self::FarmHarvestRecord => "FarmHarvestRecord", 124 Self::FarmLocation => "FarmLocation", 125 Self::FarmCrop => "FarmCrop", 126 Self::FarmCropVariety => "FarmCropVariety", 127 Self::FarmCropCycle => "FarmCropCycle", 128 Self::FarmAttachment => "FarmAttachment", 129 Self::FarmPayPeriod => "FarmPayPeriod", 130 Self::FarmInventoryItem => "FarmInventoryItem", 131 Self::FarmMediaAsset => "FarmMediaAsset", 132 Self::FarmObservation => "FarmObservation", 133 Self::Other { value } => value.as_str(), 134 } 135 } 136 } 137 138 impl From<String> for RadrootsFarmCrdtDocumentKind { 139 fn from(value: String) -> Self { 140 match value.as_str() { 141 "FarmMembership" => Self::FarmMembership, 142 "FarmRolePolicy" => Self::FarmRolePolicy, 143 "FarmTask" => Self::FarmTask, 144 "FarmWorkSession" => Self::FarmWorkSession, 145 "FarmActivity" => Self::FarmActivity, 146 "FarmHarvestRecord" => Self::FarmHarvestRecord, 147 "FarmLocation" => Self::FarmLocation, 148 "FarmCrop" => Self::FarmCrop, 149 "FarmCropVariety" => Self::FarmCropVariety, 150 "FarmCropCycle" => Self::FarmCropCycle, 151 "FarmAttachment" => Self::FarmAttachment, 152 "FarmPayPeriod" => Self::FarmPayPeriod, 153 "FarmInventoryItem" => Self::FarmInventoryItem, 154 "FarmMediaAsset" => Self::FarmMediaAsset, 155 "FarmObservation" => Self::FarmObservation, 156 _ => Self::Other { value }, 157 } 158 } 159 } 160 161 impl From<RadrootsFarmCrdtDocumentKind> for String { 162 fn from(value: RadrootsFarmCrdtDocumentKind) -> Self { 163 match value { 164 RadrootsFarmCrdtDocumentKind::Other { value } => value, 165 value => value.as_str().to_string(), 166 } 167 } 168 } 169 170 impl RadrootsFarmSemanticKind { 171 pub fn as_str(&self) -> &str { 172 match self { 173 Self::FarmTaskCreate => "FarmTaskCreate", 174 Self::FarmTaskAssign => "FarmTaskAssign", 175 Self::FarmTaskJoin => "FarmTaskJoin", 176 Self::FarmTaskStatusSet => "FarmTaskStatusSet", 177 Self::FarmTaskChecklistItemAdd => "FarmTaskChecklistItemAdd", 178 Self::FarmTaskCommentAdd => "FarmTaskCommentAdd", 179 Self::FarmTaskAttachmentAttach => "FarmTaskAttachmentAttach", 180 Self::FarmTaskFollowUpCreate => "FarmTaskFollowUpCreate", 181 Self::FarmTaskUpdate => "FarmTaskUpdate", 182 Self::FarmTaskComplete => "FarmTaskComplete", 183 Self::FarmWorkSessionStart => "FarmWorkSessionStart", 184 Self::FarmWorkSessionStop => "FarmWorkSessionStop", 185 Self::FarmWorkSessionSubmit => "FarmWorkSessionSubmit", 186 Self::FarmWorkSessionManualEntryCreate => "FarmWorkSessionManualEntryCreate", 187 Self::FarmWorkSessionApprove => "FarmWorkSessionApprove", 188 Self::FarmWorkSessionReject => "FarmWorkSessionReject", 189 Self::FarmWorkSessionCorrect => "FarmWorkSessionCorrect", 190 Self::FarmWorkSessionUpdate => "FarmWorkSessionUpdate", 191 Self::FarmWorkSessionEnd => "FarmWorkSessionEnd", 192 Self::FarmHarvestRecordCreate => "FarmHarvestRecordCreate", 193 Self::FarmHarvestLineAdd => "FarmHarvestLineAdd", 194 Self::FarmHarvestLineCorrect => "FarmHarvestLineCorrect", 195 Self::FarmHarvestLineVoid => "FarmHarvestLineVoid", 196 Self::FarmHarvestAttachmentAttach => "FarmHarvestAttachmentAttach", 197 Self::FarmHarvestRecordUpdate => "FarmHarvestRecordUpdate", 198 Self::FarmActivityCreate => "FarmActivityCreate", 199 Self::FarmNoteCreate => "FarmNoteCreate", 200 Self::FarmLocationCreate => "FarmLocationCreate", 201 Self::FarmCropCreate => "FarmCropCreate", 202 Self::FarmCropVarietyCreate => "FarmCropVarietyCreate", 203 Self::FarmCropCycleCreate => "FarmCropCycleCreate", 204 Self::FarmMemberInviteCreate => "FarmMemberInviteCreate", 205 Self::FarmMemberApprove => "FarmMemberApprove", 206 Self::FarmMemberRoleSet => "FarmMemberRoleSet", 207 Self::FarmMemberDeactivate => "FarmMemberDeactivate", 208 Self::FarmPayPeriodOpen => "FarmPayPeriodOpen", 209 Self::FarmPayPeriodClose => "FarmPayPeriodClose", 210 Self::FarmReportExportMark => "FarmReportExportMark", 211 Self::FarmInventoryItemUpdate => "FarmInventoryItemUpdate", 212 Self::FarmMediaAssetAttach => "FarmMediaAssetAttach", 213 Self::FarmObservationCreate => "FarmObservationCreate", 214 Self::FarmWorkspaceUpdate => "FarmWorkspaceUpdate", 215 Self::Other { value } => value.as_str(), 216 } 217 } 218 } 219 220 impl From<String> for RadrootsFarmSemanticKind { 221 fn from(value: String) -> Self { 222 match value.as_str() { 223 "FarmTaskCreate" => Self::FarmTaskCreate, 224 "FarmTaskAssign" => Self::FarmTaskAssign, 225 "FarmTaskJoin" => Self::FarmTaskJoin, 226 "FarmTaskStatusSet" => Self::FarmTaskStatusSet, 227 "FarmTaskChecklistItemAdd" => Self::FarmTaskChecklistItemAdd, 228 "FarmTaskCommentAdd" => Self::FarmTaskCommentAdd, 229 "FarmTaskAttachmentAttach" => Self::FarmTaskAttachmentAttach, 230 "FarmTaskFollowUpCreate" => Self::FarmTaskFollowUpCreate, 231 "FarmTaskUpdate" => Self::FarmTaskUpdate, 232 "FarmTaskComplete" => Self::FarmTaskComplete, 233 "FarmWorkSessionStart" => Self::FarmWorkSessionStart, 234 "FarmWorkSessionStop" => Self::FarmWorkSessionStop, 235 "FarmWorkSessionSubmit" => Self::FarmWorkSessionSubmit, 236 "FarmWorkSessionManualEntryCreate" => Self::FarmWorkSessionManualEntryCreate, 237 "FarmWorkSessionApprove" => Self::FarmWorkSessionApprove, 238 "FarmWorkSessionReject" => Self::FarmWorkSessionReject, 239 "FarmWorkSessionCorrect" => Self::FarmWorkSessionCorrect, 240 "FarmWorkSessionUpdate" => Self::FarmWorkSessionUpdate, 241 "FarmWorkSessionEnd" => Self::FarmWorkSessionEnd, 242 "FarmHarvestRecordCreate" => Self::FarmHarvestRecordCreate, 243 "FarmHarvestLineAdd" => Self::FarmHarvestLineAdd, 244 "FarmHarvestLineCorrect" => Self::FarmHarvestLineCorrect, 245 "FarmHarvestLineVoid" => Self::FarmHarvestLineVoid, 246 "FarmHarvestAttachmentAttach" => Self::FarmHarvestAttachmentAttach, 247 "FarmHarvestRecordUpdate" => Self::FarmHarvestRecordUpdate, 248 "FarmActivityCreate" => Self::FarmActivityCreate, 249 "FarmNoteCreate" => Self::FarmNoteCreate, 250 "FarmLocationCreate" => Self::FarmLocationCreate, 251 "FarmCropCreate" => Self::FarmCropCreate, 252 "FarmCropVarietyCreate" => Self::FarmCropVarietyCreate, 253 "FarmCropCycleCreate" => Self::FarmCropCycleCreate, 254 "FarmMemberInviteCreate" => Self::FarmMemberInviteCreate, 255 "FarmMemberApprove" => Self::FarmMemberApprove, 256 "FarmMemberRoleSet" => Self::FarmMemberRoleSet, 257 "FarmMemberDeactivate" => Self::FarmMemberDeactivate, 258 "FarmPayPeriodOpen" => Self::FarmPayPeriodOpen, 259 "FarmPayPeriodClose" => Self::FarmPayPeriodClose, 260 "FarmReportExportMark" => Self::FarmReportExportMark, 261 "FarmInventoryItemUpdate" => Self::FarmInventoryItemUpdate, 262 "FarmMediaAssetAttach" => Self::FarmMediaAssetAttach, 263 "FarmObservationCreate" => Self::FarmObservationCreate, 264 "FarmWorkspaceUpdate" => Self::FarmWorkspaceUpdate, 265 _ => Self::Other { value }, 266 } 267 } 268 } 269 270 impl From<RadrootsFarmSemanticKind> for String { 271 fn from(value: RadrootsFarmSemanticKind) -> Self { 272 match value { 273 RadrootsFarmSemanticKind::Other { value } => value, 274 value => value.as_str().to_string(), 275 } 276 } 277 } 278 279 #[cfg(all(test, feature = "serde"))] 280 mod tests { 281 use super::*; 282 283 #[test] 284 fn crdt_change_kind_uses_custom_app_data_kind() { 285 assert_eq!(KIND_FARM_CRDT_CHANGE, 78); 286 } 287 288 #[test] 289 fn crdt_change_represents_required_envelope_fields() { 290 let change = sample_change(); 291 292 assert_eq!(change.schema, RADROOTS_FARM_CRDT_CHANGE_SCHEMA); 293 assert_eq!(change.workspace.pubkey, "workspace_pubkey"); 294 assert_eq!(change.farm_group_id, "BCDEFGHIJKLMNOPQRSTUVW"); 295 assert_eq!(change.document_id, "DEFGHIJKLMNOPQRSTUVWXY"); 296 assert_eq!(change.document_kind, RadrootsFarmCrdtDocumentKind::FarmTask); 297 assert_eq!(change.crdt_backend, RadrootsCrdtBackend::Automerge); 298 assert_eq!(change.dependencies, Vec::<String>::new()); 299 assert_eq!( 300 change.semantic_kind, 301 RadrootsFarmSemanticKind::FarmTaskCreate 302 ); 303 assert_eq!(change.business_time_ms, 1_780_000_000_000); 304 assert_eq!(change.author_member_id.as_deref(), Some("member_abc")); 305 assert_eq!(change.app_version.as_deref(), Some("0.1.0")); 306 } 307 308 #[test] 309 fn crdt_change_serializes_stable_content_shape() { 310 let value = serde_json::to_value(sample_change()).unwrap(); 311 312 assert_eq!(value["schema"], RADROOTS_FARM_CRDT_CHANGE_SCHEMA); 313 assert_eq!(value["workspace"]["d_tag"], "ABCDEFGHIJKLMNOPQRSTUV"); 314 assert_eq!(value["document_kind"], "FarmTask"); 315 assert_eq!(value["crdt_backend"], "Automerge"); 316 assert_eq!(value["semantic_kind"], "FarmTaskCreate"); 317 assert_eq!(value["business_time_ms"], 1_780_000_000_000_u64); 318 } 319 320 #[test] 321 fn document_kinds_serialize_as_stable_strings() { 322 for (kind, expected) in [ 323 ( 324 RadrootsFarmCrdtDocumentKind::FarmMembership, 325 "FarmMembership", 326 ), 327 ( 328 RadrootsFarmCrdtDocumentKind::FarmRolePolicy, 329 "FarmRolePolicy", 330 ), 331 (RadrootsFarmCrdtDocumentKind::FarmTask, "FarmTask"), 332 ( 333 RadrootsFarmCrdtDocumentKind::FarmWorkSession, 334 "FarmWorkSession", 335 ), 336 (RadrootsFarmCrdtDocumentKind::FarmActivity, "FarmActivity"), 337 ( 338 RadrootsFarmCrdtDocumentKind::FarmHarvestRecord, 339 "FarmHarvestRecord", 340 ), 341 (RadrootsFarmCrdtDocumentKind::FarmLocation, "FarmLocation"), 342 (RadrootsFarmCrdtDocumentKind::FarmCrop, "FarmCrop"), 343 ( 344 RadrootsFarmCrdtDocumentKind::FarmCropVariety, 345 "FarmCropVariety", 346 ), 347 (RadrootsFarmCrdtDocumentKind::FarmCropCycle, "FarmCropCycle"), 348 ( 349 RadrootsFarmCrdtDocumentKind::FarmAttachment, 350 "FarmAttachment", 351 ), 352 (RadrootsFarmCrdtDocumentKind::FarmPayPeriod, "FarmPayPeriod"), 353 ( 354 RadrootsFarmCrdtDocumentKind::FarmInventoryItem, 355 "FarmInventoryItem", 356 ), 357 ( 358 RadrootsFarmCrdtDocumentKind::FarmMediaAsset, 359 "FarmMediaAsset", 360 ), 361 ( 362 RadrootsFarmCrdtDocumentKind::FarmObservation, 363 "FarmObservation", 364 ), 365 ] { 366 let encoded = serde_json::to_string(&kind).unwrap(); 367 assert_eq!(encoded, format!("\"{expected}\"")); 368 let decoded: RadrootsFarmCrdtDocumentKind = serde_json::from_str(&encoded).unwrap(); 369 assert_eq!(decoded, kind); 370 } 371 } 372 373 #[test] 374 fn semantic_kinds_serialize_as_stable_strings() { 375 for (kind, expected) in [ 376 (RadrootsFarmSemanticKind::FarmTaskCreate, "FarmTaskCreate"), 377 (RadrootsFarmSemanticKind::FarmTaskAssign, "FarmTaskAssign"), 378 (RadrootsFarmSemanticKind::FarmTaskJoin, "FarmTaskJoin"), 379 ( 380 RadrootsFarmSemanticKind::FarmTaskStatusSet, 381 "FarmTaskStatusSet", 382 ), 383 ( 384 RadrootsFarmSemanticKind::FarmTaskChecklistItemAdd, 385 "FarmTaskChecklistItemAdd", 386 ), 387 ( 388 RadrootsFarmSemanticKind::FarmTaskCommentAdd, 389 "FarmTaskCommentAdd", 390 ), 391 ( 392 RadrootsFarmSemanticKind::FarmTaskAttachmentAttach, 393 "FarmTaskAttachmentAttach", 394 ), 395 ( 396 RadrootsFarmSemanticKind::FarmTaskFollowUpCreate, 397 "FarmTaskFollowUpCreate", 398 ), 399 (RadrootsFarmSemanticKind::FarmTaskUpdate, "FarmTaskUpdate"), 400 ( 401 RadrootsFarmSemanticKind::FarmTaskComplete, 402 "FarmTaskComplete", 403 ), 404 ( 405 RadrootsFarmSemanticKind::FarmWorkSessionStart, 406 "FarmWorkSessionStart", 407 ), 408 ( 409 RadrootsFarmSemanticKind::FarmWorkSessionStop, 410 "FarmWorkSessionStop", 411 ), 412 ( 413 RadrootsFarmSemanticKind::FarmWorkSessionSubmit, 414 "FarmWorkSessionSubmit", 415 ), 416 ( 417 RadrootsFarmSemanticKind::FarmWorkSessionManualEntryCreate, 418 "FarmWorkSessionManualEntryCreate", 419 ), 420 ( 421 RadrootsFarmSemanticKind::FarmWorkSessionApprove, 422 "FarmWorkSessionApprove", 423 ), 424 ( 425 RadrootsFarmSemanticKind::FarmWorkSessionReject, 426 "FarmWorkSessionReject", 427 ), 428 ( 429 RadrootsFarmSemanticKind::FarmWorkSessionCorrect, 430 "FarmWorkSessionCorrect", 431 ), 432 ( 433 RadrootsFarmSemanticKind::FarmWorkSessionUpdate, 434 "FarmWorkSessionUpdate", 435 ), 436 ( 437 RadrootsFarmSemanticKind::FarmWorkSessionEnd, 438 "FarmWorkSessionEnd", 439 ), 440 ( 441 RadrootsFarmSemanticKind::FarmHarvestRecordCreate, 442 "FarmHarvestRecordCreate", 443 ), 444 ( 445 RadrootsFarmSemanticKind::FarmHarvestLineAdd, 446 "FarmHarvestLineAdd", 447 ), 448 ( 449 RadrootsFarmSemanticKind::FarmHarvestLineCorrect, 450 "FarmHarvestLineCorrect", 451 ), 452 ( 453 RadrootsFarmSemanticKind::FarmHarvestLineVoid, 454 "FarmHarvestLineVoid", 455 ), 456 ( 457 RadrootsFarmSemanticKind::FarmHarvestAttachmentAttach, 458 "FarmHarvestAttachmentAttach", 459 ), 460 ( 461 RadrootsFarmSemanticKind::FarmHarvestRecordUpdate, 462 "FarmHarvestRecordUpdate", 463 ), 464 ( 465 RadrootsFarmSemanticKind::FarmActivityCreate, 466 "FarmActivityCreate", 467 ), 468 (RadrootsFarmSemanticKind::FarmNoteCreate, "FarmNoteCreate"), 469 ( 470 RadrootsFarmSemanticKind::FarmLocationCreate, 471 "FarmLocationCreate", 472 ), 473 (RadrootsFarmSemanticKind::FarmCropCreate, "FarmCropCreate"), 474 ( 475 RadrootsFarmSemanticKind::FarmCropVarietyCreate, 476 "FarmCropVarietyCreate", 477 ), 478 ( 479 RadrootsFarmSemanticKind::FarmCropCycleCreate, 480 "FarmCropCycleCreate", 481 ), 482 ( 483 RadrootsFarmSemanticKind::FarmMemberInviteCreate, 484 "FarmMemberInviteCreate", 485 ), 486 ( 487 RadrootsFarmSemanticKind::FarmMemberApprove, 488 "FarmMemberApprove", 489 ), 490 ( 491 RadrootsFarmSemanticKind::FarmMemberRoleSet, 492 "FarmMemberRoleSet", 493 ), 494 ( 495 RadrootsFarmSemanticKind::FarmMemberDeactivate, 496 "FarmMemberDeactivate", 497 ), 498 ( 499 RadrootsFarmSemanticKind::FarmPayPeriodOpen, 500 "FarmPayPeriodOpen", 501 ), 502 ( 503 RadrootsFarmSemanticKind::FarmPayPeriodClose, 504 "FarmPayPeriodClose", 505 ), 506 ( 507 RadrootsFarmSemanticKind::FarmReportExportMark, 508 "FarmReportExportMark", 509 ), 510 ( 511 RadrootsFarmSemanticKind::FarmInventoryItemUpdate, 512 "FarmInventoryItemUpdate", 513 ), 514 ( 515 RadrootsFarmSemanticKind::FarmMediaAssetAttach, 516 "FarmMediaAssetAttach", 517 ), 518 ( 519 RadrootsFarmSemanticKind::FarmObservationCreate, 520 "FarmObservationCreate", 521 ), 522 ( 523 RadrootsFarmSemanticKind::FarmWorkspaceUpdate, 524 "FarmWorkspaceUpdate", 525 ), 526 ] { 527 let encoded = serde_json::to_string(&kind).unwrap(); 528 assert_eq!(encoded, format!("\"{expected}\"")); 529 let decoded: RadrootsFarmSemanticKind = serde_json::from_str(&encoded).unwrap(); 530 assert_eq!(decoded, kind); 531 } 532 } 533 534 #[test] 535 fn unknown_crdt_kinds_roundtrip_as_other_strings() { 536 let document_kind: RadrootsFarmCrdtDocumentKind = 537 serde_json::from_str("\"FarmSoilTest\"").unwrap(); 538 let semantic_kind: RadrootsFarmSemanticKind = 539 serde_json::from_str("\"FarmSoilTestCreate\"").unwrap(); 540 541 assert_eq!( 542 document_kind, 543 RadrootsFarmCrdtDocumentKind::Other { 544 value: "FarmSoilTest".to_string() 545 } 546 ); 547 assert_eq!( 548 semantic_kind, 549 RadrootsFarmSemanticKind::Other { 550 value: "FarmSoilTestCreate".to_string() 551 } 552 ); 553 assert_eq!(document_kind.as_str(), "FarmSoilTest"); 554 assert_eq!(semantic_kind.as_str(), "FarmSoilTestCreate"); 555 assert_eq!( 556 serde_json::to_string(&document_kind).unwrap(), 557 "\"FarmSoilTest\"" 558 ); 559 assert_eq!( 560 serde_json::to_string(&semantic_kind).unwrap(), 561 "\"FarmSoilTestCreate\"" 562 ); 563 } 564 565 fn sample_change() -> RadrootsFarmCrdtChange { 566 RadrootsFarmCrdtChange { 567 schema: RADROOTS_FARM_CRDT_CHANGE_SCHEMA.to_string(), 568 workspace: RadrootsFarmWorkspaceRef { 569 pubkey: "workspace_pubkey".to_string(), 570 d_tag: "ABCDEFGHIJKLMNOPQRSTUV".to_string(), 571 }, 572 farm_group_id: "BCDEFGHIJKLMNOPQRSTUVW".to_string(), 573 document_id: "DEFGHIJKLMNOPQRSTUVWXY".to_string(), 574 document_kind: RadrootsFarmCrdtDocumentKind::FarmTask, 575 crdt_backend: RadrootsCrdtBackend::Automerge, 576 crdt_backend_version: Some("0.x".to_string()), 577 actor_id: "actor_abc".to_string(), 578 change_hash: "crdt_hash_abc".to_string(), 579 dependencies: Vec::new(), 580 encoded_change: "base64url-encoded-change".to_string(), 581 semantic_kind: RadrootsFarmSemanticKind::FarmTaskCreate, 582 business_time_ms: 1_780_000_000_000, 583 author_member_id: Some("member_abc".to_string()), 584 app_version: Some("0.1.0".to_string()), 585 } 586 } 587 }