model.rs (23300B)
1 use crate::error::RadrootsSimplexChatProtoError; 2 use crate::version::RadrootsSimplexChatVersionRange; 3 use alloc::boxed::Box; 4 use alloc::collections::BTreeMap; 5 use alloc::string::{String, ToString}; 6 use base64::Engine as _; 7 use base64::engine::general_purpose::URL_SAFE_NO_PAD; 8 use serde::de::Error as _; 9 use serde::{Deserialize, Deserializer, Serialize, Serializer}; 10 use serde_json::{Map, Value}; 11 12 pub type RadrootsSimplexChatObject = Map<String, Value>; 13 14 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 15 pub struct RadrootsSimplexChatBase64Url(String); 16 17 impl RadrootsSimplexChatBase64Url { 18 pub fn new(value: impl Into<String>) -> Result<Self, RadrootsSimplexChatProtoError> { 19 let value = value.into(); 20 URL_SAFE_NO_PAD.decode(value.as_bytes()).map_err(|_| { 21 RadrootsSimplexChatProtoError::InvalidBase64Url { 22 field: "base64url", 23 value: value.clone(), 24 } 25 })?; 26 Ok(Self(value)) 27 } 28 29 pub fn parse_field( 30 value: String, 31 field: &'static str, 32 ) -> Result<Self, RadrootsSimplexChatProtoError> { 33 URL_SAFE_NO_PAD.decode(value.as_bytes()).map_err(|_| { 34 RadrootsSimplexChatProtoError::InvalidBase64Url { 35 field, 36 value: value.clone(), 37 } 38 })?; 39 Ok(Self(value)) 40 } 41 42 pub fn as_str(&self) -> &str { 43 &self.0 44 } 45 } 46 47 impl Serialize for RadrootsSimplexChatBase64Url { 48 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 49 where 50 S: Serializer, 51 { 52 serializer.serialize_str(self.as_str()) 53 } 54 } 55 56 impl<'de> Deserialize<'de> for RadrootsSimplexChatBase64Url { 57 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 58 where 59 D: Deserializer<'de>, 60 { 61 let value = <String as Deserialize>::deserialize(deserializer)?; 62 Self::new(value).map_err(D::Error::custom) 63 } 64 } 65 66 #[derive(Debug, Clone, PartialEq, Eq)] 67 pub enum RadrootsSimplexChatPeerType { 68 Human, 69 Bot, 70 Unknown(String), 71 } 72 73 impl Serialize for RadrootsSimplexChatPeerType { 74 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 75 where 76 S: Serializer, 77 { 78 serializer.serialize_str(match self { 79 Self::Human => "human", 80 Self::Bot => "bot", 81 Self::Unknown(value) => value, 82 }) 83 } 84 } 85 86 impl<'de> Deserialize<'de> for RadrootsSimplexChatPeerType { 87 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 88 where 89 D: Deserializer<'de>, 90 { 91 let value = <String as Deserialize>::deserialize(deserializer)?; 92 Ok(match value.as_str() { 93 "human" => Self::Human, 94 "bot" => Self::Bot, 95 _ => Self::Unknown(value), 96 }) 97 } 98 } 99 100 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 101 pub struct RadrootsSimplexChatProfile { 102 #[serde(rename = "displayName")] 103 pub display_name: String, 104 #[serde(rename = "fullName")] 105 pub full_name: String, 106 #[serde(skip_serializing_if = "Option::is_none")] 107 pub image: Option<String>, 108 #[serde(rename = "shortDescr", skip_serializing_if = "Option::is_none")] 109 pub short_descr: Option<String>, 110 #[serde(rename = "contactLink", skip_serializing_if = "Option::is_none")] 111 pub contact_link: Option<String>, 112 #[serde(rename = "peerType", skip_serializing_if = "Option::is_none")] 113 pub peer_type: Option<RadrootsSimplexChatPeerType>, 114 #[serde(skip_serializing_if = "Option::is_none")] 115 pub preferences: Option<Value>, 116 #[serde(flatten, default)] 117 pub extra: RadrootsSimplexChatObject, 118 } 119 120 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 121 pub struct RadrootsSimplexChatMessageRef { 122 #[serde(rename = "msgId", skip_serializing_if = "Option::is_none")] 123 pub msg_id: Option<RadrootsSimplexChatBase64Url>, 124 #[serde(rename = "sentAt")] 125 pub sent_at: String, 126 pub sent: bool, 127 #[serde(rename = "memberId", skip_serializing_if = "Option::is_none")] 128 pub member_id: Option<RadrootsSimplexChatBase64Url>, 129 #[serde(flatten, default)] 130 pub extra: RadrootsSimplexChatObject, 131 } 132 133 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 134 pub struct RadrootsSimplexChatMention { 135 #[serde(rename = "memberId")] 136 pub member_id: RadrootsSimplexChatBase64Url, 137 #[serde(flatten, default)] 138 pub extra: RadrootsSimplexChatObject, 139 } 140 141 #[derive(Debug, Clone, PartialEq)] 142 pub enum RadrootsSimplexChatLinkContent { 143 Page { 144 extra: RadrootsSimplexChatObject, 145 }, 146 Image { 147 extra: RadrootsSimplexChatObject, 148 }, 149 Video { 150 duration: Option<i64>, 151 extra: RadrootsSimplexChatObject, 152 }, 153 Unknown { 154 content_type: String, 155 raw: RadrootsSimplexChatObject, 156 }, 157 } 158 159 impl Serialize for RadrootsSimplexChatLinkContent { 160 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 161 where 162 S: Serializer, 163 { 164 let mut object = RadrootsSimplexChatObject::new(); 165 match self { 166 Self::Page { extra } => { 167 object.insert(String::from("type"), Value::String(String::from("page"))); 168 object.extend(extra.clone()); 169 } 170 Self::Image { extra } => { 171 object.insert(String::from("type"), Value::String(String::from("image"))); 172 object.extend(extra.clone()); 173 } 174 Self::Video { duration, extra } => { 175 object.insert(String::from("type"), Value::String(String::from("video"))); 176 if let Some(duration) = duration { 177 object.insert(String::from("duration"), Value::from(*duration)); 178 } 179 object.extend(extra.clone()); 180 } 181 Self::Unknown { raw, .. } => { 182 object = raw.clone(); 183 } 184 } 185 object.serialize(serializer) 186 } 187 } 188 189 impl<'de> Deserialize<'de> for RadrootsSimplexChatLinkContent { 190 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 191 where 192 D: Deserializer<'de>, 193 { 194 let mut raw = <RadrootsSimplexChatObject as Deserialize>::deserialize(deserializer)?; 195 let content_type = match raw.remove("type") { 196 Some(Value::String(value)) => value, 197 Some(_) => return Err(D::Error::custom("invalid link content type")), 198 None => return Err(D::Error::custom("missing link content type")), 199 }; 200 201 Ok(match content_type.as_str() { 202 "page" => Self::Page { extra: raw }, 203 "image" => Self::Image { extra: raw }, 204 "video" => { 205 let duration = match raw.remove("duration") { 206 Some(Value::Number(value)) => value.as_i64(), 207 Some(_) => return Err(D::Error::custom("invalid duration")), 208 None => None, 209 }; 210 Self::Video { 211 duration, 212 extra: raw, 213 } 214 } 215 _ => { 216 raw.insert(String::from("type"), Value::String(content_type.clone())); 217 Self::Unknown { content_type, raw } 218 } 219 }) 220 } 221 } 222 223 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 224 pub struct RadrootsSimplexChatLinkPreview { 225 pub uri: String, 226 pub title: String, 227 pub description: String, 228 pub image: RadrootsSimplexChatBase64Url, 229 #[serde(skip_serializing_if = "Option::is_none")] 230 pub content: Option<RadrootsSimplexChatLinkContent>, 231 #[serde(flatten, default)] 232 pub extra: RadrootsSimplexChatObject, 233 } 234 235 #[derive(Debug, Clone, PartialEq)] 236 pub enum RadrootsSimplexChatContent { 237 Text { 238 text: String, 239 extra: RadrootsSimplexChatObject, 240 }, 241 Link { 242 text: String, 243 preview: RadrootsSimplexChatLinkPreview, 244 extra: RadrootsSimplexChatObject, 245 }, 246 Image { 247 text: String, 248 image: RadrootsSimplexChatBase64Url, 249 extra: RadrootsSimplexChatObject, 250 }, 251 Video { 252 text: String, 253 image: RadrootsSimplexChatBase64Url, 254 duration: i64, 255 extra: RadrootsSimplexChatObject, 256 }, 257 Voice { 258 text: String, 259 duration: i64, 260 extra: RadrootsSimplexChatObject, 261 }, 262 File { 263 text: String, 264 extra: RadrootsSimplexChatObject, 265 }, 266 Unknown { 267 content_type: String, 268 text: Option<String>, 269 raw: RadrootsSimplexChatObject, 270 }, 271 } 272 273 impl Serialize for RadrootsSimplexChatContent { 274 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 275 where 276 S: Serializer, 277 { 278 let mut object = RadrootsSimplexChatObject::new(); 279 match self { 280 Self::Text { text, extra } => { 281 object.insert(String::from("type"), Value::String(String::from("text"))); 282 object.insert(String::from("text"), Value::String(text.clone())); 283 object.extend(extra.clone()); 284 } 285 Self::Link { 286 text, 287 preview, 288 extra, 289 } => { 290 object.insert(String::from("type"), Value::String(String::from("link"))); 291 object.insert(String::from("text"), Value::String(text.clone())); 292 object.insert( 293 String::from("preview"), 294 serde_json::to_value(preview).map_err(serde::ser::Error::custom)?, 295 ); 296 object.extend(extra.clone()); 297 } 298 Self::Image { text, image, extra } => { 299 object.insert(String::from("type"), Value::String(String::from("image"))); 300 object.insert(String::from("text"), Value::String(text.clone())); 301 object.insert( 302 String::from("image"), 303 Value::String(image.as_str().to_string()), 304 ); 305 object.extend(extra.clone()); 306 } 307 Self::Video { 308 text, 309 image, 310 duration, 311 extra, 312 } => { 313 object.insert(String::from("type"), Value::String(String::from("video"))); 314 object.insert(String::from("text"), Value::String(text.clone())); 315 object.insert( 316 String::from("image"), 317 Value::String(image.as_str().to_string()), 318 ); 319 object.insert(String::from("duration"), Value::from(*duration)); 320 object.extend(extra.clone()); 321 } 322 Self::Voice { 323 text, 324 duration, 325 extra, 326 } => { 327 object.insert(String::from("type"), Value::String(String::from("voice"))); 328 object.insert(String::from("text"), Value::String(text.clone())); 329 object.insert(String::from("duration"), Value::from(*duration)); 330 object.extend(extra.clone()); 331 } 332 Self::File { text, extra } => { 333 object.insert(String::from("type"), Value::String(String::from("file"))); 334 object.insert(String::from("text"), Value::String(text.clone())); 335 object.extend(extra.clone()); 336 } 337 Self::Unknown { raw, .. } => { 338 object = raw.clone(); 339 } 340 } 341 342 object.serialize(serializer) 343 } 344 } 345 346 impl<'de> Deserialize<'de> for RadrootsSimplexChatContent { 347 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 348 where 349 D: Deserializer<'de>, 350 { 351 let mut raw = <RadrootsSimplexChatObject as Deserialize>::deserialize(deserializer)?; 352 let content_type = match raw.remove("type") { 353 Some(Value::String(value)) => value, 354 Some(_) => return Err(D::Error::custom("invalid content type")), 355 None => return Err(D::Error::custom("missing content type")), 356 }; 357 358 Ok(match content_type.as_str() { 359 "text" => Self::Text { 360 text: expect_string::<D>(&mut raw, "text")?, 361 extra: raw, 362 }, 363 "link" => Self::Link { 364 text: expect_string::<D>(&mut raw, "text")?, 365 preview: serde_json::from_value( 366 expect_value(&mut raw, "preview").map_err(D::Error::custom)?, 367 ) 368 .map_err(D::Error::custom)?, 369 extra: raw, 370 }, 371 "image" => Self::Image { 372 text: expect_string::<D>(&mut raw, "text")?, 373 image: expect_base64url::<D>(&mut raw, "image")?, 374 extra: raw, 375 }, 376 "video" => Self::Video { 377 text: expect_string::<D>(&mut raw, "text")?, 378 image: expect_base64url::<D>(&mut raw, "image")?, 379 duration: expect_i64::<D>(&mut raw, "duration")?, 380 extra: raw, 381 }, 382 "voice" => Self::Voice { 383 text: expect_string::<D>(&mut raw, "text")?, 384 duration: expect_i64::<D>(&mut raw, "duration")?, 385 extra: raw, 386 }, 387 "file" => Self::File { 388 text: expect_string::<D>(&mut raw, "text")?, 389 extra: raw, 390 }, 391 _ => { 392 let text = match raw.get("text") { 393 Some(Value::String(value)) => Some(value.clone()), 394 _ => None, 395 }; 396 raw.insert(String::from("type"), Value::String(content_type.clone())); 397 Self::Unknown { 398 content_type, 399 text, 400 raw, 401 } 402 } 403 }) 404 } 405 } 406 407 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 408 pub struct RadrootsSimplexChatFileDescription { 409 #[serde(rename = "fileDescrText")] 410 pub file_descr_text: String, 411 #[serde(rename = "fileDescrPartNo")] 412 pub file_descr_part_no: i64, 413 #[serde(rename = "fileDescrComplete")] 414 pub file_descr_complete: bool, 415 #[serde(flatten, default)] 416 pub extra: RadrootsSimplexChatObject, 417 } 418 419 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 420 pub struct RadrootsSimplexChatFileInvitation { 421 #[serde(rename = "fileName")] 422 pub file_name: String, 423 #[serde(rename = "fileSize")] 424 pub file_size: u32, 425 #[serde(rename = "fileDigest", skip_serializing_if = "Option::is_none")] 426 pub file_digest: Option<RadrootsSimplexChatBase64Url>, 427 #[serde(rename = "fileConnReq", skip_serializing_if = "Option::is_none")] 428 pub file_conn_req: Option<String>, 429 #[serde(rename = "fileDescr", skip_serializing_if = "Option::is_none")] 430 pub file_descr: Option<RadrootsSimplexChatFileDescription>, 431 #[serde(flatten, default)] 432 pub extra: RadrootsSimplexChatObject, 433 } 434 435 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 436 pub struct RadrootsSimplexChatQuotedMessage { 437 #[serde(rename = "msgRef")] 438 pub msg_ref: RadrootsSimplexChatMessageRef, 439 pub content: RadrootsSimplexChatContent, 440 #[serde(flatten, default)] 441 pub extra: RadrootsSimplexChatObject, 442 } 443 444 #[derive(Debug, Clone, PartialEq)] 445 pub enum RadrootsSimplexChatForwardMarker { 446 Flag, 447 Object(RadrootsSimplexChatObject), 448 } 449 450 #[derive(Debug, Clone, PartialEq)] 451 pub enum RadrootsSimplexChatScope { 452 Member { 453 member_id: RadrootsSimplexChatBase64Url, 454 extra: RadrootsSimplexChatObject, 455 }, 456 Unknown(Value), 457 } 458 459 impl Serialize for RadrootsSimplexChatScope { 460 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 461 where 462 S: Serializer, 463 { 464 match self { 465 Self::Member { member_id, extra } => { 466 let mut data = RadrootsSimplexChatObject::new(); 467 data.insert( 468 String::from("memberId"), 469 Value::String(member_id.as_str().to_string()), 470 ); 471 data.extend(extra.clone()); 472 473 let mut object = RadrootsSimplexChatObject::new(); 474 object.insert(String::from("type"), Value::String(String::from("member"))); 475 object.insert(String::from("data"), Value::Object(data)); 476 object.serialize(serializer) 477 } 478 Self::Unknown(value) => value.serialize(serializer), 479 } 480 } 481 } 482 483 impl<'de> Deserialize<'de> for RadrootsSimplexChatScope { 484 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 485 where 486 D: Deserializer<'de>, 487 { 488 let value = Value::deserialize(deserializer)?; 489 let original = value.clone(); 490 let Value::Object(mut object) = value else { 491 return Ok(Self::Unknown(original)); 492 }; 493 494 let Some(Value::String(scope_type)) = object.remove("type") else { 495 return Ok(Self::Unknown(original)); 496 }; 497 498 if scope_type != "member" { 499 return Ok(Self::Unknown(original)); 500 } 501 502 let Some(Value::Object(mut data)) = object.remove("data") else { 503 return Ok(Self::Unknown(original)); 504 }; 505 506 let member_id = expect_base64url::<D>(&mut data, "memberId")?; 507 Ok(Self::Member { 508 member_id, 509 extra: data, 510 }) 511 } 512 } 513 514 #[derive(Debug, Clone, PartialEq)] 515 pub enum RadrootsSimplexChatContainerKind { 516 Simple, 517 Quote(Box<RadrootsSimplexChatQuotedMessage>), 518 Comment(RadrootsSimplexChatMessageRef), 519 Forward(RadrootsSimplexChatForwardMarker), 520 } 521 522 #[derive(Debug, Clone, PartialEq)] 523 pub struct RadrootsSimplexChatMessageContainer { 524 pub kind: RadrootsSimplexChatContainerKind, 525 pub content: RadrootsSimplexChatContent, 526 pub mentions: BTreeMap<String, RadrootsSimplexChatMention>, 527 pub file: Option<RadrootsSimplexChatFileInvitation>, 528 pub ttl: Option<i64>, 529 pub live: Option<bool>, 530 pub scope: Option<RadrootsSimplexChatScope>, 531 pub extra: RadrootsSimplexChatObject, 532 } 533 534 #[derive(Debug, Clone, PartialEq)] 535 pub struct RadrootsSimplexChatMessageContentReference { 536 pub msg_id: RadrootsSimplexChatBase64Url, 537 pub content: RadrootsSimplexChatContent, 538 } 539 540 #[derive(Debug, Clone, PartialEq)] 541 pub struct RadrootsSimplexChatNoParamsEvent { 542 pub extra: RadrootsSimplexChatObject, 543 } 544 545 #[derive(Debug, Clone, PartialEq)] 546 pub struct RadrootsSimplexChatContactEvent { 547 pub profile: RadrootsSimplexChatProfile, 548 pub contact_req_id: Option<RadrootsSimplexChatBase64Url>, 549 pub welcome_msg_id: Option<RadrootsSimplexChatBase64Url>, 550 pub request_message: Option<RadrootsSimplexChatMessageContentReference>, 551 pub extra: RadrootsSimplexChatObject, 552 } 553 554 #[derive(Debug, Clone, PartialEq)] 555 pub struct RadrootsSimplexChatInfoEvent { 556 pub profile: RadrootsSimplexChatProfile, 557 pub extra: RadrootsSimplexChatObject, 558 } 559 560 #[derive(Debug, Clone, PartialEq)] 561 pub struct RadrootsSimplexChatProbeEvent { 562 pub probe: RadrootsSimplexChatBase64Url, 563 pub extra: RadrootsSimplexChatObject, 564 } 565 566 #[derive(Debug, Clone, PartialEq)] 567 pub struct RadrootsSimplexChatProbeCheckEvent { 568 pub probe_hash: RadrootsSimplexChatBase64Url, 569 pub extra: RadrootsSimplexChatObject, 570 } 571 572 #[derive(Debug, Clone, PartialEq)] 573 pub struct RadrootsSimplexChatMsgNewEvent { 574 pub container: RadrootsSimplexChatMessageContainer, 575 } 576 577 #[derive(Debug, Clone, PartialEq)] 578 pub struct RadrootsSimplexChatFileDescriptionEvent { 579 pub msg_id: RadrootsSimplexChatBase64Url, 580 pub file_descr: RadrootsSimplexChatFileDescription, 581 pub extra: RadrootsSimplexChatObject, 582 } 583 584 #[derive(Debug, Clone, PartialEq)] 585 pub struct RadrootsSimplexChatMsgUpdateEvent { 586 pub msg_id: RadrootsSimplexChatBase64Url, 587 pub content: RadrootsSimplexChatContent, 588 pub mentions: BTreeMap<String, RadrootsSimplexChatMention>, 589 pub ttl: Option<i64>, 590 pub live: Option<bool>, 591 pub scope: Option<RadrootsSimplexChatScope>, 592 pub extra: RadrootsSimplexChatObject, 593 } 594 595 #[derive(Debug, Clone, PartialEq)] 596 pub struct RadrootsSimplexChatDeleteEvent { 597 pub msg_id: RadrootsSimplexChatBase64Url, 598 pub member_id: Option<RadrootsSimplexChatBase64Url>, 599 pub scope: Option<RadrootsSimplexChatScope>, 600 pub extra: RadrootsSimplexChatObject, 601 } 602 603 #[derive(Debug, Clone, PartialEq)] 604 pub struct RadrootsSimplexChatFileAcceptEvent { 605 pub file_name: String, 606 pub extra: RadrootsSimplexChatObject, 607 } 608 609 #[derive(Debug, Clone, PartialEq)] 610 pub struct RadrootsSimplexChatFileAcceptInvitationEvent { 611 pub msg_id: RadrootsSimplexChatBase64Url, 612 pub file_conn_req: Option<String>, 613 pub file_name: String, 614 pub extra: RadrootsSimplexChatObject, 615 } 616 617 #[derive(Debug, Clone, PartialEq)] 618 pub struct RadrootsSimplexChatFileCancelEvent { 619 pub msg_id: RadrootsSimplexChatBase64Url, 620 pub extra: RadrootsSimplexChatObject, 621 } 622 623 #[derive(Debug, Clone, PartialEq)] 624 pub enum RadrootsSimplexChatEvent { 625 Contact(RadrootsSimplexChatContactEvent), 626 Info(RadrootsSimplexChatInfoEvent), 627 InfoProbe(RadrootsSimplexChatProbeEvent), 628 InfoProbeCheck(RadrootsSimplexChatProbeCheckEvent), 629 InfoProbeOk(RadrootsSimplexChatProbeEvent), 630 MsgNew(Box<RadrootsSimplexChatMsgNewEvent>), 631 MsgFileDescr(RadrootsSimplexChatFileDescriptionEvent), 632 MsgUpdate(RadrootsSimplexChatMsgUpdateEvent), 633 MsgDel(RadrootsSimplexChatDeleteEvent), 634 FileAcpt(RadrootsSimplexChatFileAcceptEvent), 635 FileAcptInv(RadrootsSimplexChatFileAcceptInvitationEvent), 636 FileCancel(RadrootsSimplexChatFileCancelEvent), 637 DirectDel(RadrootsSimplexChatNoParamsEvent), 638 Ok(RadrootsSimplexChatNoParamsEvent), 639 Unknown { 640 event: String, 641 params: RadrootsSimplexChatObject, 642 }, 643 } 644 645 #[derive(Debug, Clone, PartialEq)] 646 pub struct RadrootsSimplexChatMessage { 647 pub version: Option<RadrootsSimplexChatVersionRange>, 648 pub msg_id: Option<RadrootsSimplexChatBase64Url>, 649 pub event: RadrootsSimplexChatEvent, 650 } 651 652 pub(crate) fn expect_value( 653 map: &mut RadrootsSimplexChatObject, 654 field: &'static str, 655 ) -> Result<Value, RadrootsSimplexChatProtoError> { 656 map.remove(field) 657 .ok_or(RadrootsSimplexChatProtoError::MissingField(field)) 658 } 659 660 fn expect_string<'de, D>( 661 map: &mut RadrootsSimplexChatObject, 662 field: &'static str, 663 ) -> Result<String, D::Error> 664 where 665 D: Deserializer<'de>, 666 { 667 match expect_value(map, field).map_err(D::Error::custom)? { 668 Value::String(value) => Ok(value), 669 _ => Err(D::Error::custom( 670 RadrootsSimplexChatProtoError::InvalidField(field), 671 )), 672 } 673 } 674 675 fn expect_i64<'de, D>( 676 map: &mut RadrootsSimplexChatObject, 677 field: &'static str, 678 ) -> Result<i64, D::Error> 679 where 680 D: Deserializer<'de>, 681 { 682 match expect_value(map, field).map_err(D::Error::custom)? { 683 Value::Number(value) => value 684 .as_i64() 685 .ok_or_else(|| D::Error::custom(RadrootsSimplexChatProtoError::InvalidField(field))), 686 _ => Err(D::Error::custom( 687 RadrootsSimplexChatProtoError::InvalidField(field), 688 )), 689 } 690 } 691 692 fn expect_base64url<'de, D>( 693 map: &mut RadrootsSimplexChatObject, 694 field: &'static str, 695 ) -> Result<RadrootsSimplexChatBase64Url, D::Error> 696 where 697 D: Deserializer<'de>, 698 { 699 expect_string::<D>(map, field).and_then(|value| { 700 RadrootsSimplexChatBase64Url::parse_field(value, field).map_err(D::Error::custom) 701 }) 702 }