Server tools to backfill, tail, mirror, and verify PLC logs

fjall: add missing errors for when op parsing, fallback to inserting into unknown in places where we werent

ptr.pet 94339149 10393452

verified
+76 -30
+2
.gitignore
··· 1 1 /target 2 2 weekly/ 3 + /.envrc 4 + /.direnv
+74 -30
src/plc_fjall.rs
··· 472 472 Some(serde_json::Value::Array(arr)) => { 473 473 let mut keys = Vec::with_capacity(arr.len()); 474 474 let mut failed = false; 475 - for v in arr { 475 + for v in &arr { 476 476 match v { 477 - serde_json::Value::String(s) => match DidKey::from_did_key(&s) { 477 + serde_json::Value::String(s) => match DidKey::from_did_key(s) { 478 478 Ok(k) => keys.push(k), 479 479 Err(e) => { 480 480 errors.push(StoredOpError::InvalidField( ··· 496 496 } 497 497 } 498 498 if failed { 499 - // we don't have the original array anymore here because we consumed it, 500 - // but we can reconstruct it for the unknown map if we really want to, 501 - // though usually we just move the whole thing. 502 - // since we consumed it, let's just push an error and return None. 503 - // to be absolutely correct about preserving 'unknown', we should have peeked or cloned. 504 - // let's adjust the implementation to clone if we suspect it might fail, 505 - // OR just move the whole value back if it fails. 499 + unknown.insert( 500 + StoredOpField::RotationKeys.to_string(), 501 + serde_json::Value::Array(arr), 502 + ); 506 503 Option::None 507 504 } else { 508 505 Some(keys) ··· 523 520 Some(serde_json::Value::Object(map)) => { 524 521 let mut methods = BTreeMap::new(); 525 522 let mut failed = false; 526 - for (k, v) in map { 523 + for (k, v) in &map { 527 524 match v { 528 - serde_json::Value::String(s) => match DidKey::from_did_key(&s) { 525 + serde_json::Value::String(s) => match DidKey::from_did_key(s) { 529 526 Ok(key) => { 530 - methods.insert(VerificationMethodKey::from_str(&k), key); 527 + methods.insert(VerificationMethodKey::from_str(k), key); 531 528 } 532 529 Err(e) => { 533 530 errors.push(StoredOpError::InvalidField( ··· 548 545 } 549 546 } 550 547 } 551 - if failed { Option::None } else { Some(methods) } 548 + if failed { 549 + unknown.insert( 550 + StoredOpField::VerificationMethods.to_string(), 551 + serde_json::Value::Object(map), 552 + ); 553 + Option::None 554 + } else { 555 + Some(methods) 556 + } 552 557 } 553 558 Some(v) => { 554 559 errors.push(StoredOpError::TypeMismatch( ··· 562 567 }; 563 568 564 569 let also_known_as = match obj.remove(&*StoredOpField::AlsoKnownAs) { 565 - Some(serde_json::Value::Array(arr)) => Some( 566 - arr.into_iter() 567 - .filter_map(|v| match v { 568 - serde_json::Value::String(s) => Some(Aka::from_str(&s)), 569 - _ => None, 570 - }) 571 - .collect(), 572 - ), 570 + Some(serde_json::Value::Array(arr)) => { 571 + let mut akas = Vec::with_capacity(arr.len()); 572 + let mut failed = false; 573 + for v in &arr { 574 + match v { 575 + serde_json::Value::String(s) => akas.push(Aka::from_str(s)), 576 + _ => { 577 + errors.push(StoredOpError::TypeMismatch( 578 + StoredOpField::AlsoKnownAs, 579 + "string inside array", 580 + )); 581 + failed = true; 582 + break; 583 + } 584 + } 585 + } 586 + if failed { 587 + unknown.insert( 588 + StoredOpField::AlsoKnownAs.to_string(), 589 + serde_json::Value::Array(arr), 590 + ); 591 + Option::None 592 + } else { 593 + Some(akas) 594 + } 595 + } 573 596 Some(v) => { 574 597 errors.push(StoredOpError::TypeMismatch( 575 598 StoredOpField::AlsoKnownAs, ··· 582 605 }; 583 606 584 607 let services = match obj.remove(&*StoredOpField::Services) { 585 - Some(serde_json::Value::Object(map)) => Some( 586 - map.into_iter() 587 - .filter_map(|(k, v)| { 608 + Some(serde_json::Value::Object(map)) => { 609 + let mut svcs = BTreeMap::new(); 610 + let mut failed = false; 611 + for (k, v) in &map { 612 + if let (Some(r#type), Some(endpoint)) = ( 613 + v.get("type").and_then(|t| t.as_str()), 614 + v.get("endpoint").and_then(|e| e.as_str()), 615 + ) { 588 616 let svc = StoredService { 589 - r#type: ServiceType::from_str(v.get("type")?.as_str()?), 590 - endpoint: ServiceEndpoint::from_str(v.get("endpoint")?.as_str()?), 617 + r#type: ServiceType::from_str(r#type), 618 + endpoint: ServiceEndpoint::from_str(endpoint), 591 619 }; 592 - Some((ServiceKey::from_str(&k), svc)) 593 - }) 594 - .collect(), 595 - ), 620 + svcs.insert(ServiceKey::from_str(k), svc); 621 + } else { 622 + errors.push(StoredOpError::TypeMismatch( 623 + StoredOpField::Services, 624 + "missing or invalid type/endpoint in service object", 625 + )); 626 + failed = true; 627 + break; 628 + } 629 + } 630 + if failed { 631 + unknown.insert( 632 + StoredOpField::Services.to_string(), 633 + serde_json::Value::Object(map), 634 + ); 635 + Option::None 636 + } else { 637 + Some(svcs) 638 + } 639 + } 596 640 Some(v) => { 597 641 errors.push(StoredOpError::TypeMismatch( 598 642 StoredOpField::Services,