A better Rust ATProto crate
102
fork

Configure Feed

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

Data and all its serialization is migrated now. Added a float variant to it.

+1049 -717
+1 -4
crates/jacquard-common/src/bos.rs
··· 32 32 //! } 33 33 //! ``` 34 34 35 - use core::{fmt, marker::PhantomData, ops::Deref}; 36 - 37 35 use alloc::{ 38 36 borrow::{Cow, ToOwned}, 39 37 boxed::Box, ··· 41 39 vec::Vec, 42 40 }; 43 41 44 - use serde::{Deserialize, Serialize}; 45 42 use smol_str::SmolStr; 46 43 47 - use crate::{CowStr, IntoStatic}; 44 + use crate::CowStr; 48 45 49 46 mod internal { 50 47 pub trait Ref<T: ?Sized> {
+21 -1
crates/jacquard-common/src/types/aturi.rs
··· 20 20 use regex_lite::Regex; 21 21 use serde::Serializer; 22 22 use serde::{Deserialize, Deserializer, Serialize, de::Error}; 23 - use smol_str::{SmolStr, ToSmolStr}; 23 + use smol_str::SmolStr; 24 24 25 25 use super::Lazy; 26 26 ··· 168 168 RepoPath { 169 169 collection: self.collection.into_static(), 170 170 rkey: self.rkey.map(|rkey| rkey.into_static()), 171 + } 172 + } 173 + } 174 + 175 + impl<S: Bos<str> + AsRef<str>> RepoPath<S> { 176 + /// Convert to a `RepoPath` with a different backing type. 177 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> RepoPath<B> { 178 + RepoPath { 179 + collection: self.collection.convert(), 180 + rkey: self.rkey.map(|rkey| RecordKey(rkey.0.convert())), 171 181 } 172 182 } 173 183 } ··· 446 456 fn into_static(self) -> AtUri<S::Output> { 447 457 AtUri { 448 458 uri: self.uri.into_static(), 459 + indices: self.indices, 460 + } 461 + } 462 + } 463 + 464 + impl<S: Bos<str> + AsRef<str>> AtUri<S> { 465 + /// Convert to an `AtUri` with a different backing type. 466 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> AtUri<B> { 467 + AtUri { 468 + uri: B::from(self.uri), 449 469 indices: self.indices, 450 470 } 451 471 }
+30 -18
crates/jacquard-common/src/types/blob.rs
··· 4 4 use alloc::string::{String, ToString}; 5 5 use core::convert::Infallible; 6 6 use core::{fmt, hash::Hash, ops::Deref, str::FromStr}; 7 - use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; 8 - use smol_str::{SmolStr, ToSmolStr}; 7 + use serde::{Deserialize, Deserializer, Serialize, Serializer}; 9 8 10 9 /// Blob reference for binary data in AT Protocol. 11 10 /// ··· 78 77 } 79 78 } 80 79 80 + impl<S: Bos<str> + AsRef<str>> Blob<S> { 81 + /// Convert to a `Blob` with a different backing type. 82 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> Blob<B> { 83 + Blob { 84 + r#ref: self.r#ref.convert(), 85 + mime_type: self.mime_type.convert(), 86 + size: self.size, 87 + } 88 + } 89 + } 90 + 81 91 /// Tagged blob reference with `$type` field for serde. 82 92 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 83 93 #[serde(tag = "$type", rename_all = "lowercase")] ··· 126 136 } 127 137 } 128 138 139 + impl<S: Bos<str> + AsRef<str>> BlobRef<S> { 140 + /// Convert to a `BlobRef` with a different backing type. 141 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> BlobRef<B> { 142 + match self { 143 + BlobRef::Blob(blob) => BlobRef::Blob(blob.convert()), 144 + } 145 + } 146 + } 147 + 129 148 /// MIME type identifier for blob data. 130 149 /// 131 150 /// Used to specify the content type of blobs. Supports patterns like "image/*" and "*/*". ··· 142 161 } 143 162 } 144 163 145 - impl<'m> MimeType<&'m str> { 146 - /// Infallible constructor, borrows from input. 147 - pub fn new(mime_type: &'m str) -> Self { 164 + impl<S: Bos<str>> MimeType<S> { 165 + /// Infallible constructor, wraps the input directly. 166 + pub fn new(mime_type: S) -> Self { 148 167 Self(mime_type) 149 168 } 150 169 151 - /// Infallible constructor for trusted MIME type strings. 152 - pub fn raw(mime_type: &'m str) -> Self { 153 - Self(mime_type) 170 + /// Convert to a `MimeType` with a different backing type. 171 + pub fn convert<B: Bos<str> + From<S>>(self) -> MimeType<B> { 172 + MimeType(B::from(self.0)) 154 173 } 155 174 } 156 175 157 - impl<S: Bos<str> + From<SmolStr>> MimeType<S> { 176 + impl<S: Bos<str> + FromStr> MimeType<S> { 158 177 /// Infallible constructor, takes ownership. 159 178 pub fn new_owned(mime_type: impl AsRef<str>) -> Self { 160 - Self(S::from(mime_type.as_ref().to_smolstr())) 179 + Self(S::from_str(mime_type.as_ref()).unwrap_or_else(|_| unreachable!())) 161 180 } 162 181 163 182 /// Infallible constructor for static strings. 164 183 pub fn new_static(mime_type: &'static str) -> Self { 165 - Self(S::from(SmolStr::new_static(mime_type))) 166 - } 167 - } 168 - 169 - impl<'m> MimeType<CowStr<'m>> { 170 - /// Infallible constructor, borrows if possible. 171 - pub fn new_cow(mime_type: CowStr<'m>) -> Self { 172 - Self(mime_type) 184 + Self(S::from_str(mime_type).unwrap_or_else(|_| unreachable!())) 173 185 } 174 186 } 175 187
+43 -15
crates/jacquard-common/src/types/cid.rs
··· 1 1 use crate::bos::{Bos, DefaultStr}; 2 - use crate::{CowStr, IntoStatic, cowstr::ToCowStr}; 2 + use crate::{CowStr, IntoStatic}; 3 3 use alloc::string::{String, ToString}; 4 4 pub use cid::Cid as IpldCid; 5 5 use core::{convert::Infallible, fmt, ops::Deref, str::FromStr}; ··· 53 53 /// Invalid UTF-8 in CID string. 54 54 #[error("{:?}", 0)] 55 55 Utf8(#[from] core::str::Utf8Error), 56 + /// Wraps another error with additional context 57 + #[error("converting from a string slice")] 58 + #[cfg_attr(feature = "std", diagnostic(code(jacquard::cid::str_conversion)))] 59 + Conversion, 56 60 } 57 61 58 62 // --------------------------------------------------------------------------- ··· 117 121 pub fn str(cid: &'c str) -> Self { 118 122 Self::Str(cid) 119 123 } 124 + } 120 125 126 + impl<S: Bos<str>> Cid<S> { 121 127 /// Parse a CID from bytes (tries IPLD first, falls back to UTF-8 string). 122 - pub fn new(cid: &'c [u8]) -> Result<Self, Error> { 128 + pub fn new<'c>(cid: &'c [u8]) -> Result<Cid<S>, Error> 129 + where 130 + S: From<&'c str>, 131 + { 123 132 if let Ok(cid) = IpldCid::try_from(cid.as_ref()) { 124 - Ok(Self::ipld(cid)) 133 + Ok(Cid::ipld(cid)) 125 134 } else { 126 135 let cid_str = core::str::from_utf8(cid)?; 127 - Ok(Self::Str(cid_str)) 136 + Ok(Cid::Str(cid_str.into())) 128 137 } 129 138 } 130 139 } 131 140 132 - impl<S: Bos<str> + From<SmolStr>> Cid<S> { 141 + impl<S: Bos<str> + FromStr> Cid<S> { 133 142 /// Parse a CID from bytes into an owned value. 134 143 pub fn new_owned(cid: &[u8]) -> Result<Self, Error> { 135 144 if let Ok(cid) = IpldCid::try_from(cid.as_ref()) { 136 145 Ok(Self::ipld(cid)) 137 146 } else { 138 147 let cid_str = core::str::from_utf8(cid)?; 139 - Ok(Cid::Str(S::from(cid_str.to_smolstr()))) 148 + Ok(Cid::Str( 149 + S::from_str(cid_str).map_err(|_| Error::Conversion)?, 150 + )) 140 151 } 141 152 } 142 153 } ··· 223 234 } 224 235 } 225 236 237 + impl<S: Bos<str>> Cid<S> { 238 + /// Convert to a `Cid` with a different backing type. 239 + pub fn convert<B: Bos<str> + From<S>>(self) -> Cid<B> { 240 + match self { 241 + Cid::Ipld { cid, s } => Cid::Ipld { cid, s }, 242 + Cid::Str(s) => Cid::Str(B::from(s)), 243 + } 244 + } 245 + } 246 + 226 247 impl<S: Bos<str> + AsRef<str>> From<Cid<S>> for String { 227 248 fn from(value: Cid<S>) -> Self { 228 249 value.as_str().to_string() ··· 296 317 pub fn ipld(cid: IpldCid) -> Self { 297 318 CidLink(Cid::ipld(cid)) 298 319 } 299 - } 300 320 301 - impl<'c> CidLink<&'c str> { 302 321 /// Parse a CID link from bytes. 303 - pub fn new(cid: &'c [u8]) -> Result<Self, Error> { 304 - Ok(Self(Cid::new(cid)?)) 322 + pub fn new<'c>(cid: &'c [u8]) -> Result<CidLink<S>, Error> 323 + where 324 + S: Bos<str> + From<&'c str>, 325 + { 326 + Ok(CidLink(Cid::new(cid)?)) 305 327 } 328 + } 306 329 330 + impl<'c> CidLink<&'c str> { 307 331 /// Construct a CID link from a string slice. 308 332 pub fn str(cid: &'c str) -> Self { 309 333 Self(Cid::str(cid)) ··· 315 339 } 316 340 } 317 341 318 - impl<S: Bos<str> + From<SmolStr>> CidLink<S> { 342 + impl<S: Bos<str> + FromStr> CidLink<S> { 319 343 /// Parse a CID link from bytes into an owned value. 320 344 pub fn new_owned(cid: &[u8]) -> Result<Self, Error> { 321 345 Ok(CidLink(Cid::new_owned(cid)?)) ··· 405 429 self.visit_bytes(&v) 406 430 } 407 431 408 - fn visit_newtype_struct<D>( 409 - self, 410 - deserializer: D, 411 - ) -> Result<Self::Value, D::Error> 432 + fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error> 412 433 where 413 434 D: serde::de::Deserializer<'de>, 414 435 { ··· 500 521 501 522 fn into_static(self) -> Self::Output { 502 523 CidLink(self.0.into_static()) 524 + } 525 + } 526 + 527 + impl<S: Bos<str>> CidLink<S> { 528 + /// Convert to a `CidLink` with a different backing type. 529 + pub fn convert<B: Bos<str> + From<S>>(self) -> CidLink<B> { 530 + CidLink(self.0.convert()) 503 531 } 504 532 } 505 533
+7 -8
crates/jacquard-common/src/types/collection.rs
··· 1 1 use alloc::string::String; 2 2 use core::fmt; 3 - use core::str::FromStr; 3 + use smol_str::SmolStr; 4 4 5 5 use serde::{Deserialize, Serialize}; 6 6 ··· 8 8 use crate::types::{ 9 9 aturi::RepoPath, 10 10 nsid::Nsid, 11 - recordkey::{RecordKey, RecordKeyType, Rkey}, 11 + recordkey::{RecordKey, RecordKeyType}, 12 12 }; 13 13 use crate::xrpc::XrpcResp; 14 - use crate::{BorrowOrShare, Bos, CowStr, IntoStatic}; 14 + use crate::{BorrowOrShare, Bos, IntoStatic}; 15 15 16 16 /// Trait for a collection of records that can be stored in a repository. 17 17 /// ··· 72 72 )] 73 73 #[serde(tag = "error", content = "message")] 74 74 #[non_exhaustive] 75 - pub enum RecordError<'a> { 75 + pub enum RecordError { 76 76 /// The requested record was not found 77 77 #[error("RecordNotFound")] 78 78 #[serde(rename = "RecordNotFound")] ··· 80 80 /// An unknown error occurred 81 81 #[error("Unknown")] 82 82 #[serde(rename = "Unknown")] 83 - #[serde(borrow)] 84 - Unknown(Data<'a>), 83 + Unknown(Data<SmolStr>), 85 84 } 86 85 87 - impl IntoStatic for RecordError<'_> { 88 - type Output = RecordError<'static>; 86 + impl IntoStatic for RecordError { 87 + type Output = RecordError; 89 88 90 89 fn into_static(self) -> Self::Output { 91 90 match self {
+11 -2
crates/jacquard-common/src/types/did.rs
··· 12 12 #[cfg(target_arch = "wasm32")] 13 13 use regex_lite::Regex; 14 14 use serde::{Deserialize, Deserializer, Serialize}; 15 - use smol_str::{SmolStr, ToSmolStr}; 15 + use smol_str::SmolStr; 16 16 17 17 use super::Lazy; 18 18 ··· 159 159 } 160 160 } 161 161 162 + impl<S: Bos<str>> Did<S> { 163 + /// Convert to a `Did` with a different backing type. 164 + pub fn convert<B: Bos<str> + From<S>>(self) -> Did<B> { 165 + Did(B::from(self.0)) 166 + } 167 + } 168 + 162 169 impl FromStr for Did { 163 170 type Err = AtStrError; 164 171 ··· 258 265 // new() does not strip — use new_owned() for that. 259 266 assert!(Did::<&str>::new("at://did:plc:foo").is_err()); 260 267 assert_eq!( 261 - Did::<SmolStr>::new_owned("at://did:plc:foo").unwrap().as_str(), 268 + Did::<SmolStr>::new_owned("at://did:plc:foo") 269 + .unwrap() 270 + .as_str(), 262 271 "did:plc:foo" 263 272 ); 264 273 assert_eq!(
+14 -21
crates/jacquard-common/src/types/did_doc.rs
··· 1 1 use crate::deps::fluent_uri::Uri; 2 2 use crate::types::crypto::{CryptoError, PublicKey}; 3 - use crate::types::string::{Did, Handle}; 3 + use crate::types::string::{AtprotoStr, Did, Handle}; 4 4 use crate::types::value::Data; 5 - use crate::{Bos, CowStr, DefaultStr, IntoStatic}; 5 + use crate::{Bos, DefaultStr, IntoStatic}; 6 6 use alloc::collections::BTreeMap; 7 - use alloc::string::String; 8 7 use alloc::vec::Vec; 9 8 use bon::Builder; 10 9 use serde::{Deserialize, Serialize}; ··· 69 68 #[serde(skip_serializing_if = "Option::is_none")] 70 69 pub service: Option<Vec<Service<S>>>, 71 70 // Forward‑compatible capture of unmodeled fields 72 - // TODO: re-enable extra data fields 73 - // #[serde(flatten)] 74 - // pub extra_data: BTreeMap<SmolStr, Data<'static>>, 71 + #[serde(flatten)] 72 + pub extra_data: BTreeMap<SmolStr, Data<S>>, 75 73 } 76 74 77 75 /// Default context fields for DID documents ··· 98 96 verification_method: self.verification_method.into_static(), 99 97 service: self.service.into_static(), 100 98 // TODO: re-enable extra data fields 101 - // extra_data: self.extra_data.into_static(), 99 + extra_data: self.extra_data.into_static(), 102 100 } 103 101 } 104 102 } ··· 135 133 }) 136 134 } 137 135 138 - /// Extract the AtprotoPersonalDataServer service endpoint as a `fluent_uri::Uri<String>`. 136 + /// Extract the AtprotoPersonalDataServer service endpoint as a `fluent_uri::Uri<&str>`. 139 137 /// Accepts endpoint as string or object (string preferred). 140 138 pub fn pds_endpoint(&self) -> Option<Uri<&str>> { 141 139 self.service.as_ref().and_then(|services| { 142 140 services.iter().find_map(|s| { 143 141 if s.r#type.as_ref() == "AtprotoPersonalDataServer" { 144 142 match &s.service_endpoint { 145 - Some(strv) => Uri::parse(strv.as_ref()).ok(), 146 - 143 + Some(Data::String(AtprotoStr::Uri(u))) => Uri::parse(u.as_ref()).ok(), 147 144 _ => None, 148 145 } 149 146 } else { ··· 185 182 #[serde(skip_serializing_if = "Option::is_none")] 186 183 pub public_key_multibase: Option<S>, 187 184 // Forward‑compatible capture of unmodeled fields 188 - // TODO: re-enable extra data fields 189 - // #[serde(flatten)] 190 - // pub extra_data: BTreeMap<SmolStr, Data<'static>>, 185 + #[serde(flatten)] 186 + pub extra_data: BTreeMap<SmolStr, Data<S>>, 191 187 } 192 188 193 189 impl<S> crate::IntoStatic for VerificationMethod<S> ··· 204 200 controller: self.controller.into_static(), 205 201 public_key_multibase: self.public_key_multibase.into_static(), 206 202 // TODO: re-enable extra data fields 207 - // extra_data: self.extra_data.into_static(), 203 + extra_data: self.extra_data.into_static(), 208 204 } 209 205 } 210 206 } ··· 224 220 #[serde(rename = "type")] 225 221 pub r#type: S, 226 222 /// currently atproto expects this to be a url 227 - /// 228 - /// TODO: add back in map/set support once Data<'_> is migrated 229 223 #[serde(skip_serializing_if = "Option::is_none")] 230 - pub service_endpoint: Option<S>, 224 + pub service_endpoint: Option<Data<S>>, 231 225 // Forward‑compatible capture of unmodeled fields 232 - // TODO: re-enable extra data fields 233 - // #[serde(flatten)] 234 - // pub extra_data: BTreeMap<SmolStr, Data<'static>>, 226 + #[serde(flatten)] 227 + pub extra_data: BTreeMap<SmolStr, Data<S>>, 235 228 } 236 229 237 230 impl<S> crate::IntoStatic for Service<S> ··· 247 240 r#type: self.r#type.into_static(), 248 241 service_endpoint: self.service_endpoint.into_static(), 249 242 // TODO: re-enable extra data fields 250 - // extra_data: self.extra_data.into_static(), 243 + extra_data: self.extra_data.into_static(), 251 244 } 252 245 } 253 246 }
+7
crates/jacquard-common/src/types/handle.rs
··· 232 232 } 233 233 } 234 234 235 + impl<S: Bos<str>> Handle<S> { 236 + /// Convert to a `Handle` with a different backing type. 237 + pub fn convert<B: Bos<str> + From<S>>(self) -> Handle<B> { 238 + Handle(B::from(self.0)) 239 + } 240 + } 241 + 235 242 impl FromStr for Handle { 236 243 type Err = AtStrError; 237 244
+12 -2
crates/jacquard-common/src/types/ident.rs
··· 12 12 13 13 use serde::{Deserialize, Serialize}; 14 14 15 - use smol_str::SmolStr; 16 - 17 15 /// AT Protocol identifier (either a DID or handle). 18 16 /// 19 17 /// Represents the union of DIDs and handles, which can both be used to identify ··· 131 129 } 132 130 } 133 131 132 + impl<S: Bos<str> + AsRef<str>> AtIdentifier<S> { 133 + /// Convert to an `AtIdentifier` with a different backing type. 134 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> AtIdentifier<B> { 135 + match self { 136 + AtIdentifier::Did(did) => AtIdentifier::Did(did.convert()), 137 + AtIdentifier::Handle(handle) => AtIdentifier::Handle(handle.convert()), 138 + } 139 + } 140 + } 141 + 134 142 impl<S: Bos<str> + AsRef<str>> From<Did<S>> for AtIdentifier<S> { 135 143 fn from(did: Did<S>) -> Self { 136 144 AtIdentifier::Did(did) ··· 202 210 203 211 #[cfg(test)] 204 212 mod tests { 213 + use smol_str::SmolStr; 214 + 205 215 use super::*; 206 216 use crate::cowstr::ToCowStr; 207 217
+7
crates/jacquard-common/src/types/nsid.rs
··· 131 131 } 132 132 } 133 133 134 + impl<S: Bos<str>> Nsid<S> { 135 + /// Convert to an `Nsid` with a different backing type. 136 + pub fn convert<B: Bos<str> + From<S>>(self) -> Nsid<B> { 137 + Nsid(B::from(self.0)) 138 + } 139 + } 140 + 134 141 impl FromStr for Nsid { 135 142 type Err = AtStrError; 136 143
+14
crates/jacquard-common/src/types/recordkey.rs
··· 98 98 } 99 99 } 100 100 101 + impl<T: RecordKeyType> RecordKey<T> { 102 + /// Convert the inner key to a different type. 103 + pub fn convert<U: RecordKeyType + From<T>>(self) -> RecordKey<U> { 104 + RecordKey(U::from(self.0)) 105 + } 106 + } 107 + 101 108 /// AT Protocol record key (generic "any" type) 102 109 /// 103 110 /// Record keys uniquely identify records within a collection. This is the catch-all ··· 229 236 230 237 fn into_static(self) -> Self::Output { 231 238 Rkey(self.0.into_static()) 239 + } 240 + } 241 + 242 + impl<S: Bos<str>> Rkey<S> { 243 + /// Convert to an `Rkey` with a different backing type. 244 + pub fn convert<B: Bos<str> + From<S>>(self) -> Rkey<B> { 245 + Rkey(B::from(self.0)) 232 246 } 233 247 } 234 248
+29 -10
crates/jacquard-common/src/types/string.rs
··· 1 + use crate::bos::{Bos, DefaultStr}; 1 2 use alloc::string::{String, ToString}; 2 3 use alloc::sync::Arc; 3 4 use core::str::FromStr; 4 5 #[cfg(feature = "std")] 5 6 use miette::{Diagnostic, SourceSpan}; 6 7 use serde::{Deserialize, Deserializer, Serialize, Serializer}; 7 - use smol_str::{SmolStr, ToSmolStr}; 8 + use smol_str::SmolStr; 8 9 9 10 /// Source span for error reporting (offset, length) 10 11 /// With `std` feature, this is `miette::SourceSpan`. Without, a simple tuple struct. ··· 26 27 } 27 28 } 28 29 29 - use crate::bos::{Bos, DefaultStr}; 30 - use crate::cowstr::ToCowStr; 31 30 pub use crate::{ 32 31 CowStr, 33 32 types::{ ··· 58 57 /// record keys are intentionally NOT parsed from bare strings as the validation 59 58 /// is too permissive and would catch too many values. 60 59 #[derive(Debug, Clone, PartialEq, Eq, Hash)] 61 - pub enum AtprotoStr<S: Bos<str> + AsRef<str> + Clone + Serialize = DefaultStr> { 60 + pub enum AtprotoStr<S: Bos<str> + AsRef<str> = DefaultStr> { 62 61 /// ISO 8601 datetime 63 62 Datetime(Datetime), 64 63 /// BCP 47 language tag ··· 90 89 use crate::types::handle::validate_handle; 91 90 use crate::types::nsid::validate_nsid; 92 91 93 - impl<S: Bos<str> + AsRef<str> + Clone + Serialize> AtprotoStr<S> { 92 + impl<S: Bos<str> + AsRef<str>> AtprotoStr<S> { 94 93 /// Classify and wrap a string value into the appropriate variant. 95 94 /// 96 95 /// This is fairly exhaustive and potentially **slow**, prefer using anything ··· 191 190 } 192 191 } 193 192 194 - impl<S: Bos<str> + AsRef<str> + Clone + Serialize> AsRef<str> for AtprotoStr<S> { 193 + impl<S: Bos<str> + AsRef<str>> AsRef<str> for AtprotoStr<S> { 195 194 fn as_ref(&self) -> &str { 196 195 self.as_str() 197 196 } 198 197 } 199 198 200 - impl<S: Bos<str> + AsRef<str> + Clone + Serialize> Serialize for AtprotoStr<S> { 199 + impl<S: Bos<str> + AsRef<str> + Serialize> Serialize for AtprotoStr<S> { 201 200 fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error> 202 201 where 203 202 Ser: Serializer, ··· 208 207 209 208 impl<'de, S> Deserialize<'de> for AtprotoStr<S> 210 209 where 211 - S: Bos<str> + AsRef<str> + Clone + Serialize + Deserialize<'de>, 210 + S: Bos<str> + AsRef<str> + Deserialize<'de>, 212 211 { 213 212 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 214 213 where ··· 219 218 } 220 219 } 221 220 222 - impl<S: Bos<str> + AsRef<str> + Clone + Serialize + IntoStatic> IntoStatic for AtprotoStr<S> 221 + impl<S: Bos<str> + AsRef<str>> AtprotoStr<S> { 222 + /// Convert to an `AtprotoStr` with a different backing type. 223 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> AtprotoStr<B> { 224 + match self { 225 + AtprotoStr::Datetime(dt) => AtprotoStr::Datetime(dt), 226 + AtprotoStr::Language(lang) => AtprotoStr::Language(lang), 227 + AtprotoStr::Tid(tid) => AtprotoStr::Tid(tid), 228 + AtprotoStr::Nsid(nsid) => AtprotoStr::Nsid(nsid.convert()), 229 + AtprotoStr::Did(did) => AtprotoStr::Did(did.convert()), 230 + AtprotoStr::Handle(handle) => AtprotoStr::Handle(handle.convert()), 231 + AtprotoStr::AtIdentifier(ident) => AtprotoStr::AtIdentifier(ident.convert()), 232 + AtprotoStr::AtUri(at_uri) => AtprotoStr::AtUri(at_uri.convert()), 233 + AtprotoStr::Uri(uri) => AtprotoStr::Uri(uri.convert()), 234 + AtprotoStr::Cid(cid) => AtprotoStr::Cid(cid.convert()), 235 + AtprotoStr::RecordKey(rkey) => AtprotoStr::RecordKey(RecordKey(rkey.0.convert())), 236 + AtprotoStr::String(s) => AtprotoStr::String(B::from(s)), 237 + } 238 + } 239 + } 240 + 241 + impl<S: Bos<str> + AsRef<str> + IntoStatic> IntoStatic for AtprotoStr<S> 223 242 where 224 - S::Output: Bos<str> + AsRef<str> + Clone + Serialize, 243 + S::Output: Bos<str> + AsRef<str>, 225 244 { 226 245 type Output = AtprotoStr<S::Output>; 227 246
+15 -1
crates/jacquard-common/src/types/uri.rs
··· 14 14 use alloc::string::{String, ToString}; 15 15 use core::{fmt::Display, marker::PhantomData, ops::Deref, str::FromStr}; 16 16 use serde::{Deserialize, Deserializer, Serialize, Serializer}; 17 - use smol_str::{SmolStr, ToSmolStr}; 17 + use smol_str::SmolStr; 18 18 19 19 /// Generic URI with type-specific parsing. 20 20 /// ··· 211 211 UriValue::Wss(url) => UriValue::Wss(url), 212 212 UriValue::Cid(cid) => UriValue::Cid(cid.into_static()), 213 213 UriValue::Any(s) => UriValue::Any(s.into_static()), 214 + } 215 + } 216 + } 217 + 218 + impl<S: Bos<str> + AsRef<str>> UriValue<S> { 219 + /// Convert to a `UriValue` with a different backing type. 220 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> UriValue<B> { 221 + match self { 222 + UriValue::Did(did) => UriValue::Did(did.convert()), 223 + UriValue::At(at_uri) => UriValue::At(at_uri.convert()), 224 + UriValue::Https(url) => UriValue::Https(url), 225 + UriValue::Wss(url) => UriValue::Wss(url), 226 + UriValue::Cid(cid) => UriValue::Cid(cid.convert()), 227 + UriValue::Any(s) => UriValue::Any(B::from(s)), 214 228 } 215 229 } 216 230 }
+177 -255
crates/jacquard-common/src/types/value.rs
··· 1 1 use crate::{ 2 - IntoStatic, 2 + Bos, DefaultStr, IntoStatic, 3 3 types::{DataModelType, LexiconStringType, UriType, blob::Blob, string::*}, 4 4 }; 5 5 use alloc::boxed::Box; 6 6 use alloc::collections::BTreeMap; 7 - use alloc::string::{String, ToString}; 7 + use alloc::string::ToString; 8 8 use alloc::vec::Vec; 9 9 use bytes::Bytes; 10 10 use core::convert::Infallible; 11 11 use ipld_core::ipld::Ipld; 12 + use serde::Serialize; 12 13 use smol_str::{SmolStr, ToSmolStr}; 13 14 14 15 /// Conversion utilities for Data types ··· 31 32 /// This is the generic "unknown data" type used for lexicon values, extra fields captured 32 33 /// by `#[lexicon]`, and IPLD data structures. 33 34 #[derive(Debug, Clone, PartialEq, Eq)] 34 - pub enum Data<'s> { 35 + pub enum Data<S: Bos<str> + AsRef<str> = DefaultStr> { 35 36 /// Null value 36 37 Null, 37 38 /// Boolean value ··· 39 40 /// Integer value (no floats in AT Protocol) 40 41 Integer(i64), 41 42 /// String value (parsed into specific AT Protocol types when possible) 42 - String(AtprotoStr<CowStr<'s>>), 43 + String(AtprotoStr<S>), 43 44 /// Raw bytes 44 45 Bytes(Bytes), 45 46 /// CID link reference 46 - CidLink(Cid<CowStr<'s>>), 47 + CidLink(Cid<S>), 47 48 /// Array of values 48 - Array(Array<'s>), 49 + Array(Array<S>), 49 50 /// Object/map of values 50 - Object(Object<'s>), 51 + Object(Object<S>), 51 52 /// Blob reference with metadata 52 - Blob(Blob<CowStr<'s>>), 53 + Blob(Blob<S>), 54 + /// Invalid number (floating point) 55 + InvalidNumber(S), 53 56 } 54 57 55 58 /// Errors that can occur when working with AT Protocol data ··· 61 64 FloatNotAllowed, 62 65 } 63 66 64 - impl<'s> Data<'s> { 67 + impl<S> Data<S> 68 + where 69 + S: Bos<str> + AsRef<str>, 70 + { 65 71 /// Get the data model type of this value 66 72 pub fn data_type(&self) -> DataModelType { 67 73 match self { ··· 98 104 Data::Array(_) => DataModelType::Array, 99 105 Data::Object(_) => DataModelType::Object, 100 106 Data::Blob(_) => DataModelType::Blob, 107 + Data::InvalidNumber(_) => DataModelType::Bytes, 101 108 } 102 109 } 103 - /// Parse a Data value from a JSON value 104 - pub fn from_json(json: &'s serde_json::Value) -> Result<Self, AtDataError> { 105 - Ok(if let Some(value) = json.as_bool() { 106 - Self::Boolean(value) 107 - } else if let Some(value) = json.as_i64() { 108 - Self::Integer(value) 109 - } else if let Some(value) = json.as_str() { 110 - Self::String(parsing::parse_string(value)) 111 - } else if let Some(value) = json.as_array() { 112 - Self::Array(Array::from_json(value)?) 113 - } else if let Some(value) = json.as_object() { 114 - Object::from_json(value)? 115 - } else if json.is_f64() { 116 - return Err(AtDataError::FloatNotAllowed); 117 - } else { 118 - Self::Null 119 - }) 120 - } 121 - 122 - /// Parse a Data value from a JSON value (owned) 123 - pub fn from_json_owned(json: serde_json::Value) -> Result<Data<'static>, AtDataError> { 124 - Data::from_json(&json).map(|data| data.into_static()) 125 - } 126 110 127 111 /// Get as object if this is an Object variant 128 - pub fn as_object(&self) -> Option<&Object<'s>> { 112 + pub fn as_object(&self) -> Option<&Object<S>> { 129 113 if let Data::Object(obj) = self { 130 114 Some(obj) 131 115 } else { ··· 134 118 } 135 119 136 120 /// Get as array if this is an Array variant 137 - pub fn as_array(&self) -> Option<&Array<'s>> { 121 + pub fn as_array(&self) -> Option<&Array<S>> { 138 122 if let Data::Array(arr) = self { 139 123 Some(arr) 140 124 } else { ··· 152 136 } 153 137 154 138 /// Get as object if this is an Object variant 155 - pub fn as_object_mut<'a>(&'a mut self) -> Option<&'a mut Object<'s>> { 139 + pub fn as_object_mut<'a>(&'a mut self) -> Option<&'a mut Object<S>> { 156 140 if let Data::Object(obj) = self { 157 141 Some(obj) 158 142 } else { ··· 161 145 } 162 146 163 147 /// Get as array if this is an Array variant 164 - pub fn as_array_mut<'a>(&'a mut self) -> Option<&'a mut Array<'s>> { 148 + pub fn as_array_mut<'a>(&'a mut self) -> Option<&'a mut Array<S>> { 165 149 if let Data::Array(arr) = self { 166 150 Some(arr) 167 151 } else { ··· 170 154 } 171 155 172 156 /// Get as string if this is a String variant 173 - pub fn as_str_mut(&'s mut self) -> Option<&'s mut AtprotoStr<CowStr<'s>>> { 157 + pub fn as_str_mut<'s>(&'s mut self) -> Option<&'s mut AtprotoStr<S>> { 174 158 if let Data::String(s) = self { 175 159 Some(s) 176 160 } else { ··· 232 216 /// This produces the deterministic CBOR encoding used for content-addressing. 233 217 pub fn to_dag_cbor( 234 218 &self, 235 - ) -> Result<Vec<u8>, serde_ipld_dagcbor::EncodeError<alloc::collections::TryReserveError>> { 219 + ) -> Result<Vec<u8>, serde_ipld_dagcbor::EncodeError<alloc::collections::TryReserveError>> 220 + where 221 + S: Serialize, 222 + { 236 223 serde_ipld_dagcbor::to_vec(self) 237 224 } 238 225 ··· 250 237 /// println!("Alt text: {}", alt_text.as_str().unwrap()); 251 238 /// } 252 239 /// ``` 253 - pub fn get_at_path(&'s self, path: &str) -> Option<&'s Data<'s>> { 240 + pub fn get_at_path<'s>(&'s self, path: &str) -> Option<&'s Data<S>> { 254 241 parse_and_traverse_path(self, path) 255 242 } 256 243 257 244 /// Get a mutable reference to a field at the given path 258 245 /// 259 246 /// Uses the same path syntax as [`get_at_path`](Self::get_at_path). 260 - pub fn get_at_path_mut(&mut self, path: &str) -> Option<&mut Data<'s>> { 247 + pub fn get_at_path_mut(&mut self, path: &str) -> Option<&mut Data<S>> { 261 248 parse_and_traverse_path_mut(self, path) 262 249 } 263 250 264 251 /// Set the value at the given path, returning true if successful 265 252 /// 266 253 /// Uses the same path syntax as [`get_at_path`](Self::get_at_path). 267 - pub fn set_at_path(&mut self, path: &str, new_data: Data<'_>) -> bool { 254 + pub fn set_at_path(&mut self, path: &str, new_data: Data<S>) -> bool { 268 255 if let Some(data) = parse_and_traverse_path_mut(self, path) { 269 - *data = new_data.into_static(); 256 + *data = new_data; 270 257 true 271 258 } else { 272 259 false ··· 292 279 /// // Global recursion 293 280 /// let all_cids = data.query("...cid"); // all CIDs anywhere 294 281 /// ``` 295 - pub fn query(&'s self, pattern: &str) -> QueryResult<'s> { 282 + pub fn query<'s>(&'s self, pattern: &str) -> QueryResult<'s, S> { 296 283 query_data(self, pattern) 297 284 } 285 + } 298 286 299 - /// Parse a Data value from an IPLD value (CBOR) 300 - pub fn from_cbor(cbor: &'s Ipld) -> Result<Self, AtDataError> { 301 - Ok(match cbor { 302 - Ipld::Null => Data::Null, 303 - Ipld::Bool(bool) => Data::Boolean(*bool), 304 - Ipld::Integer(int) => Data::Integer(*int as i64), 305 - Ipld::Float(_) => { 306 - return Err(AtDataError::FloatNotAllowed); 307 - } 308 - Ipld::String(string) => Self::String(parsing::parse_string(string)), 309 - Ipld::Bytes(items) => Self::Bytes(Bytes::copy_from_slice(items.as_slice())), 310 - Ipld::List(iplds) => Self::Array(Array::from_cbor(iplds)?), 311 - Ipld::Map(btree_map) => Object::from_cbor(btree_map)?, 312 - Ipld::Link(cid) => Self::CidLink(Cid::ipld(*cid)), 313 - }) 287 + impl<S: Bos<str> + AsRef<str>> Data<S> { 288 + /// Convert to a `Data` with a different backing type. 289 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> Data<B> { 290 + match self { 291 + Data::Null => Data::Null, 292 + Data::Boolean(b) => Data::Boolean(b), 293 + Data::Integer(i) => Data::Integer(i), 294 + Data::String(s) => Data::String(s.convert()), 295 + Data::Bytes(b) => Data::Bytes(b), 296 + Data::CidLink(cid) => Data::CidLink(cid.convert()), 297 + Data::Array(arr) => Data::Array(arr.convert()), 298 + Data::Object(obj) => Data::Object(obj.convert()), 299 + Data::Blob(blob) => Data::Blob(blob.convert()), 300 + Data::InvalidNumber(float) => Data::InvalidNumber(float.into()), 301 + } 314 302 } 315 303 } 316 304 317 - impl IntoStatic for Data<'_> { 318 - type Output = Data<'static>; 319 - fn into_static(self) -> Data<'static> { 305 + impl<S> IntoStatic for Data<S> 306 + where 307 + S: Bos<str> + AsRef<str> + IntoStatic, 308 + <S as IntoStatic>::Output: AsRef<str> + Bos<str>, 309 + { 310 + type Output = Data<<S as IntoStatic>::Output>; 311 + fn into_static(self) -> Data<<S as IntoStatic>::Output> { 320 312 match self { 321 313 Data::Null => Data::Null, 322 314 Data::Boolean(bool) => Data::Boolean(bool), ··· 327 319 Data::Object(object) => Data::Object(object.into_static()), 328 320 Data::CidLink(cid) => Data::CidLink(cid.into_static()), 329 321 Data::Blob(blob) => Data::Blob(blob.into_static()), 322 + Data::InvalidNumber(float) => Data::InvalidNumber(float.into_static()), 330 323 } 331 324 } 332 325 } 333 326 334 327 /// Array of AT Protocol data values 335 328 #[derive(Debug, Clone, PartialEq, Eq)] 336 - pub struct Array<'s>(pub Vec<Data<'s>>); 329 + pub struct Array<S = DefaultStr>(pub Vec<Data<S>>) 330 + where 331 + S: Bos<str> + AsRef<str>; 332 + 333 + impl<S: Bos<str> + AsRef<str>> Array<S> { 334 + /// Convert to an `Array` with a different backing type. 335 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> Array<B> { 336 + Array(self.0.into_iter().map(|d| d.convert()).collect()) 337 + } 338 + } 337 339 338 - impl IntoStatic for Array<'_> { 339 - type Output = Array<'static>; 340 - fn into_static(self) -> Array<'static> { 340 + impl<S> IntoStatic for Array<S> 341 + where 342 + S: Bos<str> + AsRef<str> + IntoStatic, 343 + <S as IntoStatic>::Output: AsRef<str> + Bos<str>, 344 + { 345 + type Output = Array<<S as IntoStatic>::Output>; 346 + fn into_static(self) -> Array<<S as IntoStatic>::Output> { 341 347 Array(self.0.into_static()) 342 348 } 343 349 } 344 350 345 - impl<'s> Array<'s> { 351 + impl<S> Array<S> 352 + where 353 + S: Bos<str> + AsRef<str>, 354 + { 346 355 /// Get the number of elements in the array 347 356 pub fn len(&self) -> usize { 348 357 self.0.len() ··· 354 363 } 355 364 356 365 /// Get an element by index 357 - pub fn get(&self, index: usize) -> Option<&Data<'s>> { 366 + pub fn get(&self, index: usize) -> Option<&Data<S>> { 358 367 self.0.get(index) 359 368 } 360 369 361 370 /// Get a mutable reference to an element by index 362 - pub fn get_mut(&mut self, index: usize) -> Option<&mut Data<'s>> { 371 + pub fn get_mut(&mut self, index: usize) -> Option<&mut Data<S>> { 363 372 self.0.get_mut(index) 364 373 } 365 374 366 375 /// Get an iterator over the array elements 367 - pub fn iter(&self) -> core::slice::Iter<'_, Data<'s>> { 376 + pub fn iter(&self) -> core::slice::Iter<'_, Data<S>> { 368 377 self.0.iter() 369 378 } 370 - 371 - /// Parse an array from JSON values 372 - pub fn from_json(json: &'s Vec<serde_json::Value>) -> Result<Self, AtDataError> { 373 - let mut array = Vec::with_capacity(json.len()); 374 - for item in json { 375 - array.push(Data::from_json(item)?); 376 - } 377 - Ok(Self(array)) 378 - } 379 - /// Parse an array from IPLD values (CBOR) 380 - pub fn from_cbor(cbor: &'s Vec<Ipld>) -> Result<Self, AtDataError> { 381 - let mut array = Vec::with_capacity(cbor.len()); 382 - for item in cbor { 383 - array.push(Data::from_cbor(item)?); 384 - } 385 - Ok(Self(array)) 386 - } 387 379 } 388 380 389 - impl<'s> core::ops::Index<usize> for Array<'s> { 390 - type Output = Data<'s>; 381 + impl<S> core::ops::Index<usize> for Array<S> 382 + where 383 + S: AsRef<str> + Bos<str>, 384 + { 385 + type Output = Data<S>; 391 386 392 387 fn index(&self, index: usize) -> &Self::Output { 393 388 &self.0[index] ··· 396 391 397 392 /// Object/map of AT Protocol data values 398 393 #[derive(Debug, Clone, PartialEq, Eq)] 399 - pub struct Object<'s>(pub BTreeMap<SmolStr, Data<'s>>); 394 + pub struct Object<S = DefaultStr>(pub BTreeMap<SmolStr, Data<S>>) 395 + where 396 + S: Bos<str> + AsRef<str>; 397 + 398 + impl<S: Bos<str> + AsRef<str>> Object<S> { 399 + /// Convert to an `Object` with a different backing type. 400 + pub fn convert<B: Bos<str> + AsRef<str> + From<S>>(self) -> Object<B> { 401 + Object(self.0.into_iter().map(|(k, v)| (k, v.convert())).collect()) 402 + } 403 + } 400 404 401 - impl IntoStatic for Object<'_> { 402 - type Output = Object<'static>; 403 - fn into_static(self) -> Object<'static> { 405 + impl<S> IntoStatic for Object<S> 406 + where 407 + S: Bos<str> + AsRef<str> + IntoStatic, 408 + <S as IntoStatic>::Output: AsRef<str> + Bos<str>, 409 + { 410 + type Output = Object<<S as IntoStatic>::Output>; 411 + fn into_static(self) -> Object<<S as IntoStatic>::Output> { 404 412 Object(self.0.into_static()) 405 413 } 406 414 } 407 415 408 - impl<'s> Object<'s> { 416 + impl<S> Object<S> 417 + where 418 + S: AsRef<str> + Bos<str>, 419 + { 409 420 /// Get a value by key 410 - pub fn get(&self, key: &str) -> Option<&Data<'s>> { 421 + pub fn get(&self, key: &str) -> Option<&Data<S>> { 411 422 self.0.get(key) 412 423 } 413 424 414 425 /// Get a mutable reference to a value by key 415 - pub fn get_mut(&mut self, key: &str) -> Option<&mut Data<'s>> { 426 + pub fn get_mut(&mut self, key: &str) -> Option<&mut Data<S>> { 416 427 self.0.get_mut(key) 417 428 } 418 429 ··· 432 443 } 433 444 434 445 /// Get an iterator over the key-value pairs 435 - pub fn iter(&self) -> alloc::collections::btree_map::Iter<'_, SmolStr, Data<'s>> { 446 + pub fn iter(&self) -> alloc::collections::btree_map::Iter<'_, SmolStr, Data<S>> { 436 447 self.0.iter() 437 448 } 438 449 439 450 /// Get an iterator over the keys 440 - pub fn keys(&self) -> alloc::collections::btree_map::Keys<'_, SmolStr, Data<'s>> { 451 + pub fn keys(&self) -> alloc::collections::btree_map::Keys<'_, SmolStr, Data<S>> { 441 452 self.0.keys() 442 453 } 443 454 ··· 449 460 } 450 461 451 462 /// Get an iterator over the values 452 - pub fn values(&self) -> alloc::collections::btree_map::Values<'_, SmolStr, Data<'s>> { 463 + pub fn values(&self) -> alloc::collections::btree_map::Values<'_, SmolStr, Data<S>> { 453 464 self.0.values() 454 465 } 455 - 456 - /// Parse an object from a JSON map with type inference 457 - /// 458 - /// Uses key names to infer the appropriate AT Protocol types for values. 459 - pub fn from_json( 460 - json: &'s serde_json::Map<String, serde_json::Value>, 461 - ) -> Result<Data<'s>, AtDataError> { 462 - if let Some(type_field) = json.get("$type").and_then(|v| v.as_str()) { 463 - if parsing::infer_from_type(type_field) == DataModelType::Blob { 464 - if let Some(blob) = parsing::json_to_blob(json) { 465 - return Ok(Data::Blob(blob)); 466 - } 467 - } 468 - } 469 - let mut map = BTreeMap::new(); 470 - 471 - for (key, value) in json { 472 - if key == "$type" { 473 - map.insert(key.to_smolstr(), Data::from_json(value)?); 474 - } 475 - match parsing::string_key_type_guess(key) { 476 - DataModelType::Null if value.is_null() => { 477 - map.insert(key.to_smolstr(), Data::Null); 478 - } 479 - DataModelType::Boolean if value.is_boolean() => { 480 - map.insert(key.to_smolstr(), Data::Boolean(value.as_bool().unwrap())); 481 - } 482 - DataModelType::Integer if value.is_i64() => { 483 - map.insert(key.to_smolstr(), Data::Integer(value.as_i64().unwrap())); 484 - } 485 - DataModelType::Bytes if value.is_string() => { 486 - map.insert( 487 - key.to_smolstr(), 488 - parsing::decode_bytes(value.as_str().unwrap()), 489 - ); 490 - } 491 - DataModelType::CidLink => { 492 - if let Some(value) = value.as_object() { 493 - if let Some(value) = value.get("$link").and_then(|v| v.as_str()) { 494 - map.insert(key.to_smolstr(), Data::CidLink(Cid::Str(value.into()))); 495 - } else { 496 - map.insert(key.to_smolstr(), Object::from_json(value)?); 497 - } 498 - } else { 499 - map.insert(key.to_smolstr(), Data::from_json(value)?); 500 - } 501 - } 502 - DataModelType::Blob if value.is_object() => { 503 - map.insert( 504 - key.to_smolstr(), 505 - Object::from_json(value.as_object().unwrap())?, 506 - ); 507 - } 508 - DataModelType::Array if value.is_array() => { 509 - map.insert( 510 - key.to_smolstr(), 511 - Data::Array(Array::from_json(value.as_array().unwrap())?), 512 - ); 513 - } 514 - DataModelType::Object if value.is_object() => { 515 - map.insert( 516 - key.to_smolstr(), 517 - Object::from_json(value.as_object().unwrap())?, 518 - ); 519 - } 520 - DataModelType::String(string_type) if value.is_string() => { 521 - parsing::insert_string(&mut map, key, value.as_str().unwrap(), string_type)?; 522 - } 523 - _ => { 524 - map.insert(key.to_smolstr(), Data::from_json(value)?); 525 - } 526 - } 527 - } 528 - 529 - Ok(Data::Object(Object(map))) 530 - } 531 - 532 - /// Parse an object from IPLD (CBOR) with type inference 533 - /// 534 - /// Uses key names to infer the appropriate AT Protocol types for values. 535 - pub fn from_cbor(cbor: &'s BTreeMap<String, Ipld>) -> Result<Data<'s>, AtDataError> { 536 - if let Some(Ipld::String(type_field)) = cbor.get("$type") { 537 - if parsing::infer_from_type(type_field) == DataModelType::Blob { 538 - if let Some(blob) = parsing::cbor_to_blob(cbor) { 539 - return Ok(Data::Blob(blob)); 540 - } 541 - } 542 - } 543 - let mut map = BTreeMap::new(); 544 - 545 - for (key, value) in cbor { 546 - if key == "$type" { 547 - map.insert(key.to_smolstr(), Data::from_cbor(value)?); 548 - } 549 - match (parsing::string_key_type_guess(key), value) { 550 - (DataModelType::Null, Ipld::Null) => { 551 - map.insert(key.to_smolstr(), Data::Null); 552 - } 553 - (DataModelType::Boolean, Ipld::Bool(value)) => { 554 - map.insert(key.to_smolstr(), Data::Boolean(*value)); 555 - } 556 - (DataModelType::Integer, Ipld::Integer(int)) => { 557 - map.insert(key.to_smolstr(), Data::Integer(*int as i64)); 558 - } 559 - (DataModelType::Bytes, Ipld::Bytes(value)) => { 560 - map.insert(key.to_smolstr(), Data::Bytes(Bytes::copy_from_slice(value))); 561 - } 562 - (DataModelType::Blob, Ipld::Map(value)) => { 563 - map.insert(key.to_smolstr(), Object::from_cbor(value)?); 564 - } 565 - (DataModelType::Array, Ipld::List(value)) => { 566 - map.insert(key.to_smolstr(), Data::Array(Array::from_cbor(value)?)); 567 - } 568 - (DataModelType::Object, Ipld::Map(value)) => { 569 - map.insert(key.to_smolstr(), Object::from_cbor(value)?); 570 - } 571 - (DataModelType::String(string_type), Ipld::String(value)) => { 572 - parsing::insert_string(&mut map, key, value, string_type)?; 573 - } 574 - _ => { 575 - map.insert(key.to_smolstr(), Data::from_cbor(value)?); 576 - } 577 - } 578 - } 579 - 580 - Ok(Data::Object(Object(map))) 581 - } 582 466 } 583 467 584 - impl<'s> core::ops::Index<&str> for Object<'s> { 585 - type Output = Data<'s>; 468 + impl<S> core::ops::Index<&str> for Object<S> 469 + where 470 + S: AsRef<str> + Bos<str>, 471 + { 472 + type Output = Data<S>; 586 473 587 474 fn index(&self, key: &str) -> &Self::Output { 588 475 &self.0[key] ··· 834 721 /// # Ok(()) 835 722 /// # } 836 723 /// ``` 837 - pub fn from_data<'de, T>(data: &'de Data<'de>) -> Result<T, DataDeserializerError> 724 + pub fn from_data<'de, T, S>(data: &'de Data<S>) -> Result<T, DataDeserializerError> 838 725 where 839 726 T: serde::Deserialize<'de>, 727 + S: Bos<str> + AsRef<str> + serde::Deserialize<'de> + core::convert::From<&'de str>, 840 728 { 841 729 T::deserialize(data) 842 730 } ··· 844 732 /// Deserialize a typed value from a `Data` value 845 733 /// 846 734 /// Takes ownership rather than borrows. Will allocate. 847 - pub fn from_data_owned<'de, T>(data: Data<'_>) -> Result<T, DataDeserializerError> 735 + pub fn from_data_owned<'de, T, S>( 736 + data: Data<S>, 737 + ) -> Result<<T as IntoStatic>::Output, DataDeserializerError> 848 738 where 849 - T: serde::Deserialize<'de>, 739 + T: serde::Deserialize<'de> + IntoStatic, 740 + S: Bos<str> + AsRef<str> + serde::Deserialize<'de> + IntoStatic + core::convert::From<&'de str>, 741 + <S as IntoStatic>::Output: Bos<str> + AsRef<str>, 850 742 { 851 - T::deserialize(data.into_static()) 743 + T::deserialize(data).map(|d| d.into_static()) 852 744 } 853 745 854 746 /// Deserialize a typed value from a `serde_json::Value` ··· 976 868 /// # Ok(()) 977 869 /// # } 978 870 /// ``` 979 - pub fn to_data<T>(value: &T) -> Result<Data<'static>, convert::ConversionError> 871 + pub fn to_data<'s, T, S>(value: &T) -> Result<Data<S>, convert::ConversionError> 980 872 where 981 873 T: serde::Serialize, 874 + S: Bos<str> + AsRef<str> + serde::Serialize + From<CowStr<'s>>, 982 875 { 983 876 let raw = to_raw_data(value).map_err(|e| convert::ConversionError::InvalidRawData { 984 877 message: e.to_string(), ··· 987 880 } 988 881 989 882 /// Parse and traverse a path through nested Data structures 990 - fn parse_and_traverse_path<'s>(data: &'s Data<'s>, path: &str) -> Option<&'s Data<'s>> { 883 + fn parse_and_traverse_path<'s, S>(data: &'s Data<S>, path: &str) -> Option<&'s Data<S>> 884 + where 885 + S: AsRef<str> + Bos<str>, 886 + { 991 887 let mut current = data; 992 888 let mut path = path.trim_start_matches('.'); 993 889 ··· 1049 945 } 1050 946 1051 947 /// Parse and traverse a path through nested Data structures 1052 - fn parse_and_traverse_path_mut<'d, 's>( 1053 - data: &'s mut Data<'d>, 948 + fn parse_and_traverse_path_mut<'d, 's, S>( 949 + data: &'s mut Data<S>, 1054 950 path: &str, 1055 - ) -> Option<&'s mut Data<'d>> { 951 + ) -> Option<&'s mut Data<S>> 952 + where 953 + S: AsRef<str> + Bos<str>, 954 + { 1056 955 let mut current = data; 1057 956 let mut path = path.trim_start_matches('.'); 1058 957 ··· 1118 1017 1119 1018 /// Result of a data query operation 1120 1019 #[derive(Debug, Clone, PartialEq)] 1121 - pub enum QueryResult<'s> { 1020 + pub enum QueryResult<'s, S> 1021 + where 1022 + S: AsRef<str> + Bos<str>, 1023 + { 1122 1024 /// Single value expected and found 1123 - Single(&'s Data<'s>), 1025 + Single(&'s Data<S>), 1124 1026 1125 1027 /// Multiple values from wildcard or global recursion 1126 - Multiple(Vec<QueryMatch<'s>>), 1028 + Multiple(Vec<QueryMatch<'s, S>>), 1127 1029 1128 1030 /// No matches found 1129 1031 None, 1130 1032 } 1131 1033 1132 - impl<'s> QueryResult<'s> { 1034 + impl<'s, S> QueryResult<'s, S> 1035 + where 1036 + S: AsRef<str> + Bos<str>, 1037 + { 1133 1038 /// Get single value if available 1134 - pub fn single(&self) -> Option<&'s Data<'s>> { 1039 + pub fn single(&self) -> Option<&'s Data<S>> { 1135 1040 match self { 1136 1041 QueryResult::Single(data) => Some(data), 1137 1042 _ => None, ··· 1139 1044 } 1140 1045 1141 1046 /// Get multiple matches if available 1142 - pub fn multiple(&self) -> Option<&[QueryMatch<'s>]> { 1047 + pub fn multiple(&self) -> Option<&[QueryMatch<'s, S>]> { 1143 1048 match self { 1144 1049 QueryResult::Multiple(matches) => Some(matches), 1145 1050 _ => None, ··· 1147 1052 } 1148 1053 1149 1054 /// Get first value regardless of result type 1150 - pub fn first(&self) -> Option<&'s Data<'s>> { 1055 + pub fn first(&self) -> Option<&'s Data<S>> { 1151 1056 match self { 1152 1057 QueryResult::Single(data) => Some(data), 1153 1058 QueryResult::Multiple(matches) => matches.first().and_then(|m| m.value), ··· 1161 1066 } 1162 1067 1163 1068 /// Get all values as an iterator (flattens single/multiple) 1164 - pub fn values(&self) -> impl Iterator<Item = &'s Data<'s>> { 1069 + pub fn values(&self) -> impl Iterator<Item = &'s Data<S>> { 1165 1070 match self { 1166 1071 QueryResult::Single(data) => vec![*data].into_iter(), 1167 1072 QueryResult::Multiple(matches) => matches ··· 1176 1081 1177 1082 /// A single match from a query operation 1178 1083 #[derive(Debug, Clone, PartialEq)] 1179 - pub struct QueryMatch<'s> { 1084 + pub struct QueryMatch<'s, S> 1085 + where 1086 + S: AsRef<str> + Bos<str>, 1087 + { 1180 1088 /// Path where this value was found (e.g., "actors\[0\].handle") 1181 1089 pub path: SmolStr, 1182 1090 /// The value (None if field was missing during wildcard iteration) 1183 - pub value: Option<&'s Data<'s>>, 1091 + pub value: Option<&'s Data<S>>, 1184 1092 } 1185 1093 1186 1094 /// Query pattern segment ··· 1256 1164 } 1257 1165 1258 1166 /// Execute a query on data 1259 - fn query_data<'s>(data: &'s Data<'s>, pattern: &str) -> QueryResult<'s> { 1167 + fn query_data<'s, S>(data: &'s Data<S>, pattern: &str) -> QueryResult<'s, S> 1168 + where 1169 + S: AsRef<str> + Bos<str>, 1170 + { 1260 1171 let segments = parse_query_pattern(pattern); 1261 1172 if segments.is_empty() { 1262 1173 return QueryResult::None; ··· 1294 1205 } 1295 1206 1296 1207 /// Execute a single segment on current results 1297 - fn execute_segment<'s>(current: &[QueryMatch<'s>], segment: &QuerySegment) -> Vec<QueryMatch<'s>> { 1208 + fn execute_segment<'s, S>( 1209 + current: &[QueryMatch<'s, S>], 1210 + segment: &QuerySegment, 1211 + ) -> Vec<QueryMatch<'s, S>> 1212 + where 1213 + S: AsRef<str> + Bos<str>, 1214 + { 1298 1215 let mut next = Vec::new(); 1299 1216 1300 1217 for qm in current { ··· 1351 1268 } 1352 1269 1353 1270 /// Recursively find first occurrence of a field (scoped recursion) 1354 - fn find_field_recursive<'s>( 1355 - data: &'s Data<'s>, 1271 + fn find_field_recursive<'s, S>( 1272 + data: &'s Data<S>, 1356 1273 field: &str, 1357 1274 base_path: &SmolStr, 1358 - ) -> Option<QueryMatch<'s>> { 1275 + ) -> Option<QueryMatch<'s, S>> 1276 + where 1277 + S: AsRef<str> + Bos<str>, 1278 + { 1359 1279 match data { 1360 1280 Data::Object(obj) => { 1361 1281 // Check direct children first ··· 1390 1310 } 1391 1311 1392 1312 /// Recursively find all occurrences of a field (global recursion) 1393 - fn find_all_fields_recursive<'s>( 1394 - data: &'s Data<'s>, 1313 + fn find_all_fields_recursive<'s, S>( 1314 + data: &'s Data<S>, 1395 1315 field: &str, 1396 1316 base_path: &SmolStr, 1397 - results: &mut Vec<QueryMatch<'s>>, 1398 - ) { 1317 + results: &mut Vec<QueryMatch<'s, S>>, 1318 + ) where 1319 + S: AsRef<str> + Bos<str>, 1320 + { 1399 1321 match data { 1400 1322 Data::Object(obj) => { 1401 1323 // Check direct children
+129 -46
crates/jacquard-common/src/types/value/convert.rs
··· 5 5 string::AtprotoStr, 6 6 value::{Array, Data, Object, RawData, parsing}, 7 7 }; 8 - use crate::{CowStr, IntoStatic}; 9 - use alloc::borrow::ToOwned; 8 + use crate::{Bos, CowStr}; 10 9 use alloc::boxed::Box; 11 10 use alloc::collections::BTreeMap; 12 11 use alloc::string::String; ··· 14 13 use alloc::vec::Vec; 15 14 use bytes::Bytes; 16 15 use core::any::TypeId; 16 + use core::convert::Infallible; 17 + use core::str::FromStr; 18 + use serde::Serialize; 17 19 use smol_str::SmolStr; 18 20 use std::borrow::Cow; 19 21 ··· 45 47 }, 46 48 } 47 49 48 - impl TryFrom<Data<'_>> for () { 50 + impl<S> TryFrom<Data<S>> for () 51 + where 52 + S: AsRef<str> + Bos<str>, 53 + { 49 54 type Error = ConversionError; 50 55 51 - fn try_from(ipld: Data) -> Result<Self, Self::Error> { 56 + fn try_from(ipld: Data<S>) -> Result<Self, Self::Error> { 52 57 match ipld { 53 58 Data::Null => Ok(()), 54 59 _ => Err(ConversionError::WrongAtprotoType { ··· 61 66 62 67 macro_rules! derive_try_from_atproto_option { 63 68 ($enum:ident, $ty:ty) => { 64 - impl TryFrom<Data<'static>> for Option<$ty> { 69 + impl<S: 'static> TryFrom<Data<S>> for Option<$ty> 70 + where 71 + S: AsRef<str> + Bos<str>, 72 + { 65 73 type Error = ConversionError; 66 74 67 - fn try_from(ipld: Data<'static>) -> Result<Self, Self::Error> { 75 + fn try_from(ipld: Data<S>) -> Result<Self, Self::Error> { 68 76 match ipld { 69 77 Data::Null => Ok(None), 70 78 Data::$enum(value) => Ok(Some(value.try_into().map_err(|_| { ··· 85 93 86 94 macro_rules! derive_try_from_atproto { 87 95 ($enum:ident, $ty:ty) => { 88 - impl TryFrom<Data<'static>> for $ty { 96 + impl<S: 'static> TryFrom<Data<S>> for $ty 97 + where 98 + S: AsRef<str> + Bos<str>, 99 + { 89 100 type Error = ConversionError; 90 101 91 - fn try_from(ipld: Data<'static>) -> Result<Self, Self::Error> { 102 + fn try_from(ipld: Data<S>) -> Result<Self, Self::Error> { 92 103 match ipld { 93 104 Data::$enum(value) => { 94 105 Ok(value ··· 111 122 112 123 macro_rules! derive_into_atproto_prim { 113 124 ($enum:ident, $ty:ty, $fn:ident) => { 114 - impl<'s> From<$ty> for Data<'s> { 125 + impl<S> From<$ty> for Data<S> 126 + where 127 + S: AsRef<str> + Bos<str>, 128 + { 115 129 fn from(t: $ty) -> Self { 116 130 Data::$enum(t.$fn() as _) 117 131 } ··· 121 135 122 136 macro_rules! derive_into_atproto { 123 137 ($enum:ident, $ty:ty, $($fn:ident),*) => { 124 - impl<'s> From<$ty> for Data<'s> { 138 + impl<S> From<$ty> for Data<S> 139 + where 140 + S: AsRef<str> + Bos<str>, { 125 141 fn from(t: $ty) -> Self { 126 142 Data::$enum(t$(.$fn())*) 127 143 } ··· 129 145 }; 130 146 } 131 147 132 - impl From<String> for Data<'_> { 148 + impl<S> From<String> for Data<S> 149 + where 150 + S: AsRef<str> + Bos<str> + From<String>, 151 + { 133 152 fn from(t: String) -> Self { 134 - Data::String(AtprotoStr::new(CowStr::from(t))) 153 + Data::String(AtprotoStr::new(t.into())) 135 154 } 136 155 } 137 156 138 - impl<'a> From<&'a str> for Data<'a> { 157 + impl<'a, S> From<&'a str> for Data<S> 158 + where 159 + S: AsRef<str> + Bos<str> + From<&'a str>, 160 + { 139 161 fn from(t: &'a str) -> Self { 140 - Data::String(AtprotoStr::new(CowStr::Borrowed(t))) 162 + Data::String(AtprotoStr::new(S::from(t))) 141 163 } 142 164 } 143 165 144 - impl From<&[u8]> for Data<'_> { 166 + impl<S> From<&[u8]> for Data<S> 167 + where 168 + S: AsRef<str> + Bos<str>, 169 + { 145 170 fn from(t: &[u8]) -> Self { 146 171 Data::Bytes(Bytes::copy_from_slice(t)) 147 172 } 148 173 } 149 174 150 - impl<'s> From<CowStr<'s>> for Data<'s> { 175 + impl<'s, S> From<CowStr<'s>> for Data<S> 176 + where 177 + S: AsRef<str> + Bos<str> + FromStr<Err = Infallible>, 178 + { 151 179 fn from(t: CowStr<'s>) -> Self { 152 - Data::String(AtprotoStr::new(t)) 180 + Data::String(AtprotoStr::new( 181 + S::from_str(t.as_ref()).unwrap_or_else(|_| unreachable!()), 182 + )) 153 183 } 154 184 } 155 185 156 - impl From<SmolStr> for Data<'_> { 186 + impl<S> From<SmolStr> for Data<S> 187 + where 188 + S: AsRef<str> + Bos<str> + From<SmolStr>, 189 + { 157 190 fn from(t: SmolStr) -> Self { 158 - Data::String(AtprotoStr::new(CowStr::Owned(t))) 191 + Data::String(AtprotoStr::new(S::from(t))) 159 192 } 160 193 } 161 194 162 - impl<'s> From<Cow<'s, str>> for Data<'s> { 195 + impl<'s, S> From<Cow<'s, str>> for Data<S> 196 + where 197 + S: AsRef<str> + Bos<str> + FromStr<Err = Infallible>, 198 + { 163 199 fn from(t: Cow<'s, str>) -> Self { 164 - Data::String(AtprotoStr::new(CowStr::from(t))) 200 + Data::String(AtprotoStr::new( 201 + S::from_str(t.as_ref()).unwrap_or_else(|_| unreachable!()), 202 + )) 165 203 } 166 204 } 167 205 168 - impl<'s> TryFrom<Data<'s>> for Option<String> { 206 + impl<S> TryFrom<Data<S>> for Option<String> 207 + where 208 + S: AsRef<str> + Bos<str> + Clone + Serialize, 209 + { 169 210 type Error = ConversionError; 170 211 171 - fn try_from(ipld: Data<'s>) -> Result<Self, Self::Error> { 212 + fn try_from(ipld: Data<S>) -> Result<Self, Self::Error> { 172 213 match ipld { 173 214 Data::Null => Ok(None), 174 215 Data::String(value) => Ok(Some(value.try_into().map_err(|_| { ··· 185 226 } 186 227 } 187 228 188 - impl<'s> TryFrom<Data<'s>> for String { 229 + impl<S> TryFrom<Data<S>> for String 230 + where 231 + S: AsRef<str> + Bos<str> + TryFrom<String> + Clone + Serialize, 232 + { 189 233 type Error = ConversionError; 190 234 191 - fn try_from(ipld: Data<'s>) -> Result<Self, Self::Error> { 235 + fn try_from(ipld: Data<S>) -> Result<Self, Self::Error> { 192 236 match ipld { 193 237 Data::String(value) => { 194 238 Ok(value ··· 207 251 } 208 252 } 209 253 210 - impl<'s> From<Vec<Data<'s>>> for Array<'s> { 211 - fn from(value: Vec<Data<'s>>) -> Self { 254 + impl<S> From<Vec<Data<S>>> for Array<S> 255 + where 256 + S: AsRef<str> + Bos<str>, 257 + { 258 + fn from(value: Vec<Data<S>>) -> Self { 212 259 Array(value) 213 260 } 214 261 } 215 262 216 - impl<'s> From<BTreeMap<SmolStr, Data<'s>>> for Object<'s> { 217 - fn from(value: BTreeMap<SmolStr, Data<'s>>) -> Self { 263 + impl<S> From<BTreeMap<SmolStr, Data<S>>> for Object<S> 264 + where 265 + S: AsRef<str> + Bos<str>, 266 + { 267 + fn from(value: BTreeMap<SmolStr, Data<S>>) -> Self { 218 268 Object(value) 219 269 } 220 270 } ··· 233 283 derive_into_atproto_prim!(Integer, usize, clone); 234 284 derive_into_atproto!(Bytes, Box<[u8]>, into); 235 285 derive_into_atproto!(Bytes, Vec<u8>, into); 236 - derive_into_atproto!(Array, Array<'s>, into); 237 - derive_into_atproto!(Object, Object<'s>, to_owned); 286 + derive_into_atproto!(Array, Array<S>,); 287 + derive_into_atproto!(Object, Object<S>,); 288 + 289 + derive_into_atproto!(CidLink, Cid<S>,); 290 + derive_into_atproto!(Blob, crate::types::blob::Blob<S>,); 291 + derive_into_atproto!(String, AtprotoStr<S>,); 292 + 293 + impl<S> From<CidLink<S>> for Data<S> 294 + where 295 + S: AsRef<str> + Bos<str>, 296 + { 297 + fn from(t: CidLink<S>) -> Self { 298 + Data::CidLink(t.0) 299 + } 300 + } 301 + 302 + impl<S> From<crate::types::blob::BlobRef<S>> for Data<S> 303 + where 304 + S: AsRef<str> + Bos<str>, 305 + { 306 + fn from(t: crate::types::blob::BlobRef<S>) -> Self { 307 + Data::Blob(t.into()) 308 + } 309 + } 238 310 239 - derive_into_atproto!(CidLink, Cid<CowStr<'s>>, clone); 240 - derive_into_atproto!(CidLink, &Cid<CowStr<'s>>, to_owned); 311 + impl<S> From<&Cid<S>> for Data<S> 312 + where 313 + S: AsRef<str> + Bos<str> + Clone, 314 + { 315 + fn from(t: &Cid<S>) -> Self { 316 + Data::CidLink(t.clone()) 317 + } 318 + } 241 319 242 320 derive_try_from_atproto!(Boolean, bool); 243 321 derive_try_from_atproto!(Integer, i8); ··· 253 331 derive_try_from_atproto!(Integer, u128); 254 332 derive_try_from_atproto!(Integer, usize); 255 333 derive_try_from_atproto!(Bytes, Vec<u8>); 256 - derive_try_from_atproto!(Object, Object<'static>); 257 - derive_try_from_atproto!(CidLink, Cid<CowStr<'static>>); 334 + derive_try_from_atproto!(Array, Array<S>); 335 + derive_try_from_atproto!(Object, Object<S>); 336 + derive_try_from_atproto!(CidLink, Cid<S>); 337 + derive_try_from_atproto!(Blob, crate::types::blob::Blob<S>); 258 338 259 339 derive_try_from_atproto_option!(Boolean, bool); 260 340 derive_try_from_atproto_option!(Integer, i8); ··· 271 351 derive_try_from_atproto_option!(Integer, usize); 272 352 273 353 derive_try_from_atproto_option!(Bytes, Vec<u8>); 274 - derive_try_from_atproto_option!(Array, Array<'static>); 275 - derive_try_from_atproto_option!(Object, Object<'static>); 276 - derive_try_from_atproto_option!(CidLink, Cid<CowStr<'static>>); 354 + derive_try_from_atproto_option!(Array, Array<S>); 355 + derive_try_from_atproto_option!(Object, Object<S>); 356 + derive_try_from_atproto_option!(CidLink, Cid<S>); 357 + derive_try_from_atproto_option!(Blob, crate::types::blob::Blob<S>); 277 358 278 359 /// Convert RawData to validated Data with type inference 279 - impl<'s> TryFrom<RawData<'s>> for Data<'s> { 360 + impl<'s, S> TryFrom<RawData<'s>> for Data<S> 361 + where 362 + S: Bos<str> + AsRef<str> + From<CowStr<'s>>, 363 + { 280 364 type Error = ConversionError; 281 365 282 366 fn try_from(raw: RawData<'s>) -> Result<Self, Self::Error> { ··· 290 374 } 291 375 RawData::String(s) => { 292 376 // Apply string type inference 293 - // Need to convert to owned because parse_string borrows from its input 294 - Ok(Data::String(parsing::parse_string(&s).into_static())) 377 + Ok(Data::String(parsing::parse_string(S::from(s)))) 295 378 } 296 379 RawData::Bytes(b) => Ok(Data::Bytes(b)), 297 - RawData::CidLink(cid) => Ok(Data::CidLink(cid)), 380 + RawData::CidLink(cid) => Ok(Data::CidLink(cid.convert())), 298 381 RawData::Array(arr) => { 299 382 let mut validated = Vec::with_capacity(arr.len()); 300 383 for item in arr { ··· 323 406 } 324 407 }; 325 408 return Ok(Data::Blob(crate::types::blob::Blob { 326 - r#ref: CidLink(cid.clone()), 327 - mime_type: crate::types::blob::MimeType::from(mime.clone()), 409 + r#ref: CidLink(cid.clone().convert()), 410 + mime_type: crate::types::blob::MimeType::new(S::from(mime.clone())), 328 411 size: size_val, 329 412 })); 330 413 } ··· 334 417 // Regular object - convert recursively with type inference based on keys 335 418 let mut validated = BTreeMap::new(); 336 419 for (key, value) in map { 337 - let data_value: Data = value.try_into()?; 420 + let data_value: Data<S> = value.try_into()?; 338 421 validated.insert(key, data_value); 339 422 } 340 423 Ok(Data::Object(Object(validated))) 341 424 } 342 - RawData::Blob(blob) => Ok(Data::Blob(blob)), 425 + RawData::Blob(blob) => Ok(Data::Blob(blob.convert())), 343 426 RawData::InvalidBlob(_) => Err(ConversionError::InvalidRawData { 344 427 message: "invalid blob structure".to_string(), 345 428 }),
+126 -99
crates/jacquard-common/src/types/value/parsing.rs
··· 1 - use crate::cowstr::ToCowStr; 1 + use crate::Bos; 2 2 use crate::deps::fluent_uri::Uri; 3 + use crate::types::aturi::validate_and_index; 4 + use crate::types::cid::IpldCid; 5 + use crate::types::did::validate_did; 6 + use crate::types::handle::validate_handle; 7 + use crate::types::nsid::validate_nsid; 3 8 use crate::{ 4 9 IntoStatic, 5 10 types::{ ··· 21 26 use smol_str::{SmolStr, ToSmolStr}; 22 27 23 28 /// Insert a string into an at:// `Data<'_>` map, inferring its type. 24 - pub fn insert_string<'s>( 25 - map: &mut BTreeMap<SmolStr, Data<'s>>, 29 + pub fn insert_string<'s, S>( 30 + map: &mut BTreeMap<SmolStr, Data<S>>, 26 31 key: &'s str, 27 - value: &'s str, 32 + value: S, 28 33 string_type: LexiconStringType, 29 - ) -> Result<(), AtDataError> { 34 + ) -> Result<(), AtDataError> 35 + where 36 + S: AsRef<str> + Bos<str>, 37 + { 30 38 match string_type { 31 39 LexiconStringType::Datetime => { 32 - if let Ok(datetime) = Datetime::from_str(value) { 40 + if let Ok(datetime) = Datetime::from_str(value.as_ref()) { 33 41 map.insert( 34 42 key.to_smolstr(), 35 43 Data::String(AtprotoStr::Datetime(datetime)), 36 44 ); 37 45 } else { 38 - map.insert( 39 - key.to_smolstr(), 40 - Data::String(AtprotoStr::String(value.into())), 41 - ); 46 + map.insert(key.to_smolstr(), Data::String(AtprotoStr::String(value))); 42 47 } 43 48 } 44 49 LexiconStringType::AtUri => { 45 - if let Ok(value) = AtUri::new(value.to_cowstr()) { 50 + if validate_and_index(value.as_ref()).is_ok() { 46 51 // AtprotoStr::AtUri stores AtUri<'static>; convert to owned. 47 52 map.insert( 48 53 key.to_smolstr(), 49 - Data::String(AtprotoStr::AtUri(value.into_static())), 54 + Data::String(AtprotoStr::AtUri(unsafe { AtUri::unchecked(value) })), 50 55 ); 51 56 } else { 52 - map.insert( 53 - key.to_smolstr(), 54 - Data::String(AtprotoStr::String(value.into())), 55 - ); 57 + map.insert(key.to_smolstr(), Data::String(AtprotoStr::String(value))); 56 58 } 57 59 } 58 60 LexiconStringType::Did => { 59 - if let Ok(value) = Did::new(value.to_cowstr()) { 60 - map.insert(key.to_smolstr(), Data::String(AtprotoStr::Did(value))); 61 - } else { 61 + if validate_did(value.as_ref()).is_ok() { 62 62 map.insert( 63 63 key.to_smolstr(), 64 - Data::String(AtprotoStr::String(value.into())), 64 + Data::String(AtprotoStr::Did(unsafe { Did::unchecked(value) })), 65 65 ); 66 + } else { 67 + map.insert(key.to_smolstr(), Data::String(AtprotoStr::String(value))); 66 68 } 67 69 } 68 70 LexiconStringType::Handle => { 69 - if let Ok(value) = Handle::new(value.to_cowstr()) { 70 - map.insert(key.to_smolstr(), Data::String(AtprotoStr::Handle(value))); 71 - } else { 71 + if validate_handle(value.as_ref()).is_ok() { 72 72 map.insert( 73 73 key.to_smolstr(), 74 - Data::String(AtprotoStr::String(value.into())), 74 + Data::String(AtprotoStr::Handle(unsafe { Handle::unchecked(value) })), 75 75 ); 76 + } else { 77 + map.insert(key.to_smolstr(), Data::String(AtprotoStr::String(value))); 76 78 } 77 79 } 78 80 LexiconStringType::AtIdentifier => { 79 - if let Ok(value) = AtIdentifier::new(value.to_cowstr()) { 81 + if validate_handle(value.as_ref()).is_ok() || validate_did(value.as_ref()).is_ok() { 80 82 map.insert( 81 83 key.to_smolstr(), 82 - Data::String(AtprotoStr::AtIdentifier(value)), 84 + Data::String(AtprotoStr::AtIdentifier(unsafe { 85 + AtIdentifier::unchecked(value) 86 + })), 83 87 ); 84 88 } else { 85 89 map.insert( ··· 89 93 } 90 94 } 91 95 LexiconStringType::Nsid => { 92 - if let Ok(value) = Nsid::new(value.to_cowstr()) { 93 - map.insert(key.to_smolstr(), Data::String(AtprotoStr::Nsid(value))); 94 - } else { 96 + if validate_nsid(value.as_ref()).is_ok() { 95 97 map.insert( 96 98 key.to_smolstr(), 97 - Data::String(AtprotoStr::String(value.into())), 99 + Data::String(AtprotoStr::Nsid(unsafe { Nsid::unchecked(value) })), 98 100 ); 101 + } else { 102 + map.insert(key.to_smolstr(), Data::String(AtprotoStr::String(value))); 99 103 } 100 104 } 101 105 LexiconStringType::Cid => { 102 - if let Ok(value) = Cid::<CowStr<'s>>::new_owned(value.as_bytes()) { 103 - map.insert(key.to_smolstr(), Data::String(AtprotoStr::Cid(value))); 106 + let s: &str = value.as_ref(); 107 + // CID: try to parse as IPLD first, otherwise wrap as string CID. 108 + if IpldCid::try_from(s).is_ok() || s.starts_with("bafy") { 109 + map.insert( 110 + key.to_smolstr(), 111 + Data::String(AtprotoStr::Cid(unsafe { Cid::unchecked_str(value) })), 112 + ); 104 113 } else { 105 114 map.insert( 106 115 key.to_smolstr(), ··· 109 118 } 110 119 } 111 120 LexiconStringType::Language => { 112 - if let Ok(value) = Language::new(value) { 121 + if let Ok(value) = Language::new(value.as_ref()) { 113 122 map.insert(key.to_smolstr(), Data::String(AtprotoStr::Language(value))); 114 123 } else { 115 - map.insert( 116 - key.to_smolstr(), 117 - Data::String(AtprotoStr::String(value.into())), 118 - ); 124 + map.insert(key.to_smolstr(), Data::String(AtprotoStr::String(value))); 119 125 } 120 126 } 121 127 LexiconStringType::Tid => { 122 - if let Ok(value) = Tid::new(value) { 128 + if let Ok(value) = Tid::new(value.as_ref()) { 123 129 map.insert(key.to_smolstr(), Data::String(AtprotoStr::Tid(value))); 124 130 } else { 125 - map.insert( 126 - key.to_smolstr(), 127 - Data::String(AtprotoStr::String(value.into())), 128 - ); 131 + map.insert(key.to_smolstr(), Data::String(AtprotoStr::String(value))); 129 132 } 130 133 } 131 134 LexiconStringType::RecordKey => { 132 135 // Validate the rkey without shadowing the original `value: &'s str`. 133 - if Rkey::new(value).is_ok() { 136 + if Rkey::new(value.as_ref()).is_ok() { 134 137 map.insert( 135 138 key.to_smolstr(), 136 139 // Rkey already validated above; borrow the original &'s str directly. 137 140 Data::String(AtprotoStr::RecordKey( 138 - RecordKey::any(CowStr::Borrowed(value)).expect("Rkey validation passed"), 141 + RecordKey::any(value).expect("Rkey validation passed"), 139 142 )), 140 143 ); 141 144 } else { ··· 147 150 } 148 151 LexiconStringType::Uri(_) => { 149 152 // AtprotoStr::Uri stores UriValue<'static>, so we must produce an owned value. 150 - if let Ok(uri) = UriValue::new_owned(value) { 151 - map.insert(key.to_smolstr(), Data::String(AtprotoStr::Uri(uri))); 153 + if let Ok(uri) = UriValue::new(value.as_ref()) { 154 + match uri { 155 + UriValue::Any(_) => { 156 + map.insert( 157 + key.to_smolstr(), 158 + Data::String(AtprotoStr::String(value.into())), 159 + ); 160 + } 161 + _ => { 162 + map.insert( 163 + key.to_smolstr(), 164 + Data::String(AtprotoStr::Uri( 165 + UriValue::new(value).expect("already verified"), 166 + )), 167 + ); 168 + } 169 + } 152 170 } else { 153 171 map.insert( 154 172 key.to_smolstr(), ··· 164 182 } 165 183 166 184 /// smarter parsing to avoid trying as many posibilities. 167 - pub fn parse_string<'s>(string: &'s str) -> AtprotoStr<CowStr<'s>> { 168 - if string.len() < 2048 && string.starts_with("did:") { 169 - if let Ok(did) = Did::new(string.to_cowstr()) { 170 - return AtprotoStr::Did(did); 185 + pub fn parse_string<S>(string: S) -> AtprotoStr<S> 186 + where 187 + S: Bos<str> + AsRef<str>, 188 + { 189 + let s = string.as_ref(); 190 + if s.len() < 2048 && s.starts_with("did:") { 191 + if validate_did(s).is_ok() { 192 + return AtprotoStr::Did(unsafe { Did::unchecked(string) }); 171 193 } 172 - } else if string.starts_with("20") && string.ends_with("Z") { 194 + } else if s.starts_with("20") && s.ends_with("Z") { 173 195 // probably a date (for the next 75 years) 174 - if let Ok(datetime) = Datetime::from_str(string) { 196 + if let Ok(datetime) = Datetime::from_str(s) { 175 197 return AtprotoStr::Datetime(datetime); 176 198 } 177 - } else if string.starts_with("at://") { 178 - // AtprotoStr::AtUri stores AtUri<'static>; convert to owned. 179 - if let Ok(uri) = AtUri::new(string.to_cowstr()) { 180 - return AtprotoStr::AtUri(uri); 199 + } else if s.starts_with("at://") { 200 + if crate::types::aturi::validate_and_index(s).is_ok() { 201 + return AtprotoStr::AtUri(unsafe { AtUri::unchecked(string) }); 181 202 } 182 - } else if string.starts_with("https://") { 183 - if let Ok(uri) = Uri::parse(string) { 203 + } else if s.starts_with("https://") { 204 + if let Ok(uri) = Uri::parse(s) { 184 205 return AtprotoStr::Uri(UriValue::Https(uri.to_owned())); 185 206 } 186 - } else if string.starts_with("wss://") { 187 - if let Ok(uri) = Uri::parse(string) { 207 + } else if s.starts_with("wss://") { 208 + if let Ok(uri) = Uri::parse(s) { 188 209 return AtprotoStr::Uri(UriValue::Wss(uri.to_owned())); 189 210 } 190 - } else if string.starts_with("ipfs://") { 191 - // URI variant must be 'static; convert to an owned CID. 192 - return AtprotoStr::Uri(UriValue::Cid( 193 - Cid::<CowStr<'static>>::new_owned(string.as_bytes()) 194 - .unwrap_or_else(|_| Cid::cow_str(CowStr::Owned(string.to_smolstr()))), 195 - )); 196 - } else if string.contains('.') && !string.contains([' ', '\n']) { 211 + } else if s.starts_with("ipfs://") { 212 + let s: &str = string.as_ref(); 213 + // CID: try to parse as IPLD first, otherwise wrap as string CID. 214 + if IpldCid::try_from(s).is_ok() || s.starts_with("bafy") { 215 + return AtprotoStr::Uri(UriValue::Cid(unsafe { Cid::unchecked_str(string) })); 216 + } 217 + } else if s.contains('.') && !s.contains([' ', '\n']) { 197 218 // Dotted strings without a scheme could be handles, NSIDs, or URIs. 198 219 // Use TLD lookup and camelCase heuristic to disambiguate. 199 220 // - Handles: domain order with TLD at the end (e.g., "example.com") 200 221 // - NSIDs: reverse domain order with TLD at the start (e.g., "com.example.service") 201 222 // - Tiebreaker: camelCase in the last segment indicates NSID (e.g., "getRecord") 202 - let first_segment = string.split('.').next().unwrap_or(""); 203 - let last_segment = string.rsplit('.').next().unwrap_or(""); 223 + let first_segment = s.split('.').next().unwrap_or(""); 224 + let last_segment = s.rsplit('.').next().unwrap_or(""); 204 225 205 226 let first_is_tld = crate::tld::is_tld(first_segment); 206 227 let last_is_tld = crate::tld::is_tld(last_segment); ··· 208 229 209 230 // First segment is a known TLD → reverse domain order → try NSID first. 210 231 if first_is_tld { 211 - if let Ok(nsid) = Nsid::new(string.to_cowstr()) { 212 - return AtprotoStr::Nsid(nsid); 232 + if validate_nsid(s).is_ok() { 233 + return AtprotoStr::Nsid(unsafe { Nsid::unchecked(string) }); 213 234 } 214 235 } 215 236 216 237 // Last segment is a known TLD and first is not → normal domain order → handle. 217 238 if last_is_tld && !first_is_tld { 218 - if let Ok(handle) = AtIdentifier::new(string.to_cowstr()) { 219 - return AtprotoStr::AtIdentifier(handle); 239 + if validate_handle(s.as_ref()).is_ok() || validate_did(s.as_ref()).is_ok() { 240 + return AtprotoStr::AtIdentifier(unsafe { AtIdentifier::unchecked(string) }); 220 241 } 221 242 } 222 243 223 244 // camelCase in last segment → NSID (e.g., "com.atproto.repo.getRecord"). 224 245 if has_upper_last_segment { 225 - if let Ok(nsid) = Nsid::new(string.to_cowstr()) { 226 - return AtprotoStr::Nsid(nsid); 246 + if validate_nsid(s).is_ok() { 247 + return AtprotoStr::Nsid(unsafe { Nsid::unchecked(string) }); 227 248 } 228 249 } 229 250 230 251 // Fallback: try both, preferring handle. 231 - if let Ok(handle) = AtIdentifier::new(string.to_cowstr()) { 232 - return AtprotoStr::AtIdentifier(handle); 233 - } else if let Ok(nsid) = Nsid::new(string.to_cowstr()) { 234 - return AtprotoStr::Nsid(nsid); 235 - } else if string.contains("://") && Uri::<&str>::parse(string).is_ok() { 252 + if validate_handle(s.as_ref()).is_ok() { 253 + return AtprotoStr::AtIdentifier(AtIdentifier::Handle(unsafe { 254 + Handle::unchecked(string) 255 + })); 256 + } else if validate_nsid(s).is_ok() { 257 + return AtprotoStr::Nsid(unsafe { Nsid::unchecked(string) }); 258 + } else if s.contains("://") && Uri::<&str>::parse(s).is_ok() { 236 259 // AtprotoStr::Uri stores UriValue<'static>; convert to owned. 237 - return AtprotoStr::Uri(UriValue::Any(CowStr::Owned(string.to_smolstr()))); 260 + return AtprotoStr::Uri(UriValue::Any(string)); 238 261 } 239 - } else if string.len() == 13 { 240 - if let Ok(tid) = Tid::new(string) { 262 + } else if s.len() == 13 { 263 + drop(s); 264 + if let Ok(tid) = Tid::new(string.as_ref()) { 241 265 return AtprotoStr::Tid(tid); 242 266 } 243 - } else if !string.contains([' ', '\n']) && string.len() > 20 { 244 - // CID: must be longer than typical short strings to avoid false positives 245 - // Most CIDs are 46+ chars (base32 encoded), minimum realistic is around 30 246 - if let Ok(cid) = Cid::<CowStr<'s>>::new_owned(string.as_bytes()) { 247 - return AtprotoStr::Cid(cid); 267 + } else if !s.contains([' ', '\n']) && s.len() > 20 { 268 + // CID: try to parse as IPLD first, otherwise wrap as string CID. 269 + if IpldCid::try_from(s).is_ok() || s.starts_with("bafy") { 270 + return AtprotoStr::Cid(unsafe { Cid::unchecked_str(string) }); 248 271 } 249 272 } 273 + drop(s); 250 274 251 - AtprotoStr::String(string.into()) 275 + AtprotoStr::String(string) 252 276 } 253 277 254 278 /// First-level guess at what we should parse the corresponding value as ··· 301 325 if let (Some(mime_type), Some(size)) = (mime_type, size) { 302 326 return Some(Blob { 303 327 r#ref: CidLink::<CowStr<'b>>::ipld(*value), 304 - mime_type: MimeType::new_cow(CowStr::Borrowed(mime_type)), 328 + mime_type: MimeType::new(CowStr::Borrowed(mime_type)), 305 329 size: size as usize, 306 330 }); 307 331 } ··· 309 333 if let Some(mime_type) = mime_type { 310 334 return Some(Blob { 311 335 r#ref: CidLink::cow_str(CowStr::Borrowed(value.as_str())), 312 - mime_type: MimeType::new_cow(CowStr::Borrowed(mime_type)), 336 + mime_type: MimeType::new(CowStr::Borrowed(mime_type)), 313 337 size: 0, 314 338 }); 315 339 } ··· 333 357 if let (Some(mime_type), Some(size)) = (mime_type, size) { 334 358 return Some(Blob { 335 359 r#ref: CidLink::cow_str(CowStr::Borrowed(value)), 336 - mime_type: MimeType::new_cow(CowStr::Borrowed(mime_type)), 360 + mime_type: MimeType::new(CowStr::Borrowed(mime_type)), 337 361 size: size as usize, 338 362 }); 339 363 } ··· 342 366 if let Some(mime_type) = mime_type { 343 367 return Some(Blob { 344 368 r#ref: CidLink::cow_str(CowStr::Borrowed(value)), 345 - mime_type: MimeType::new_cow(CowStr::Borrowed(mime_type)), 369 + mime_type: MimeType::new(CowStr::Borrowed(mime_type)), 346 370 size: 0, 347 371 }); 348 372 } ··· 360 384 } 361 385 362 386 /// decode a base64 byte string into atproto data 363 - pub fn decode_bytes<'s>(bytes: &str) -> Data<'s> { 387 + pub fn decode_bytes<S>(bytes: S) -> Data<S> 388 + where 389 + S: Bos<str> + AsRef<str>, 390 + { 364 391 // First one should just work. rest are insurance. 365 - if let Ok(bytes) = BASE64_STANDARD.decode(bytes) { 392 + if let Ok(bytes) = BASE64_STANDARD.decode(bytes.as_ref().as_bytes()) { 366 393 Data::Bytes(Bytes::from_owner(bytes)) 367 - } else if let Ok(bytes) = BASE64_STANDARD_NO_PAD.decode(bytes) { 394 + } else if let Ok(bytes) = BASE64_STANDARD_NO_PAD.decode(bytes.as_ref().as_bytes()) { 368 395 Data::Bytes(Bytes::from_owner(bytes)) 369 - } else if let Ok(bytes) = BASE64_URL_SAFE.decode(bytes) { 396 + } else if let Ok(bytes) = BASE64_URL_SAFE.decode(bytes.as_ref().as_bytes()) { 370 397 Data::Bytes(Bytes::from_owner(bytes)) 371 - } else if let Ok(bytes) = BASE64_URL_SAFE_NO_PAD.decode(bytes) { 398 + } else if let Ok(bytes) = BASE64_URL_SAFE_NO_PAD.decode(bytes.as_ref().as_bytes()) { 372 399 Data::Bytes(Bytes::from_owner(bytes)) 373 400 } else { 374 - Data::String(AtprotoStr::String(CowStr::Borrowed(bytes).into_static())) 401 + Data::String(AtprotoStr::String(S::from(bytes))) 375 402 } 376 403 } 377 404
+281 -137
crates/jacquard-common/src/types/value/serde_impl.rs
··· 1 + use crate::Bos; 1 2 use crate::types::cid::IpldCid; 2 - use alloc::borrow::ToOwned; 3 3 use alloc::boxed::Box; 4 4 use alloc::collections::BTreeMap; 5 5 use alloc::string::String; ··· 8 8 use base64::{Engine, prelude::BASE64_STANDARD}; 9 9 use bytes::Bytes; 10 10 use core::fmt; 11 + use core::marker::PhantomData; 11 12 use core::str::FromStr; 13 + use serde::de::value::StrDeserializer; 12 14 use serde::{Deserialize, Deserializer, Serialize, Serializer, de::VariantAccess}; 13 15 use smol_str::{SmolStr, ToSmolStr}; 14 16 ··· 28 30 }, 29 31 }; 30 32 31 - impl Serialize for Data<'_> { 33 + impl<D> Serialize for Data<D> 34 + where 35 + D: Bos<str> + AsRef<str> + Serialize, 36 + { 32 37 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 33 38 where 34 39 S: Serializer, ··· 65 70 Data::Array(arr) => arr.serialize(serializer), 66 71 Data::Object(obj) => obj.serialize(serializer), 67 72 Data::Blob(blob) => blob.serialize(serializer), 73 + Data::InvalidNumber(float) => { 74 + if let Ok(f) = float.as_ref().parse::<f32>() { 75 + f.serialize(serializer) 76 + } else { 77 + float.serialize(serializer) 78 + } 79 + } 68 80 } 69 81 } 70 82 } 71 83 72 - impl<'de, 'a> Deserialize<'de> for Data<'a> 84 + impl<'de, S> Deserialize<'de> for Data<S> 73 85 where 74 - 'de: 'a, 86 + S: Bos<str> + AsRef<str> + Deserialize<'de>, 75 87 { 76 88 /// Currently only works for self-describing formats 77 89 /// Thankfully the supported atproto data formats are both self-describing (json and dag-cbor). ··· 80 92 where 81 93 D: Deserializer<'de>, 82 94 { 83 - deserializer.deserialize_any(DataVisitor) 95 + deserializer.deserialize_any(DataVisitor(PhantomData)) 84 96 } 85 97 } 86 98 87 - struct DataVisitor; 99 + struct DataVisitor<S>(PhantomData<S>); 88 100 89 - impl<'de: 'v, 'v> serde::de::Visitor<'v> for DataVisitor { 90 - type Value = Data<'v>; 101 + impl<'de: 'v, 'v, S> serde::de::Visitor<'v> for DataVisitor<S> 102 + where 103 + S: Bos<str> + AsRef<str> + Deserialize<'v>, 104 + { 105 + type Value = Data<S>; 91 106 92 107 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 93 108 formatter.write_str("any valid AT Protocol data value") ··· 135 150 Ok(Data::Integer((v % (i64::MAX as u64)) as i64)) 136 151 } 137 152 138 - fn visit_f64<E>(self, _v: f64) -> Result<Self::Value, E> 153 + fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E> 139 154 where 140 155 E: serde::de::Error, 141 156 { 142 - Ok(Data::String(AtprotoStr::String( 143 - CowStr::Owned(_v.to_smolstr()).into_static(), 144 - ))) 157 + let v = v.to_smolstr(); 158 + let s = StrDeserializer::new(v.as_str()); 159 + Ok(Data::InvalidNumber(S::deserialize(s)?)) 145 160 // Err(E::custom( 146 161 // "floating point numbers not allowed in AT protocol data", 147 162 // )) ··· 151 166 where 152 167 E: serde::de::Error, 153 168 { 154 - Ok(Data::String(AtprotoStr::String( 155 - CowStr::Borrowed(v).into_static(), 156 - ))) 169 + let s = StrDeserializer::new(v); 170 + Ok(Data::String(AtprotoStr::String(S::deserialize(s)?))) 157 171 } 158 172 159 173 fn visit_borrowed_str<E>(self, v: &'v str) -> Result<Self::Value, E> 160 174 where 161 175 E: serde::de::Error, 162 176 { 163 - // Don't infer type here - just store as plain string 164 - // Type inference happens in apply_type_inference based on field names 165 - Ok(Data::String(AtprotoStr::String(v.into()))) 177 + let s = StrDeserializer::new(v); 178 + Ok(Data::String(AtprotoStr::String(S::deserialize(s)?))) 166 179 } 167 180 168 181 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> 169 182 where 170 183 E: serde::de::Error, 171 184 { 172 - Ok(Data::String(AtprotoStr::String(v.into()))) 185 + let s = StrDeserializer::new(&v); 186 + Ok(Data::String(AtprotoStr::String(S::deserialize(s)?))) 173 187 } 174 188 175 189 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> ··· 200 214 match data.variant::<SmolStr>() { 201 215 Ok((key, value)) => { 202 216 let mut map = BTreeMap::new(); 203 - if let Ok(variant) = value.newtype_variant::<Data>() { 217 + if let Ok(variant) = value.newtype_variant::<Data<S>>() { 204 218 map.insert(key, variant); 205 219 } 206 220 Ok(Data::Object(Object(map))) ··· 224 238 where 225 239 D: Deserializer<'v>, 226 240 { 227 - deserializer.deserialize_any(CidAwareVisitor) 241 + deserializer.deserialize_any(CidAwareVisitor(PhantomData)) 228 242 } 229 243 230 244 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> ··· 234 248 use serde::de::Error; 235 249 236 250 // Peek at first key to check for special single-key patterns 237 - let mut temp_map: BTreeMap<SmolStr, Data<'v>> = BTreeMap::new(); 251 + let mut temp_map: BTreeMap<SmolStr, Data<S>> = BTreeMap::new(); 238 252 239 253 while let Some(key) = map.next_key::<SmolStr>()? { 240 254 // Check for special patterns on single-key maps 241 255 if temp_map.is_empty() { 242 256 if key.as_str() == "$link" { 243 257 // {"$link": "cid_string"} pattern 244 - let cid_str: String = map.next_value()?; 258 + let cid_str: S = map.next_value()?; 245 259 // Check if there are more keys 246 260 if let Some(next_key) = map.next_key::<SmolStr>()? { 247 261 // More keys, treat as regular object 248 262 temp_map.insert(key, Data::String(AtprotoStr::String(cid_str.into()))); 249 - let next_value: Data = map.next_value()?; 263 + let next_value: Data<S> = map.next_value()?; 250 264 temp_map.insert(next_key, next_value); 251 265 continue; 252 266 } else { 253 267 // Only key, return CidLink 254 - return Ok(Data::CidLink(Cid::cow_str(CowStr::from(cid_str)))); 268 + return Ok(Data::CidLink(unsafe { 269 + Cid::unchecked_str(S::from(cid_str)) 270 + })); 255 271 } 256 272 } else if key.as_str() == "$bytes" { 257 273 // {"$bytes": "base64_string"} pattern 258 - let bytes_str: String = map.next_value()?; 274 + let bytes_str: S = map.next_value()?; 259 275 // Check if there are more keys 260 276 if map.next_key::<SmolStr>()?.is_some() { 261 277 // More keys, treat as regular object - shouldn't happen but handle it ··· 263 279 continue; 264 280 } else { 265 281 // Only key, decode and return bytes 266 - return Ok(decode_bytes(&bytes_str)); 282 + return Ok(decode_bytes(bytes_str)); 267 283 } 268 284 } 269 285 } 270 286 271 - let value: Data = map.next_value()?; 287 + let value: Data<S> = map.next_value()?; 272 288 temp_map.insert(key, value); 273 289 } 274 290 ··· 277 293 } 278 294 } 279 295 280 - fn apply_type_inference<'s>(mut map: BTreeMap<SmolStr, Data<'s>>) -> Result<Data<'s>, AtDataError> { 296 + fn apply_type_inference<'s, S>(mut map: BTreeMap<SmolStr, Data<S>>) -> Result<Data<S>, AtDataError> 297 + where 298 + S: AsRef<str> + Bos<str>, 299 + { 281 300 // Check for CID link pattern first: {"$link": "cid_string"} 282 301 if map.len() == 1 { 283 - if let Some(Data::String(AtprotoStr::String(link))) = map.get("$link") { 284 - // Need to extract ownership, can't borrow from map we're about to consume 285 - let link_owned = link.clone(); 286 - return Ok(Data::CidLink(Cid::cow_str(link_owned))); 302 + if let Some(Data::String(AtprotoStr::String(link))) = map.remove("$link") { 303 + return Ok(Data::CidLink(unsafe { Cid::unchecked_str(link) })); 287 304 } 288 305 } 289 306 290 307 // Check for $type field to detect special structures 291 - let type_field = map.get("$type").and_then(|v| { 308 + let type_field = map.remove("$type").and_then(|v| { 292 309 if let Data::String(AtprotoStr::String(s)) = v { 293 - Some(s.as_ref()) 310 + Some(s) 294 311 } else { 295 312 None 296 313 } ··· 298 315 299 316 // Check for blob 300 317 if let Some(type_str) = type_field { 301 - if infer_from_type(type_str) == DataModelType::Blob { 318 + if infer_from_type(type_str.as_ref()) == DataModelType::Blob { 302 319 // Try to construct blob from the collected data 303 - let ref_cid = map.get("ref").and_then(|v| { 320 + let ref_cid = map.remove("ref").and_then(|v| { 304 321 if let Data::CidLink(cid) = v { 305 - Some(cid.clone()) 322 + Some(cid) 306 323 } else { 307 324 None 308 325 } 309 326 }); 310 327 311 - let mime_type = map.get("mimeType").and_then(|v| { 328 + let mime_type = map.remove("mimeType").and_then(|v| { 312 329 if let Data::String(AtprotoStr::String(s)) = v { 313 - Some(s.clone()) 330 + Some(s) 314 331 } else { 315 332 None 316 333 } 317 334 }); 318 335 319 - let size = map.get("size").and_then(|v| { 336 + let size = map.remove("size").and_then(|v| { 320 337 if let Data::Integer(i) = v { 321 - Some(*i as usize) 338 + Some(i as usize) 322 339 } else { 323 340 None 324 341 } ··· 328 345 return Ok(Data::Blob(Blob { 329 346 // ref_cid is already Cid<CowStr<'s>>; wrap directly. 330 347 r#ref: CidLink(ref_cid), 331 - mime_type: MimeType::from(mime_cowstr), 348 + mime_type: MimeType::new(mime_cowstr), 332 349 size, 333 350 })); 334 351 } 335 352 } 336 353 } 337 354 338 - // Apply type inference for string fields based on key names (mutate in place) 339 - for (key, value) in map.iter_mut() { 340 - if let Data::String(AtprotoStr::String(s)) = value.to_owned() { 341 - let type_hint = string_key_type_guess(key.as_str()); 342 - let refined = match type_hint { 343 - DataModelType::String(string_type) => refine_string_by_type(s, string_type), 344 - DataModelType::Bytes => { 345 - // Decode base64 346 - decode_bytes(&s) 355 + // Apply type inference for string fields based on key names. 356 + // Drain and rebuild to avoid cloning S values. 357 + let map = map 358 + .into_iter() 359 + .map(|(key, value)| { 360 + let refined = if let Data::String(AtprotoStr::String(s)) = value { 361 + let type_hint = string_key_type_guess(key.as_str()); 362 + match type_hint { 363 + DataModelType::String(string_type) => refine_string_by_type(s, string_type), 364 + DataModelType::Bytes => decode_bytes(s), 365 + DataModelType::CidLink if key.as_str() == "$link" => { 366 + Data::CidLink(unsafe { Cid::unchecked_str(s) }) 367 + } 368 + _ => Data::String(AtprotoStr::String(s)), 347 369 } 348 - DataModelType::CidLink if key.as_str() == "$link" => Data::CidLink(Cid::cow_str(s)), 349 - _ => continue, // no refinement needed 370 + } else { 371 + value 350 372 }; 351 - *value = refined; 352 - } 353 - } 373 + (key, refined) 374 + }) 375 + .collect(); 354 376 355 377 Ok(Data::Object(Object(map))) 356 378 } 357 379 358 - fn refine_string_by_type<'s>(s: CowStr<'s>, string_type: LexiconStringType) -> Data<'s> { 380 + fn refine_string_by_type<S>(s: S, string_type: LexiconStringType) -> Data<S> 381 + where 382 + S: Bos<str> + AsRef<str>, 383 + { 384 + use crate::types::aturi::validate_and_index; 385 + use crate::types::cid::IpldCid; 386 + use crate::types::did::validate_did; 387 + use crate::types::handle::validate_handle; 388 + use crate::types::nsid::validate_nsid; 389 + use crate::types::recordkey::validate_rkey; 390 + 359 391 match string_type { 360 - LexiconStringType::Datetime => Datetime::from_str(&s) 361 - .map(|dt| Data::String(AtprotoStr::Datetime(dt))) 362 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 363 - LexiconStringType::AtUri => AtUri::new_owned(s.clone()) 364 - .map(|uri| Data::String(AtprotoStr::AtUri(uri))) 365 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 366 - LexiconStringType::Did => Did::new_owned(s.clone()) 367 - .map(|did| Data::String(AtprotoStr::Did(did))) 368 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 369 - LexiconStringType::Handle => Handle::new_owned(s.clone()) 370 - .map(|handle| Data::String(AtprotoStr::Handle(handle))) 371 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 372 - LexiconStringType::AtIdentifier => AtIdentifier::new_owned(s.clone()) 373 - .map(|ident| Data::String(AtprotoStr::AtIdentifier(ident))) 374 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 375 - LexiconStringType::Nsid => Nsid::new_owned(s.clone()) 376 - .map(|nsid| Data::String(AtprotoStr::Nsid(nsid))) 377 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 378 - LexiconStringType::Cid => Cid::<CowStr<'s>>::new_owned(s.as_bytes()) 379 - .map(|cid| Data::String(AtprotoStr::Cid(cid))) 380 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.into()))), 381 - LexiconStringType::Language => Language::new(&s) 382 - .map(|lang| Data::String(AtprotoStr::Language(lang))) 383 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 384 - LexiconStringType::Tid => Tid::new(s.clone()) 385 - .map(|tid| Data::String(AtprotoStr::Tid(tid))) 386 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 387 - LexiconStringType::RecordKey => Rkey::new(s.clone()) 388 - .map(|rkey| Data::String(AtprotoStr::RecordKey(RecordKey(rkey)))) 389 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 390 - LexiconStringType::Uri(_) => UriValue::new_owned(s.clone()) 391 - .map(|uri| Data::String(AtprotoStr::Uri(uri))) 392 - .unwrap_or_else(|_| Data::String(AtprotoStr::String(s.clone()))), 393 - LexiconStringType::String => Data::String(parse_string(&s).into_static()), 392 + LexiconStringType::Datetime => { 393 + if let Ok(dt) = Datetime::from_str(s.as_ref()) { 394 + return Data::String(AtprotoStr::Datetime(dt)); 395 + } 396 + } 397 + LexiconStringType::AtUri => { 398 + if validate_and_index(s.as_ref()).is_ok() { 399 + return Data::String(AtprotoStr::AtUri(unsafe { AtUri::unchecked(s) })); 400 + } 401 + } 402 + LexiconStringType::Did => { 403 + if validate_did(s.as_ref()).is_ok() { 404 + return Data::String(AtprotoStr::Did(unsafe { Did::unchecked(s) })); 405 + } 406 + } 407 + LexiconStringType::Handle => { 408 + if validate_handle(s.as_ref()).is_ok() 409 + && !s.as_ref().contains(|c: char| c.is_ascii_uppercase()) 410 + { 411 + return Data::String(AtprotoStr::Handle(unsafe { Handle::unchecked(s) })); 412 + } 413 + } 414 + LexiconStringType::AtIdentifier => { 415 + if validate_did(s.as_ref()).is_ok() 416 + || (validate_handle(s.as_ref()).is_ok() 417 + && !s.as_ref().contains(|c: char| c.is_ascii_uppercase())) 418 + { 419 + return Data::String(AtprotoStr::AtIdentifier(unsafe { 420 + AtIdentifier::unchecked(s) 421 + })); 422 + } 423 + } 424 + LexiconStringType::Nsid => { 425 + if validate_nsid(s.as_ref()).is_ok() { 426 + return Data::String(AtprotoStr::Nsid(unsafe { Nsid::unchecked(s) })); 427 + } 428 + } 429 + LexiconStringType::Cid => { 430 + if IpldCid::try_from(s.as_ref().as_bytes()).is_ok() || s.as_ref().starts_with("bafy") { 431 + return Data::String(AtprotoStr::Cid(unsafe { Cid::unchecked_str(s) })); 432 + } 433 + } 434 + LexiconStringType::Language => { 435 + if let Ok(lang) = Language::new(s.as_ref()) { 436 + return Data::String(AtprotoStr::Language(lang)); 437 + } 438 + } 439 + LexiconStringType::Tid => { 440 + if let Ok(tid) = Tid::new(s.as_ref()) { 441 + return Data::String(AtprotoStr::Tid(tid)); 442 + } 443 + } 444 + LexiconStringType::RecordKey => { 445 + if validate_rkey(s.as_ref()).is_ok() { 446 + return Data::String(AtprotoStr::RecordKey(RecordKey(unsafe { 447 + Rkey::unchecked(s) 448 + }))); 449 + } 450 + } 451 + LexiconStringType::Uri(_) => { 452 + // UriValue::new is infallible but may fall through to Any. 453 + // Prefer AtprotoStr::String over wrapping as Uri(Any). 454 + match UriValue::new(s) { 455 + Ok(UriValue::Any(s)) => return Data::String(AtprotoStr::String(s)), 456 + Ok(uri) => return Data::String(AtprotoStr::Uri(uri)), 457 + Err(_) => unreachable!(), 458 + } 459 + } 460 + LexiconStringType::String => { 461 + return Data::String(parse_string(s)); 462 + } 394 463 } 464 + // Fallback for failed validation. 465 + Data::String(AtprotoStr::String(s)) 395 466 } 396 467 397 - impl Serialize for Array<'_> { 468 + impl<D> Serialize for Array<D> 469 + where 470 + D: Bos<str> + AsRef<str> + Serialize, 471 + { 398 472 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 399 473 where 400 474 S: Serializer, ··· 408 482 } 409 483 } 410 484 411 - impl<'de, 'a> Deserialize<'de> for Array<'a> 485 + impl<'de, 'a, S> Deserialize<'de> for Array<S> 412 486 where 413 487 'de: 'a, 488 + S: AsRef<str> + Bos<str> + Deserialize<'de>, 414 489 { 415 490 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 416 491 where 417 492 D: Deserializer<'de>, 418 493 { 419 494 // Just deserialize as Vec<Data> directly - the Data visitor handles everything 420 - let vec: Vec<Data<'a>> = Deserialize::deserialize(deserializer)?; 495 + let vec: Vec<Data<S>> = Deserialize::deserialize(deserializer)?; 421 496 Ok(Array(vec)) 422 497 } 423 498 } 424 499 425 - impl Serialize for Object<'_> { 500 + impl<D> Serialize for Object<D> 501 + where 502 + D: AsRef<str> + Bos<str> + Serialize, 503 + { 426 504 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 427 505 where 428 506 S: Serializer, ··· 436 514 } 437 515 } 438 516 439 - impl<'de, 'a> Deserialize<'de> for Object<'a> 517 + impl<'de, 'a, S> Deserialize<'de> for Object<S> 440 518 where 441 519 'de: 'a, 520 + S: AsRef<str> + Bos<str> + Deserialize<'de>, 442 521 { 443 522 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 444 523 where ··· 448 527 449 528 // Deserialize via Data, then extract the Object 450 529 // The Data visitor handles all the type inference and special cases 451 - let data: Data<'a> = Data::deserialize(deserializer)?; 530 + let data: Data<S> = Data::deserialize(deserializer)?; 452 531 match data { 453 532 Data::Object(obj) => Ok(obj), 454 533 _ => Err(D::Error::custom("expected object, got something else")), ··· 703 782 704 783 // In DAG-CBOR, newtype_struct wraps tag 42 (CID) 705 784 // The CidDeserializer will call visit_bytes with the CID bytes 706 - struct CidAwareVisitor; 785 + struct CidAwareVisitor<S>(PhantomData<S>); 707 786 708 - impl<'de: 'v, 'v> serde::de::Visitor<'v> for CidAwareVisitor { 709 - type Value = Data<'v>; 787 + impl<'de: 'v, 'v, S> serde::de::Visitor<'v> for CidAwareVisitor<S> 788 + where 789 + S: AsRef<str> + Bos<str>, 790 + { 791 + type Value = Data<S>; 710 792 711 793 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { 712 794 f.write_str("CID bytes or other newtype content") ··· 827 909 } 828 910 829 911 // Deserializer implementation for &Data<'de> - allows deserializing typed data from Data values 830 - impl<'de> serde::Deserializer<'de> for &'de Data<'de> { 912 + impl<'de, S> serde::Deserializer<'de> for &'de Data<S> 913 + where 914 + S: AsRef<str> + Bos<str>, 915 + { 831 916 type Error = DataDeserializerError; 832 917 833 918 fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> ··· 852 937 Data::Object(obj) => visitor.visit_map(ObjectDeserializer::new(&obj.0)), 853 938 Data::Blob(blob) => { 854 939 // Blob is a root type - deserialize as the Blob itself via map representation 855 - visitor.visit_map(BlobDeserializer::new(blob)) 940 + visitor.visit_map(BlobDeserializer::new(&blob)) 856 941 } 942 + Data::InvalidNumber(float) => visitor.visit_borrowed_str(float.as_ref()), 857 943 } 858 944 } 859 945 ··· 875 961 } 876 962 877 963 // Deserializer implementation for &Data<'de> - allows deserializing typed data from Data values 878 - impl<'de> serde::Deserializer<'de> for Data<'static> { 964 + impl<'de, S> serde::Deserializer<'de> for Data<S> 965 + where 966 + S: AsRef<str> + Bos<str>, 967 + { 879 968 type Error = DataDeserializerError; 880 969 881 970 fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> ··· 895 984 // Blob is a root type - deserialize as the Blob itself via map representation 896 985 visitor.visit_map(OwnedBlobDeserializer::new(blob)) 897 986 } 987 + Data::InvalidNumber(float) => visitor.visit_str(float.as_ref()), 898 988 } 899 989 } 900 990 ··· 1035 1125 } 1036 1126 1037 1127 // MapAccess implementation for Blob - allows borrowing from blob fields 1038 - struct BlobDeserializer<'de> { 1039 - blob: &'de Blob<CowStr<'de>>, 1128 + struct BlobDeserializer<'de, S> 1129 + where 1130 + S: AsRef<str> + Bos<str>, 1131 + { 1132 + blob: &'de Blob<S>, 1040 1133 field_index: usize, 1041 1134 } 1042 1135 1043 - impl<'de> BlobDeserializer<'de> { 1044 - fn new(blob: &'de Blob<CowStr<'de>>) -> Self { 1136 + impl<'de, S> BlobDeserializer<'de, S> 1137 + where 1138 + S: AsRef<str> + Bos<str>, 1139 + { 1140 + fn new(blob: &'de Blob<S>) -> Self { 1045 1141 Self { 1046 1142 blob, 1047 1143 field_index: 0, ··· 1049 1145 } 1050 1146 } 1051 1147 1052 - impl<'de> serde::de::MapAccess<'de> for BlobDeserializer<'de> { 1148 + impl<'de, S> serde::de::MapAccess<'de> for BlobDeserializer<'de, S> 1149 + where 1150 + S: AsRef<str> + Bos<str>, 1151 + { 1053 1152 type Error = DataDeserializerError; 1054 1153 1055 1154 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error> ··· 1083 1182 } 1084 1183 } 1085 1184 1086 - struct OwnedBlobDeserializer { 1087 - blob: Blob<CowStr<'static>>, 1185 + struct OwnedBlobDeserializer<S> 1186 + where 1187 + S: AsRef<str> + Bos<str>, 1188 + { 1189 + blob: Blob<S>, 1088 1190 field_index: usize, 1089 1191 } 1090 1192 1091 - impl OwnedBlobDeserializer { 1092 - fn new(blob: Blob<CowStr<'_>>) -> Self { 1193 + impl<S> OwnedBlobDeserializer<S> 1194 + where 1195 + S: AsRef<str> + Bos<str>, 1196 + { 1197 + fn new(blob: Blob<S>) -> Self { 1093 1198 Self { 1094 - blob: blob.into_static(), 1199 + blob: blob, 1095 1200 field_index: 0, 1096 1201 } 1097 1202 } 1098 1203 } 1099 1204 1100 - impl<'de> serde::de::MapAccess<'de> for OwnedBlobDeserializer { 1205 + impl<'de, S> serde::de::MapAccess<'de> for OwnedBlobDeserializer<S> 1206 + where 1207 + S: AsRef<str> + Bos<str>, 1208 + { 1101 1209 type Error = DataDeserializerError; 1102 1210 1103 1211 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error> ··· 1192 1300 } 1193 1301 1194 1302 // SeqAccess implementation for Data::Array 1195 - struct ArrayDeserializer<'de> { 1196 - iter: core::slice::Iter<'de, Data<'de>>, 1303 + struct ArrayDeserializer<'de, S> 1304 + where 1305 + S: Bos<str> + AsRef<str>, 1306 + { 1307 + iter: core::slice::Iter<'de, Data<S>>, 1197 1308 } 1198 1309 1199 - impl<'de> ArrayDeserializer<'de> { 1200 - fn new(slice: &'de [Data<'de>]) -> Self { 1310 + impl<'de, S> ArrayDeserializer<'de, S> 1311 + where 1312 + S: Bos<str> + AsRef<str>, 1313 + { 1314 + fn new(slice: &'de [Data<S>]) -> Self { 1201 1315 Self { iter: slice.iter() } 1202 1316 } 1203 1317 } 1204 1318 1205 - impl<'de> serde::de::SeqAccess<'de> for ArrayDeserializer<'de> { 1319 + impl<'de, S> serde::de::SeqAccess<'de> for ArrayDeserializer<'de, S> 1320 + where 1321 + S: Bos<str> + AsRef<str>, 1322 + { 1206 1323 type Error = DataDeserializerError; 1207 1324 1208 1325 fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error> ··· 1217 1334 } 1218 1335 1219 1336 // SeqAccess implementation for Data::Array 1220 - struct OwnedArrayDeserializer { 1221 - iter: alloc::vec::IntoIter<Data<'static>>, 1337 + struct OwnedArrayDeserializer<S> 1338 + where 1339 + S: Bos<str> + AsRef<str>, 1340 + { 1341 + iter: alloc::vec::IntoIter<Data<S>>, 1222 1342 } 1223 1343 1224 - impl OwnedArrayDeserializer { 1225 - fn new(slice: Vec<Data<'static>>) -> Self { 1344 + impl<S> OwnedArrayDeserializer<S> 1345 + where 1346 + S: Bos<str> + AsRef<str>, 1347 + { 1348 + fn new(slice: Vec<Data<S>>) -> Self { 1226 1349 Self { 1227 1350 iter: slice.into_iter(), 1228 1351 } 1229 1352 } 1230 1353 } 1231 1354 1232 - impl<'de> serde::de::SeqAccess<'de> for OwnedArrayDeserializer { 1355 + impl<'de, S> serde::de::SeqAccess<'de> for OwnedArrayDeserializer<S> 1356 + where 1357 + S: Bos<str> + AsRef<str>, 1358 + { 1233 1359 type Error = DataDeserializerError; 1234 1360 1235 1361 fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error> ··· 1244 1370 } 1245 1371 1246 1372 // MapAccess implementation for Data::Object 1247 - struct ObjectDeserializer<'de> { 1248 - iter: alloc::collections::btree_map::Iter<'de, SmolStr, Data<'de>>, 1249 - value: Option<&'de Data<'de>>, 1373 + struct ObjectDeserializer<'de, S> 1374 + where 1375 + S: Bos<str> + AsRef<str>, 1376 + { 1377 + iter: alloc::collections::btree_map::Iter<'de, SmolStr, Data<S>>, 1378 + value: Option<&'de Data<S>>, 1250 1379 } 1251 1380 1252 - impl<'de> ObjectDeserializer<'de> { 1253 - fn new(map: &'de BTreeMap<SmolStr, Data<'de>>) -> Self { 1381 + impl<'de, S> ObjectDeserializer<'de, S> 1382 + where 1383 + S: Bos<str> + AsRef<str>, 1384 + { 1385 + fn new(map: &'de BTreeMap<SmolStr, Data<S>>) -> Self { 1254 1386 Self { 1255 1387 iter: map.iter(), 1256 1388 value: None, ··· 1258 1390 } 1259 1391 } 1260 1392 1261 - impl<'de> serde::de::MapAccess<'de> for ObjectDeserializer<'de> { 1393 + impl<'de, S> serde::de::MapAccess<'de> for ObjectDeserializer<'de, S> 1394 + where 1395 + S: Bos<str> + AsRef<str>, 1396 + { 1262 1397 type Error = DataDeserializerError; 1263 1398 1264 1399 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error> ··· 1289 1424 } 1290 1425 1291 1426 // MapAccess implementation for Data::Object 1292 - struct OwnedObjectDeserializer { 1293 - iter: alloc::collections::btree_map::IntoIter<SmolStr, Data<'static>>, 1294 - value: Option<Data<'static>>, 1427 + struct OwnedObjectDeserializer<S> 1428 + where 1429 + S: Bos<str> + AsRef<str>, 1430 + { 1431 + iter: alloc::collections::btree_map::IntoIter<SmolStr, Data<S>>, 1432 + value: Option<Data<S>>, 1295 1433 } 1296 1434 1297 - impl OwnedObjectDeserializer { 1298 - fn new(map: BTreeMap<SmolStr, Data<'static>>) -> Self { 1435 + impl<S> OwnedObjectDeserializer<S> 1436 + where 1437 + S: Bos<str> + AsRef<str>, 1438 + { 1439 + fn new(map: BTreeMap<SmolStr, Data<S>>) -> Self { 1299 1440 Self { 1300 1441 iter: map.into_iter(), 1301 1442 value: None, ··· 1303 1444 } 1304 1445 } 1305 1446 1306 - impl<'de> serde::de::MapAccess<'de> for OwnedObjectDeserializer { 1447 + impl<'de, S> serde::de::MapAccess<'de> for OwnedObjectDeserializer<S> 1448 + where 1449 + S: Bos<str> + AsRef<str>, 1450 + { 1307 1451 type Error = DataDeserializerError; 1308 1452 1309 1453 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
+56 -55
crates/jacquard-common/src/types/value/tests.rs
··· 2 2 3 3 use super::*; 4 4 use core::str::FromStr; 5 + use std::string::String; 5 6 6 7 /// Canonicalize JSON by sorting object keys recursively 7 8 fn canonicalize_json(value: &serde_json::Value) -> serde_json::Value { ··· 118 119 assert!(matches!(items[1], Data::Boolean(true))); 119 120 assert!(matches!(items[2], Data::Integer(42))); 120 121 if let Data::String(AtprotoStr::String(s)) = &items[3] { 121 - assert_eq!(s.as_ref(), "test"); 122 + assert_eq!(s, "test"); 122 123 } else { 123 124 panic!("expected plain string"); 124 125 } ··· 333 334 let mut map = BTreeMap::new(); 334 335 map.insert( 335 336 SmolStr::new_static("name"), 336 - Data::String(AtprotoStr::String("Alice".into())), 337 + Data::String(AtprotoStr::String("Alice")), 337 338 ); 338 339 map.insert(SmolStr::new_static("age"), Data::Integer(30)); 339 340 let data = Data::Object(Object(map)); ··· 345 346 346 347 #[test] 347 348 fn test_from_data_vec() { 348 - let data = Data::Array(Array(vec![ 349 + let data: Data = Data::Array(Array(vec![ 349 350 Data::Integer(1), 350 351 Data::Integer(2), 351 352 Data::Integer(3), ··· 375 376 let mut nested_map = BTreeMap::new(); 376 377 nested_map.insert( 377 378 SmolStr::new_static("value"), 378 - Data::String(AtprotoStr::String("test".into())), 379 + Data::String(AtprotoStr::String("test")), 379 380 ); 380 381 381 382 let mut parent_map = BTreeMap::new(); ··· 427 428 let mut map = BTreeMap::new(); 428 429 map.insert( 429 430 SmolStr::new_static("required"), 430 - Data::String(AtprotoStr::String("value".into())), 431 + Data::String(AtprotoStr::String("value")), 431 432 ); 432 433 // optional field not present 433 434 let data = Data::Object(Object(map)); ··· 522 523 map.insert( 523 524 SmolStr::new_static("nsid"), 524 525 Data::String(AtprotoStr::Nsid( 525 - Nsid::new_static("app.bsky.feed.post").unwrap(), 526 + Nsid::<CowStr<'_>>::new_static("app.bsky.feed.post").unwrap(), 526 527 )), 527 528 ); 528 529 map.insert( ··· 626 627 map.insert( 627 628 SmolStr::new_static("ident_did"), 628 629 Data::String(AtprotoStr::AtIdentifier(AtIdentifier::Did( 629 - Did::new_owned("did:plc:abc").unwrap(), 630 + Did::new("did:plc:abc").unwrap(), 630 631 ))), 631 632 ); 632 633 map.insert( 633 634 SmolStr::new_static("ident_handle"), 634 635 Data::String(AtprotoStr::AtIdentifier(AtIdentifier::Handle( 635 - Handle::new_static("bob.test").unwrap(), 636 + Handle::new("bob.test").unwrap(), 636 637 ))), 637 638 ); 638 639 let data = Data::Object(Object(map)); ··· 730 731 created_at: "2024-01-15T12:30:45.123Z".to_string(), 731 732 }; 732 733 733 - let data = to_data(&post).unwrap(); 734 + let data: Data = to_data(&post).unwrap(); 734 735 735 736 match data { 736 737 Data::Object(obj) => { 737 738 // Check text is plain string 738 739 match obj.0.get("text").unwrap() { 739 - Data::String(AtprotoStr::String(s)) => assert_eq!(s.as_ref(), "hello world"), 740 + Data::String(AtprotoStr::String(s)) => assert_eq!(s, "hello world"), 740 741 _ => panic!("expected plain string for text"), 741 742 } 742 743 // Check DID was inferred ··· 774 775 let mut map_with_langs = BTreeMap::new(); 775 776 map_with_langs.insert( 776 777 SmolStr::new_static("text"), 777 - Data::String(AtprotoStr::String("hello".into())), 778 + Data::String(AtprotoStr::String("hello")), 778 779 ); 779 780 map_with_langs.insert( 780 781 SmolStr::new_static("langs"), ··· 794 795 let mut map_without_langs = BTreeMap::new(); 795 796 map_without_langs.insert( 796 797 SmolStr::new_static("text"), 797 - Data::String(AtprotoStr::String("world".into())), 798 + Data::String(AtprotoStr::String("world")), 798 799 ); 799 800 let data_without_langs = Data::Object(Object(map_without_langs)); 800 801 ··· 807 808 let mut map_with_null = BTreeMap::new(); 808 809 map_with_null.insert( 809 810 SmolStr::new_static("text"), 810 - Data::String(AtprotoStr::String("null test".into())), 811 + Data::String(AtprotoStr::String("null test")), 811 812 ); 812 813 map_with_null.insert(SmolStr::new_static("langs"), Data::Null); 813 814 let data_with_null = Data::Object(Object(map_with_null)); ··· 821 822 fn test_data_accessors() { 822 823 // Test as_object 823 824 let mut map = BTreeMap::new(); 824 - map.insert(SmolStr::new_static("key"), Data::Integer(42)); 825 + map.insert(SmolStr::new_static("key"), Data::<&str>::Integer(42)); 825 826 let obj_data = Data::Object(Object(map.clone())); 826 827 assert!(obj_data.as_object().is_some()); 827 828 assert_eq!(obj_data.as_object().unwrap().0.len(), 1); 828 - assert!(Data::Null.as_object().is_none()); 829 + assert!(Data::<&str>::Null.as_object().is_none()); 829 830 830 831 // Test as_array 831 - let arr_data = Data::Array(Array(vec![Data::Integer(1), Data::Integer(2)])); 832 + let arr_data = Data::<&str>::Array(Array(vec![Data::Integer(1), Data::Integer(2)])); 832 833 assert!(arr_data.as_array().is_some()); 833 834 assert_eq!(arr_data.as_array().unwrap().0.len(), 2); 834 - assert!(Data::Null.as_array().is_none()); 835 + assert!(Data::<&str>::Null.as_array().is_none()); 835 836 836 837 // Test as_str 837 - let str_data = Data::String(AtprotoStr::String("hello".into())); 838 + let str_data = Data::<&str>::String(AtprotoStr::String("hello".into())); 838 839 assert_eq!(str_data.as_str(), Some("hello")); 839 - assert!(Data::Null.as_str().is_none()); 840 + assert!(Data::<&str>::Null.as_str().is_none()); 840 841 841 842 // Test as_integer 842 - let int_data = Data::Integer(42); 843 + let int_data: Data = Data::Integer(42); 843 844 assert_eq!(int_data.as_integer(), Some(42)); 844 - assert!(Data::Null.as_integer().is_none()); 845 + assert!(Data::<&str>::Null.as_integer().is_none()); 845 846 846 847 // Test as_boolean 847 - let bool_data = Data::Boolean(true); 848 + let bool_data: Data = Data::Boolean(true); 848 849 assert_eq!(bool_data.as_boolean(), Some(true)); 849 - assert!(Data::Null.as_boolean().is_none()); 850 + assert!(Data::<&str>::Null.as_boolean().is_none()); 850 851 851 852 // Test is_null 852 - assert!(Data::Null.is_null()); 853 - assert!(!Data::Integer(0).is_null()); 853 + assert!(Data::<&str>::Null.is_null()); 854 + assert!(!Data::<&str>::Integer(0).is_null()); 854 855 } 855 856 856 857 #[test] ··· 887 888 #[test] 888 889 fn test_data_to_dag_cbor() { 889 890 // Test simple types 890 - let null_data = Data::Null; 891 + let null_data: Data = Data::Null; 891 892 assert!(null_data.to_dag_cbor().is_ok()); 892 893 893 - let int_data = Data::Integer(42); 894 + let int_data: Data = Data::Integer(42); 894 895 assert!(int_data.to_dag_cbor().is_ok()); 895 896 896 - let str_data = Data::String(AtprotoStr::String("hello".into())); 897 + let str_data: Data = Data::String(AtprotoStr::String("hello".into())); 897 898 assert!(str_data.to_dag_cbor().is_ok()); 898 899 899 900 // Test complex types 900 901 let mut map = BTreeMap::new(); 901 - map.insert(SmolStr::new_static("num"), Data::Integer(42)); 902 + map.insert(SmolStr::new_static("num"), Data::<SmolStr>::Integer(42)); 902 903 map.insert( 903 904 SmolStr::new_static("text"), 904 905 Data::String(AtprotoStr::String("test".into())), ··· 909 910 assert!(!cbor_result.unwrap().is_empty()); 910 911 911 912 // Test array 912 - let arr_data = Data::Array(Array(vec![ 913 + let arr_data: Data = Data::Array(Array(vec![ 913 914 Data::Integer(1), 914 915 Data::Integer(2), 915 916 Data::Integer(3), ··· 944 945 #[test] 945 946 fn test_object_methods() { 946 947 let mut map = BTreeMap::new(); 947 - map.insert(SmolStr::new_static("num"), Data::Integer(42)); 948 + map.insert(SmolStr::new_static("num"), Data::<SmolStr>::Integer(42)); 948 949 map.insert( 949 950 SmolStr::new_static("text"), 950 951 Data::String(AtprotoStr::String("hello".into())), ··· 964 965 assert_eq!(obj.len(), 2); 965 966 assert!(!obj.is_empty()); 966 967 967 - let empty_obj = Object(BTreeMap::new()); 968 + let empty_obj: Object = Object(BTreeMap::new()); 968 969 assert_eq!(empty_obj.len(), 0); 969 970 assert!(empty_obj.is_empty()); 970 971 ··· 979 980 980 981 #[test] 981 982 fn test_array_methods() { 982 - let arr = Array(vec![Data::Integer(1), Data::Integer(2), Data::Integer(3)]); 983 + let arr = Array::<&str>(vec![Data::Integer(1), Data::Integer(2), Data::Integer(3)]); 983 984 984 985 // Test get 985 986 assert_eq!(arr.get(0), Some(&Data::Integer(1))); ··· 990 991 assert_eq!(arr.len(), 3); 991 992 assert!(!arr.is_empty()); 992 993 993 - let empty_arr = Array(vec![]); 994 + let empty_arr = Array::<&str>(vec![]); 994 995 assert_eq!(empty_arr.len(), 0); 995 996 assert!(empty_arr.is_empty()); 996 997 ··· 1007 1008 let mut inner = BTreeMap::new(); 1008 1009 inner.insert( 1009 1010 SmolStr::new_static("alt"), 1010 - Data::String(AtprotoStr::String("test".into())), 1011 + Data::String(AtprotoStr::String("test")), 1011 1012 ); 1012 1013 1013 1014 let mut outer = BTreeMap::new(); ··· 1036 1037 let mut item1 = BTreeMap::new(); 1037 1038 item1.insert( 1038 1039 SmolStr::new_static("name"), 1039 - Data::String(AtprotoStr::String("first".into())), 1040 + Data::String(AtprotoStr::String("first")), 1040 1041 ); 1041 1042 1042 1043 let mut item2 = BTreeMap::new(); 1043 1044 item2.insert( 1044 1045 SmolStr::new_static("name"), 1045 - Data::String(AtprotoStr::String("second".into())), 1046 + Data::String(AtprotoStr::String("second")), 1046 1047 ); 1047 1048 1048 1049 let items = Data::Array(Array(vec![ ··· 1073 1074 let mut img1 = BTreeMap::new(); 1074 1075 img1.insert( 1075 1076 SmolStr::new_static("alt"), 1076 - Data::String(AtprotoStr::String("img1".into())), 1077 + Data::String(AtprotoStr::String("img1")), 1077 1078 ); 1078 1079 1079 1080 let mut img2 = BTreeMap::new(); 1080 1081 img2.insert( 1081 1082 SmolStr::new_static("alt"), 1082 - Data::String(AtprotoStr::String("img2".into())), 1083 + Data::String(AtprotoStr::String("img2")), 1083 1084 ); 1084 1085 1085 1086 let images = Data::Array(Array(vec![ ··· 1133 1134 let mut inner = BTreeMap::new(); 1134 1135 inner.insert( 1135 1136 SmolStr::new_static("handle"), 1136 - Data::String(AtprotoStr::String("alice.bsky.social".into())), 1137 + Data::String(AtprotoStr::String("alice.bsky.social")), 1137 1138 ); 1138 1139 1139 1140 let mut outer = BTreeMap::new(); ··· 1153 1154 let mut actor1 = BTreeMap::new(); 1154 1155 actor1.insert( 1155 1156 SmolStr::new_static("handle"), 1156 - Data::String(AtprotoStr::String("alice".into())), 1157 + Data::String(AtprotoStr::String("alice")), 1157 1158 ); 1158 1159 1159 1160 let mut actor2 = BTreeMap::new(); 1160 1161 actor2.insert( 1161 1162 SmolStr::new_static("handle"), 1162 - Data::String(AtprotoStr::String("bob".into())), 1163 + Data::String(AtprotoStr::String("bob")), 1163 1164 ); 1164 1165 1165 1166 let mut actor3 = BTreeMap::new(); 1166 1167 actor3.insert( 1167 1168 SmolStr::new_static("name"), 1168 - Data::String(AtprotoStr::String("carol".into())), 1169 + Data::String(AtprotoStr::String("carol")), 1169 1170 ); 1170 1171 1171 1172 let actors = Data::Array(Array(vec![ ··· 1194 1195 let mut images = BTreeMap::new(); 1195 1196 images.insert( 1196 1197 SmolStr::new_static("alt"), 1197 - Data::String(AtprotoStr::String("img".into())), 1198 + Data::String(AtprotoStr::String("img")), 1198 1199 ); 1199 1200 1200 1201 let mut video = BTreeMap::new(); 1201 1202 video.insert( 1202 1203 SmolStr::new_static("alt"), 1203 - Data::String(AtprotoStr::String("vid".into())), 1204 + Data::String(AtprotoStr::String("vid")), 1204 1205 ); 1205 1206 1206 1207 let mut embed = BTreeMap::new(); ··· 1224 1225 let mut handle_map = BTreeMap::new(); 1225 1226 handle_map.insert( 1226 1227 SmolStr::new_static("handle"), 1227 - Data::String(AtprotoStr::String("alice".into())), 1228 + Data::String(AtprotoStr::String("alice")), 1228 1229 ); 1229 1230 1230 1231 let mut profile_map = BTreeMap::new(); ··· 1260 1261 let mut inner1 = BTreeMap::new(); 1261 1262 inner1.insert( 1262 1263 SmolStr::new_static("cid"), 1263 - Data::String(AtprotoStr::String("cid1".into())), 1264 + Data::String(AtprotoStr::String("cid1")), 1264 1265 ); 1265 1266 1266 1267 let mut inner2 = BTreeMap::new(); 1267 1268 inner2.insert( 1268 1269 SmolStr::new_static("cid"), 1269 - Data::String(AtprotoStr::String("cid2".into())), 1270 + Data::String(AtprotoStr::String("cid2")), 1270 1271 ); 1271 1272 1272 1273 let mut middle = BTreeMap::new(); ··· 1277 1278 root.insert(SmolStr::new_static("thread"), Data::Object(Object(middle))); 1278 1279 root.insert( 1279 1280 SmolStr::new_static("cid"), 1280 - Data::String(AtprotoStr::String("cid3".into())), 1281 + Data::String(AtprotoStr::String("cid3")), 1281 1282 ); 1282 1283 1283 1284 let data = Data::Object(Object(root)); ··· 1301 1302 let mut actor1 = BTreeMap::new(); 1302 1303 actor1.insert( 1303 1304 SmolStr::new_static("handle"), 1304 - Data::String(AtprotoStr::String("alice".into())), 1305 + Data::String(AtprotoStr::String("alice")), 1305 1306 ); 1306 1307 1307 1308 let mut actor2 = BTreeMap::new(); 1308 1309 actor2.insert( 1309 1310 SmolStr::new_static("handle"), 1310 - Data::String(AtprotoStr::String("bob".into())), 1311 + Data::String(AtprotoStr::String("bob")), 1311 1312 ); 1312 1313 1313 1314 let actors = Data::Array(Array(vec![ ··· 1331 1332 #[test] 1332 1333 fn test_query_no_match() { 1333 1334 let mut map = BTreeMap::new(); 1334 - map.insert(SmolStr::new_static("foo"), Data::Integer(42)); 1335 + map.insert(SmolStr::new_static("foo"), Data::<&str>::Integer(42)); 1335 1336 let data = Data::Object(Object(map)); 1336 1337 1337 1338 // Field doesn't exist ··· 1345 1346 #[test] 1346 1347 fn test_query_result_helpers() { 1347 1348 let mut map = BTreeMap::new(); 1348 - map.insert(SmolStr::new_static("value"), Data::Integer(42)); 1349 + map.insert(SmolStr::new_static("value"), Data::<&str>::Integer(42)); 1349 1350 let data = Data::Object(Object(map)); 1350 1351 1351 1352 let result = data.query("value"); ··· 1380 1381 1381 1382 // Object without $type field 1382 1383 let mut map2 = BTreeMap::new(); 1383 - map2.insert(SmolStr::new_static("foo"), Data::Integer(42)); 1384 + map2.insert(SmolStr::new_static("foo"), Data::<&str>::Integer(42)); 1384 1385 let obj2 = Object(map2); 1385 1386 1386 1387 assert_eq!(obj2.type_discriminator(), None); ··· 1389 1390 assert_eq!(data2.type_discriminator(), None); 1390 1391 1391 1392 // Non-object data 1392 - let data3 = Data::Integer(42); 1393 + let data3: Data = Data::Integer(42); 1393 1394 assert_eq!(data3.type_discriminator(), None); 1394 1395 1395 1396 // RawData with $type
+69 -43
crates/jacquard-common/src/xrpc/atproto.rs
··· 29 29 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 30 30 #[serde(rename_all = "camelCase")] 31 31 #[allow(missing_docs)] 32 - pub struct ListRecords<'a> { 33 - #[serde(borrow)] 34 - pub collection: Nsid<CowStr<'a>>, 32 + pub struct ListRecords<S = DefaultStr> 33 + where 34 + S: Bos<str> + AsRef<str>, 35 + { 36 + pub collection: Nsid<S>, 35 37 #[serde(skip_serializing_if = "Option::is_none")] 36 - #[serde(borrow)] 37 - pub cursor: Option<CowStr<'a>>, 38 + pub cursor: Option<S>, 38 39 #[serde(skip_serializing_if = "Option::is_none")] 39 40 pub limit: Option<i64>, 40 - #[serde(borrow)] 41 - pub repo: AtIdentifier<CowStr<'a>>, 41 + pub repo: AtIdentifier<S>, 42 42 #[serde(skip_serializing_if = "Option::is_none")] 43 43 pub reverse: Option<bool>, 44 44 } 45 45 46 - impl IntoStatic for ListRecords<'_> { 47 - type Output = ListRecords<'static>; 46 + impl<S> IntoStatic for ListRecords<S> 47 + where 48 + S: Bos<str> + AsRef<str> + IntoStatic, 49 + S::Output: Bos<str> + AsRef<str>, 50 + { 51 + type Output = ListRecords<S::Output>; 48 52 49 53 fn into_static(self) -> Self::Output { 50 54 ListRecords { ··· 61 65 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 62 66 #[serde(rename_all = "camelCase")] 63 67 #[allow(missing_docs)] 64 - pub struct ListRecordsOutput<'a> { 68 + pub struct ListRecordsOutput<S = DefaultStr> 69 + where 70 + S: Bos<str> + AsRef<str>, 71 + { 65 72 #[serde(skip_serializing_if = "Option::is_none")] 66 - #[serde(borrow)] 67 - pub cursor: Option<CowStr<'a>>, 68 - #[serde(borrow)] 69 - pub records: Vec<ListRecordsRecord<'a>>, 73 + pub cursor: Option<S>, 74 + pub records: Vec<ListRecordsRecord<S>>, 70 75 } 71 76 72 - impl IntoStatic for ListRecordsOutput<'_> { 73 - type Output = ListRecordsOutput<'static>; 77 + impl<S> IntoStatic for ListRecordsOutput<S> 78 + where 79 + S: Bos<str> + AsRef<str> + IntoStatic, 80 + S::Output: Bos<str> + AsRef<str>, 81 + { 82 + type Output = ListRecordsOutput<S::Output>; 74 83 75 84 fn into_static(self) -> Self::Output { 76 85 ListRecordsOutput { ··· 84 93 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 85 94 #[serde(rename_all = "camelCase")] 86 95 #[allow(missing_docs)] 87 - pub struct ListRecordsRecord<'a> { 96 + pub struct ListRecordsRecord<S = DefaultStr> 97 + where 98 + S: Bos<str> + AsRef<str>, 99 + { 88 100 #[serde(skip_serializing_if = "Option::is_none")] 89 - #[serde(borrow)] 90 - pub cid: Option<Cid<CowStr<'a>>>, 91 - #[serde(borrow)] 92 - pub uri: AtUri<CowStr<'a>>, 93 - #[serde(borrow)] 94 - pub value: Data<'a>, 101 + pub cid: Option<Cid<S>>, 102 + pub uri: AtUri<S>, 103 + pub value: Data<S>, 95 104 } 96 105 97 - impl IntoStatic for ListRecordsRecord<'_> { 98 - type Output = ListRecordsRecord<'static>; 106 + impl<S> IntoStatic for ListRecordsRecord<S> 107 + where 108 + S: Bos<str> + AsRef<str> + IntoStatic, 109 + S::Output: Bos<str> + AsRef<str>, 110 + { 111 + type Output = ListRecordsRecord<<S as IntoStatic>::Output>; 99 112 100 113 fn into_static(self) -> Self::Output { 101 114 ListRecordsRecord { ··· 112 125 impl XrpcResp for ListRecordsResponse { 113 126 const NSID: &'static str = "com.atproto.repo.listRecords"; 114 127 const ENCODING: &'static str = "application/json"; 115 - type Output<'de> = ListRecordsOutput<'de>; 128 + type Output<'de> = ListRecordsOutput; 116 129 type Err<'de> = GenericError<'de>; 117 130 } 118 131 119 - impl<'a> XrpcRequest for ListRecords<'a> { 132 + impl<'a, S> XrpcRequest for ListRecords<S> 133 + where 134 + S: Bos<str> + AsRef<str> + Serialize, 135 + { 120 136 const NSID: &'static str = "com.atproto.repo.listRecords"; 121 137 const METHOD: XrpcMethod = XrpcMethod::Query; 122 138 type Response = ListRecordsResponse; ··· 159 175 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 160 176 #[allow(missing_docs)] 161 177 #[serde(rename_all = "camelCase")] 162 - pub struct GetRecordOutput<'a> { 178 + pub struct GetRecordOutput<S = DefaultStr> 179 + where 180 + S: Bos<str> + AsRef<str>, 181 + { 163 182 #[serde(skip_serializing_if = "Option::is_none")] 164 - #[serde(borrow)] 165 - pub cid: Option<Cid<CowStr<'a>>>, 166 - #[serde(borrow)] 167 - pub uri: AtUri<CowStr<'a>>, 168 - #[serde(borrow)] 169 - pub value: Data<'a>, 183 + pub cid: Option<Cid<S>>, 184 + pub uri: AtUri<S>, 185 + pub value: Data<S>, 170 186 } 171 187 172 - impl IntoStatic for GetRecordOutput<'_> { 173 - type Output = GetRecordOutput<'static>; 188 + impl<S> IntoStatic for GetRecordOutput<S> 189 + where 190 + S: Bos<str> + AsRef<str> + IntoStatic, 191 + S::Output: Bos<str> + AsRef<str>, 192 + { 193 + type Output = GetRecordOutput<S::Output>; 174 194 175 195 fn into_static(self) -> Self::Output { 176 196 GetRecordOutput { ··· 223 243 impl XrpcResp for GetRecordResponse { 224 244 const NSID: &'static str = "com.atproto.repo.getRecord"; 225 245 const ENCODING: &'static str = "application/json"; 226 - type Output<'de> = GetRecordOutput<'de>; 246 + type Output<'de> = GetRecordOutput; 227 247 type Err<'de> = GetRecordError<'de>; 228 248 } 229 249 ··· 366 386 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] 367 387 #[serde(rename_all = "camelCase")] 368 388 #[allow(missing_docs)] 369 - pub struct ResolveDidOutput<'a> { 370 - #[serde(borrow)] 371 - pub did_doc: Data<'a>, 389 + pub struct ResolveDidOutput<S = DefaultStr> 390 + where 391 + S: Bos<str> + AsRef<str>, 392 + { 393 + pub did_doc: Data<S>, 372 394 } 373 395 374 - impl IntoStatic for ResolveDidOutput<'_> { 375 - type Output = ResolveDidOutput<'static>; 396 + impl<S> IntoStatic for ResolveDidOutput<S> 397 + where 398 + S: Bos<str> + AsRef<str> + IntoStatic, 399 + S::Output: Bos<str> + AsRef<str>, 400 + { 401 + type Output = ResolveDidOutput<S::Output>; 376 402 377 403 fn into_static(self) -> Self::Output { 378 404 ResolveDidOutput { ··· 433 459 impl XrpcResp for ResolveDidResponse { 434 460 const NSID: &'static str = "com.atproto.identity.resolveDid"; 435 461 const ENCODING: &'static str = "application/json"; 436 - type Output<'de> = ResolveDidOutput<'de>; 462 + type Output<'de> = ResolveDidOutput; 437 463 type Err<'de> = ResolveDidError<'de>; 438 464 } 439 465