imessage_database/message_types/
translation.rs1use plist::Value;
5use std::io::Cursor;
6
7use crate::{
8 error::plist::PlistParseError,
9 util::plist::{
10 extract_array_key, extract_bytes_key, extract_dict_idx, extract_dictionary,
11 extract_string_key, parse_ns_keyed_archiver, plist_as_dictionary,
12 },
13};
14
15#[derive(Debug, PartialEq, Eq)]
17pub struct Translation {
18 pub translated_text: String,
20 pub translation_lang: String,
22 pub source_lang: String,
24}
25
26impl Translation {
27 pub fn from_payload(plist: &Value) -> Result<Self, PlistParseError> {
29 let translation_data = extract_bytes_key(plist_as_dictionary(plist)?, "tmp")?;
30
31 let inner_plist = parse_ns_keyed_archiver(
32 &Value::from_reader(Cursor::new(translation_data))
33 .map_err(|_| PlistParseError::NoPayload)?,
34 )?;
35
36 let translation_data = extract_dict_idx(
38 extract_array_key(
39 extract_dictionary(plist_as_dictionary(&inner_plist)?, "0")?,
40 "0",
41 )?,
42 0,
43 )?;
44
45 Ok(Self {
46 translated_text: extract_string_key(
47 extract_dictionary(translation_data, "translatedText")?,
48 "NSString",
49 )?
50 .to_string(),
51 translation_lang: extract_string_key(translation_data, "translationLanguage")?
52 .to_string(),
53 source_lang: extract_string_key(translation_data, "sourceLanguage")?.to_string(),
54 })
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use std::{env::current_dir, fs::File};
61
62 use plist::Value;
63
64 use crate::message_types::translation::Translation;
65
66 #[test]
67 fn test_parse_translation() {
68 let plist_path = current_dir()
69 .unwrap()
70 .as_path()
71 .join("test_data/app_message/Translation.plist");
72 let plist_data = File::open(plist_path).unwrap();
73 let plist = Value::from_reader(plist_data).unwrap();
74
75 let translation = Translation::from_payload(&plist).unwrap();
76
77 println!("{:#?}", translation);
78 assert_eq!(
79 translation.translated_text,
80 "Oh, il a traduit ce que j'ai envoyé !"
81 );
82 assert_eq!(translation.source_lang, "en_US");
83 assert_eq!(translation.translation_lang, "fr_FR");
84 }
85
86 #[test]
87 fn test_parse_translation_received() {
88 let plist_path = current_dir()
89 .unwrap()
90 .as_path()
91 .join("test_data/app_message/TranslationReceived.plist");
92 let plist_data = File::open(plist_path).unwrap();
93 let plist = Value::from_reader(plist_data).unwrap();
94
95 let translation = Translation::from_payload(&plist).unwrap();
96
97 println!("{:#?}", translation);
98 assert_eq!(translation.translated_text, "I want chicken");
99 assert_eq!(translation.source_lang, "fr_FR");
100 assert_eq!(translation.translation_lang, "en_US");
101 }
102}