don't
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

test(atproto): expand tests for `Did`

Signed-off-by: tjh <x@tjh.dev>

tjh 21bd7e94 cab06f74

+119 -37
+119 -37
crates/atproto/src/did.rs
··· 29 29 /// 30 30 /// Panics if `s` is not a valid DID. 31 31 /// 32 - #[must_use] 32 + #[must_use] 33 33 pub fn from_static(did: &'static str) -> &'static Self { 34 34 validate_did(did).expect("hard-coded did should be valid"); 35 35 Self::new(did) ··· 64 64 /// assert_eq!(did.typ(), "did"); 65 65 /// ``` 66 66 /// 67 - #[must_use] 67 + #[must_use] 68 68 pub fn typ(&self) -> &'static str { 69 69 assert_eq!( 70 70 &self.inner[..4], ··· 88 88 /// assert_eq!(did.method(), "web"); 89 89 /// ``` 90 90 /// 91 - #[must_use] 91 + #[must_use] 92 92 pub fn method(&self) -> &str { 93 93 &self.inner[4..self.method_terminator()] 94 94 } ··· 107 107 /// assert_eq!(did.ident(), "tjh.dev"); 108 108 /// ``` 109 109 /// 110 - #[must_use] 110 + #[must_use] 111 111 pub fn ident(&self) -> &str { 112 112 &self.inner[1 + self.method_terminator()..] 113 113 } 114 114 115 115 /// Get the DID as a string slice. 116 116 #[inline] 117 - #[must_use] 117 + #[must_use] 118 118 pub const fn as_str(&self) -> &str { 119 119 &self.inner 120 120 } 121 121 122 122 /// Convert to a `Box<Did>`. 123 - #[must_use] 123 + #[must_use] 124 124 pub fn into_boxed(&self) -> Box<Self> { 125 125 Self::new_boxed(self.inner.to_owned().into_boxed_str()) 126 126 } ··· 182 182 impl TryFrom<Box<str>> for Box<Did> { 183 183 type Error = Error; 184 184 #[inline] 185 + /// Convert a `Box<str>` to a `Box<Did>` without copying. 185 186 fn try_from(value: Box<str>) -> Result<Self, Self::Error> { 186 187 validate_did(&value)?; 187 188 Ok(Did::new_boxed(value)) ··· 192 191 impl TryFrom<String> for Box<Did> { 193 192 type Error = Error; 194 193 #[inline] 194 + /// Convert a [`String`] to a `Box<Did>` without copying. 195 195 fn try_from(value: String) -> Result<Self, Self::Error> { 196 196 validate_did(&value)?; 197 197 Ok(Did::new_boxed(value.into_boxed_str())) 198 - } 199 - } 200 - 201 - impl From<&Did> for Box<Did> { 202 - #[inline] 203 - fn from(value: &Did) -> Self { 204 - Did::new_boxed(value.inner.to_owned().into_boxed_str()) 205 198 } 206 199 } 207 200 ··· 227 232 } 228 233 } 229 234 230 - #[derive(Debug, thiserror::Error)] 235 + #[derive(Debug, PartialEq, Eq, thiserror::Error)] 231 236 pub enum Error { 232 237 #[error("Invalid DID format, expected \"did:{{method}}:{{ident}}\"")] 233 238 Format, ··· 253 258 validate_ident(ident)?; 254 259 Ok(()) 255 260 } 256 - (Some(_), _, _) => Err(Error::WrongType), 261 + (Some(_), Some(_), Some(_)) => Err(Error::WrongType), 257 262 _ => Err(Error::Format), 258 263 } 259 264 } 260 265 261 266 fn validate_method(method: &str) -> Result<(), Error> { 262 - if !method.is_empty() && method.as_bytes().iter().all(u8::is_ascii_lowercase) { Ok(()) } else { Err(Error::InvalidMethod(method.into())) } 267 + if method.as_bytes().iter().all(u8::is_ascii_lowercase) { 268 + Ok(()) 269 + } else { 270 + Err(Error::InvalidMethod(method.into())) 271 + } 263 272 } 264 273 265 274 fn validate_ident(ident: &str) -> Result<(), Error> { 266 - if !ident.is_empty() 267 - && ident 268 - .as_bytes() 269 - .iter() 270 - .all(|v| v.is_ascii_alphanumeric() || [b'.', b'_', b':', b'%', b'-'].contains(v)) && !ident.ends_with([':', '%']) { Ok(()) } else { Err(Error::InvalidIdent(ident.into())) } 275 + if ident 276 + .as_bytes() 277 + .iter() 278 + .all(|v| v.is_ascii_alphanumeric() || [b'.', b'_', b':', b'%', b'-'].contains(v)) 279 + && !ident.ends_with([':', '%']) 280 + { 281 + Ok(()) 282 + } else { 283 + Err(Error::InvalidIdent(ident.into())) 284 + } 271 285 272 286 // @TODO Validate percent encoding in ident. 273 287 } ··· 300 296 Self { inner } 301 297 } 302 298 303 - #[must_use] 299 + #[must_use] 304 300 pub fn from_static(did: &'static str) -> Self { 305 301 validate_did(did).expect("hard-coded did should be valid"); 306 302 let inner = did.into(); ··· 423 419 #[derive(Hash, PartialEq, Eq, PartialOrd, Ord)] 424 420 #[repr(transparent)] 425 421 pub struct CowDid<'a>(pub std::borrow::Cow<'a, Did>); 422 + 423 + impl fmt::Display for CowDid<'_> { 424 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 425 + fmt::Display::fmt(&self.0, f) 426 + } 427 + } 428 + 429 + impl fmt::Debug for CowDid<'_> { 430 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 431 + fmt::Debug::fmt(&self.0, f) 432 + } 433 + } 426 434 427 435 impl ops::Deref for CowDid<'_> { 428 436 type Target = Did; ··· 641 625 642 626 #[cfg(test)] 643 627 mod tests { 644 - use super::{CowDid, Did, OwnedDid}; 628 + use super::{CowDid, Did, Error, OwnedDid}; 629 + 630 + /// An example DID. 631 + const EXAMPLE_DID: &str = "did:plc:65gha4t3avpfpzmvpbwovss7"; 645 632 646 633 #[test] 647 634 fn can_parse_examples() { ··· 683 664 "did:method:val/two", 684 665 "did:method:val?two", 685 666 "did:method:val#two", 667 + "did:method", 686 668 ]; 687 669 688 670 for sample in samples { ··· 693 673 694 674 #[test] 695 675 fn rejects_empty() { 696 - assert!(Did::parse("").is_err()); 676 + assert_eq!(Did::parse(""), Err(Error::Format)); 697 677 } 698 678 699 679 #[test] 700 680 fn rejects_empty_type() { 701 - assert!(Did::parse(":meth:ident").is_err()); 681 + assert_eq!(Did::parse(":meth:ident"), Err(Error::WrongType)); 702 682 } 703 683 704 684 #[test] 705 685 fn rejects_empty_method() { 706 - assert!(Did::parse("did::ident").is_err()); 686 + assert_eq!(Did::parse("did::ident"), Err(Error::EmptyMethod)); 707 687 } 708 688 709 689 #[test] 710 690 fn rejects_empty_ident() { 711 - assert!(Did::parse("did:meth:").is_err()); 691 + assert_eq!(Did::parse("did:meth:"), Err(Error::EmptyIdent)); 712 692 } 713 693 714 694 #[test] 715 695 #[should_panic] 716 696 fn reject_invalid_static() { 717 697 let _ = Did::from_static("did:_plc:asdf"); 698 + let _ = OwnedDid::from_static("did:plc65gha4t3avpfpzmvpbwovss7"); 718 699 } 719 700 720 - #[cfg(feature = "serde")] 721 - const SER: &str = r#"{"did":"did:plc:65gha4t3avpfpzmvpbwovss7"}"#; 701 + #[test] 702 + fn can_display_did() { 703 + let did = Did::from_static(EXAMPLE_DID); 704 + 705 + assert_eq!(format!("{did}"), EXAMPLE_DID); 706 + assert_eq!(format!("{did:?}"), format!("{EXAMPLE_DID:?}")); 707 + 708 + let owned: OwnedDid = did.into(); 709 + assert_eq!(format!("{owned}"), EXAMPLE_DID); 710 + assert_eq!(format!("{owned:?}"), format!("{EXAMPLE_DID:?}")); 711 + } 712 + 713 + #[test] 714 + fn can_parse_to_boxed() { 715 + let _: Box<Did> = EXAMPLE_DID.parse().unwrap(); 716 + } 717 + 718 + #[test] 719 + fn can_parse_to_owned() { 720 + let _: OwnedDid = EXAMPLE_DID.parse().unwrap(); 721 + } 722 + 723 + #[test] 724 + fn can_convert_static() { 725 + let _: &'static str = EXAMPLE_DID.try_into().unwrap(); 726 + let _: OwnedDid = EXAMPLE_DID.try_into().unwrap(); 727 + let _ = OwnedDid::from_static(EXAMPLE_DID); 728 + } 729 + 730 + #[test] 731 + fn can_convert_box_str_inplace() { 732 + let boxed_str = String::from(EXAMPLE_DID).into_boxed_str(); 733 + let ptr = boxed_str.as_ptr() as usize; 734 + 735 + let did: Box<Did> = boxed_str.try_into().unwrap(); 736 + assert_eq!( 737 + ptr, 738 + did.as_ptr() as usize, 739 + "Boxed DID should point to same memory as boxed str" 740 + ); 741 + } 742 + 743 + #[test] 744 + fn can_convert_string_inplace() { 745 + let string = String::from(EXAMPLE_DID); 746 + let ptr = string.as_ptr() as usize; 747 + 748 + let did: Box<Did> = string.try_into().unwrap(); 749 + assert_eq!( 750 + ptr, 751 + did.as_ptr() as usize, 752 + "Boxed DID should point to same memory as String" 753 + ); 754 + } 722 755 723 756 #[test] 724 757 #[cfg(feature = "serde")] ··· 782 709 did: &'a Did, 783 710 } 784 711 785 - let test: Test = serde_json::from_str(SER).unwrap(); 786 - assert_eq!(test.did, "did:plc:65gha4t3avpfpzmvpbwovss7"); 712 + let json = serde_json::to_string(&serde_json::json!({"did":EXAMPLE_DID})).unwrap(); 713 + let test: Test = serde_json::from_str(&json).unwrap(); 714 + assert_eq!(test.did, EXAMPLE_DID); 715 + assert_eq!(EXAMPLE_DID, test.did); 787 716 } 788 717 789 718 #[test] ··· 796 721 did: OwnedDid, 797 722 } 798 723 799 - let test: Test = serde_json::from_str(SER).unwrap(); 800 - assert_eq!(test.did.as_ref(), "did:plc:65gha4t3avpfpzmvpbwovss7"); 724 + let json = serde_json::to_string(&serde_json::json!({"did":EXAMPLE_DID})).unwrap(); 725 + let test: Test = serde_json::from_str(&json).unwrap(); 726 + assert_eq!(test.did, EXAMPLE_DID); 727 + assert_eq!(EXAMPLE_DID, test.did); 801 728 } 802 729 803 730 #[test] ··· 810 733 did: Box<Did>, 811 734 } 812 735 813 - let test: Test = serde_json::from_str(SER).unwrap(); 814 - assert_eq!(test.did, "did:plc:65gha4t3avpfpzmvpbwovss7"); 736 + let json = serde_json::to_string(&serde_json::json!({"did":EXAMPLE_DID})).unwrap(); 737 + let test: Test = serde_json::from_str(&json).unwrap(); 738 + assert_eq!(test.did, EXAMPLE_DID); 739 + assert_eq!(EXAMPLE_DID, test.did); 815 740 } 816 741 817 742 #[test] ··· 825 746 did: CowDid<'a>, 826 747 } 827 748 828 - let test: Test = serde_json::from_str(SER).unwrap(); 829 - assert_eq!(test.did.as_ref(), "did:plc:65gha4t3avpfpzmvpbwovss7"); 749 + let json = serde_json::to_string(&serde_json::json!({"did":EXAMPLE_DID})).unwrap(); 750 + let test: Test = serde_json::from_str(&json).unwrap(); 751 + assert_eq!(test.did, EXAMPLE_DID); 752 + assert_eq!(EXAMPLE_DID, test.did); 753 + 830 754 assert!(matches!(test.did.0, std::borrow::Cow::Borrowed(_))); 831 755 } 832 756