From 6ebc53c31d7634f6162f732d5831f255e95a5f55 Mon Sep 17 00:00:00 2001
From: Christopher Sardegna
Date: Mon, 6 May 2024 09:01:09 -0700
Subject: [PATCH 1/8] add destination_caller_id field
---
imessage-database/src/tables/messages.rs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/imessage-database/src/tables/messages.rs b/imessage-database/src/tables/messages.rs
index f848f10a..bc68dcde 100644
--- a/imessage-database/src/tables/messages.rs
+++ b/imessage-database/src/tables/messages.rs
@@ -78,6 +78,7 @@ pub struct Message {
pub text: Option,
pub service: Option,
pub handle_id: Option,
+ pub destination_caller_id: Option,
pub subject: Option,
pub date: i64,
pub date_read: i64,
@@ -108,6 +109,7 @@ impl Table for Message {
text: row.get("text").unwrap_or(None),
service: row.get("service").unwrap_or(None),
handle_id: row.get("handle_id").unwrap_or(None),
+ destination_caller_id: row.get("destination_caller_id").unwrap_or(None),
subject: row.get("subject").unwrap_or(None),
date: row.get("date")?,
date_read: row.get("date_read").unwrap_or(0),
@@ -966,6 +968,7 @@ mod tests {
text: None,
service: Some("iMessage".to_string()),
handle_id: Some(i32::default()),
+ destination_caller_id: None,
subject: None,
date: i64::default(),
date_read: i64::default(),
From 9cf8cb8191d21f30448f7eba8b4e47f34b6ca8f2 Mon Sep 17 00:00:00 2001
From: Christopher Sardegna
Date: Mon, 6 May 2024 09:02:44 -0700
Subject: [PATCH 2/8] support destination_caller_id for #237
---
imessage-exporter/src/app/options.rs | 27 +++++++++++++++++++--
imessage-exporter/src/app/runtime.rs | 31 ++++++++++++++++++-------
imessage-exporter/src/exporters/html.rs | 19 +++++++++++----
imessage-exporter/src/exporters/txt.rs | 25 ++++++++++++++++----
4 files changed, 83 insertions(+), 19 deletions(-)
diff --git a/imessage-exporter/src/app/options.rs b/imessage-exporter/src/app/options.rs
index a1f57a4a..694b9c6c 100644
--- a/imessage-exporter/src/app/options.rs
+++ b/imessage-exporter/src/app/options.rs
@@ -31,6 +31,7 @@ pub const OPTION_DISABLE_LAZY_LOADING: &str = "no-lazy";
pub const OPTION_CUSTOM_NAME: &str = "custom-name";
pub const OPTION_PLATFORM: &str = "platform";
pub const OPTION_BYPASS_FREE_SPACE_CHECK: &str = "ignore-disk-warning";
+pub const OPTION_USE_CALLER_ID: &str = "use-caller-id";
// Other CLI Text
pub const SUPPORTED_FILE_TYPES: &str = "txt, html";
@@ -62,6 +63,8 @@ pub struct Options {
pub no_lazy: bool,
/// Custom name for database owner in output
pub custom_name: Option,
+ /// If true, use the database owners caller ID instead of "Me"
+ pub use_caller_id: bool,
/// The database source's platform
pub platform: Platform,
/// If true, disable the free disk space check
@@ -80,6 +83,7 @@ impl Options {
let end_date: Option<&String> = args.get_one(OPTION_END_DATE);
let no_lazy = args.get_flag(OPTION_DISABLE_LAZY_LOADING);
let custom_name: Option<&String> = args.get_one(OPTION_CUSTOM_NAME);
+ let use_caller_id = args.get_flag(OPTION_USE_CALLER_ID);
let platform_type: Option<&String> = args.get_one(OPTION_PLATFORM);
let ignore_disk_space = args.get_flag(OPTION_BYPASS_FREE_SPACE_CHECK);
@@ -149,6 +153,13 @@ impl Options {
)));
}
+ // Ensure that there are no custom name conflicts
+ if custom_name.is_some() && use_caller_id {
+ return Err(RuntimeError::InvalidOptions(format!(
+ "`--{OPTION_CUSTOM_NAME}` is enabled; `--{OPTION_USE_CALLER_ID}` is disallowed"
+ )));
+ }
+
// Build query context
let mut query_context = QueryContext::default();
if let Some(start) = start_date {
@@ -217,6 +228,7 @@ impl Options {
query_context,
no_lazy,
custom_name: custom_name.cloned(),
+ use_caller_id,
platform,
ignore_disk_space,
})
@@ -369,16 +381,24 @@ fn get_command() -> Command {
Arg::new(OPTION_CUSTOM_NAME)
.short('m')
.long(OPTION_CUSTOM_NAME)
- .help("Specify an optional custom name for the database owner's messages in exports\n")
+ .help(format!("Specify an optional custom name for the database owner's messages in exports\nConflicts with --{OPTION_USE_CALLER_ID}\n"))
.display_order(10)
)
+ .arg(
+ Arg::new(OPTION_USE_CALLER_ID)
+ .short('i')
+ .long(OPTION_USE_CALLER_ID)
+ .help(format!("Use the database owner's caller ID in exports instead of \"Me\"\nConflicts with --{OPTION_CUSTOM_NAME}\n"))
+ .action(ArgAction::SetTrue)
+ .display_order(11)
+ )
.arg(
Arg::new(OPTION_BYPASS_FREE_SPACE_CHECK)
.short('b')
.long(OPTION_BYPASS_FREE_SPACE_CHECK)
.help("Bypass the disk space check when exporting data\nBy default, exports will not run if there is not enough free disk space\n")
.action(ArgAction::SetTrue)
- .display_order(11)
+ .display_order(12)
)
}
@@ -420,6 +440,7 @@ mod arg_tests {
query_context: QueryContext::default(),
no_lazy: false,
custom_name: None,
+ use_caller_id: false,
platform: Platform::default(),
ignore_disk_space: false,
};
@@ -514,6 +535,7 @@ mod arg_tests {
query_context: QueryContext::default(),
no_lazy: false,
custom_name: None,
+ use_caller_id: false,
platform: Platform::default(),
ignore_disk_space: false,
};
@@ -542,6 +564,7 @@ mod arg_tests {
query_context: QueryContext::default(),
no_lazy: true,
custom_name: None,
+ use_caller_id: false,
platform: Platform::default(),
ignore_disk_space: false,
};
diff --git a/imessage-exporter/src/app/runtime.rs b/imessage-exporter/src/app/runtime.rs
index dd689920..b688a4cd 100644
--- a/imessage-exporter/src/app/runtime.rs
+++ b/imessage-exporter/src/app/runtime.rs
@@ -154,7 +154,7 @@ impl Config {
let mut added = 0;
let mut out_s = String::with_capacity(MAX_LENGTH);
for participant_id in participants {
- let participant = self.who(Some(*participant_id), false);
+ let participant = self.who(Some(*participant_id), false, &None);
if participant.len() + out_s.len() < MAX_LENGTH {
if !out_s.is_empty() {
out_s.push_str(", ");
@@ -339,8 +339,19 @@ impl Config {
}
/// Determine who sent a message
- pub fn who(&self, handle_id: Option, is_from_me: bool) -> &str {
+ pub fn who<'a, 'b>(
+ &'a self,
+ handle_id: Option,
+ is_from_me: bool,
+ destination_caller_id: &'b Option,
+ ) -> &'a str {
if is_from_me {
+ // TODO: Support destination_caller_id
+ if let Some(caller_id) = destination_caller_id {
+ if self.options.use_caller_id {
+ return caller_id;
+ }
+ }
return self.options.custom_name.as_deref().unwrap_or(ME);
} else if let Some(handle_id) = handle_id {
return match self.participants.get(&handle_id) {
@@ -378,6 +389,7 @@ mod filename_tests {
query_context: QueryContext::default(),
no_lazy: false,
custom_name: None,
+ use_caller_id: false,
platform: Platform::macOS,
ignore_disk_space: false,
}
@@ -608,6 +620,7 @@ mod who_tests {
query_context: QueryContext::default(),
no_lazy: false,
custom_name: None,
+ use_caller_id: false,
platform: Platform::macOS,
ignore_disk_space: false,
}
@@ -645,6 +658,7 @@ mod who_tests {
text: None,
service: Some("iMessage".to_string()),
handle_id: Some(i32::default()),
+ destination_caller_id: None,
subject: None,
date: i64::default(),
date_read: i64::default(),
@@ -677,7 +691,7 @@ mod who_tests {
app.participants.insert(10, "Person 10".to_string());
// Get participant name
- let who = app.who(Some(10), false);
+ let who = app.who(Some(10), false, &None);
assert_eq!(who, "Person 10".to_string());
}
@@ -687,7 +701,7 @@ mod who_tests {
let app = fake_app(options);
// Get participant name
- let who = app.who(Some(10), false);
+ let who = app.who(Some(10), false, &None);
assert_eq!(who, "Unknown".to_string());
}
@@ -697,7 +711,7 @@ mod who_tests {
let app = fake_app(options);
// Get participant name
- let who = app.who(Some(0), true);
+ let who = app.who(Some(0), true, &None);
assert_eq!(who, "Me".to_string());
}
@@ -708,7 +722,7 @@ mod who_tests {
let app = fake_app(options);
// Get participant name
- let who = app.who(Some(0), true);
+ let who = app.who(Some(0), true, &None);
assert_eq!(who, "Name".to_string());
}
@@ -718,7 +732,7 @@ mod who_tests {
let app = fake_app(options);
// Get participant name
- let who = app.who(None, true);
+ let who = app.who(None, true, &None);
assert_eq!(who, "Me".to_string());
}
@@ -728,7 +742,7 @@ mod who_tests {
let app = fake_app(options);
// Get participant name
- let who = app.who(None, false);
+ let who = app.who(None, false, &None);
assert_eq!(who, "Unknown".to_string());
}
@@ -831,6 +845,7 @@ mod directory_tests {
query_context: QueryContext::default(),
no_lazy: false,
custom_name: None,
+ use_caller_id: false,
platform: Platform::macOS,
ignore_disk_space: false,
}
diff --git a/imessage-exporter/src/exporters/html.rs b/imessage-exporter/src/exporters/html.rs
index b79676ba..31b3e3cf 100644
--- a/imessage-exporter/src/exporters/html.rs
+++ b/imessage-exporter/src/exporters/html.rs
@@ -217,7 +217,11 @@ impl<'a> Writer<'a> for HTML<'a> {
// Add message sender
self.add_line(
&mut formatted_message,
- self.config.who(message.handle_id, message.is_from_me),
+ self.config.who(
+ message.handle_id,
+ message.is_from_me,
+ &message.destination_caller_id,
+ ),
"",
"
",
);
@@ -620,12 +624,15 @@ impl<'a> Writer<'a> for HTML<'a> {
Ok(format!(
"{:?} by {}",
reaction,
- self.config.who(msg.handle_id, msg.is_from_me),
+ self.config
+ .who(msg.handle_id, msg.is_from_me, &msg.destination_caller_id),
))
}
Variant::Sticker(_) => {
let mut paths = Attachment::from_message(&self.config.db, msg)?;
- let who = self.config.who(msg.handle_id, msg.is_from_me);
+ let who =
+ self.config
+ .who(msg.handle_id, msg.is_from_me, &msg.destination_caller_id);
// Sticker messages have only one attachment, the sticker image
Ok(match paths.get_mut(0) {
Some(sticker) => self.format_sticker(sticker, msg),
@@ -663,7 +670,9 @@ impl<'a> Writer<'a> for HTML<'a> {
}
fn format_announcement(&self, msg: &'a Message) -> String {
- let mut who = self.config.who(msg.handle_id, msg.is_from_me);
+ let mut who = self
+ .config
+ .who(msg.handle_id, msg.is_from_me, &msg.destination_caller_id);
// Rename yourself so we render the proper grammar here
if who == ME {
who = self.config.options.custom_name.as_deref().unwrap_or("You");
@@ -1392,6 +1401,7 @@ mod tests {
text: None,
service: Some("iMessage".to_string()),
handle_id: Some(i32::default()),
+ destination_caller_id: None,
subject: None,
date: i64::default(),
date_read: i64::default(),
@@ -1426,6 +1436,7 @@ mod tests {
query_context: QueryContext::default(),
no_lazy: false,
custom_name: None,
+ use_caller_id: false,
platform: Platform::macOS,
ignore_disk_space: false,
}
diff --git a/imessage-exporter/src/exporters/txt.rs b/imessage-exporter/src/exporters/txt.rs
index 7a377fcc..0f1fc804 100644
--- a/imessage-exporter/src/exporters/txt.rs
+++ b/imessage-exporter/src/exporters/txt.rs
@@ -141,7 +141,11 @@ impl<'a> Writer<'a> for TXT<'a> {
// Add message sender
self.add_line(
&mut formatted_message,
- self.config.who(message.handle_id, message.is_from_me),
+ self.config.who(
+ message.handle_id,
+ message.is_from_me,
+ &message.destination_caller_id,
+ ),
&indent,
);
@@ -322,7 +326,11 @@ impl<'a> Writer<'a> for TXT<'a> {
}
fn format_sticker(&self, sticker: &'a mut Attachment, message: &Message) -> String {
- let who = self.config.who(message.handle_id, message.is_from_me);
+ let who = self.config.who(
+ message.handle_id,
+ message.is_from_me,
+ &message.destination_caller_id,
+ );
match self.format_attachment(sticker, message) {
Ok(path_to_sticker) => {
let sticker_effect = sticker.get_sticker_effect(
@@ -414,12 +422,15 @@ impl<'a> Writer<'a> for TXT<'a> {
Ok(format!(
"{:?} by {}",
reaction,
- self.config.who(msg.handle_id, msg.is_from_me),
+ self.config
+ .who(msg.handle_id, msg.is_from_me, &msg.destination_caller_id),
))
}
Variant::Sticker(_) => {
let mut paths = Attachment::from_message(&self.config.db, msg)?;
- let who = self.config.who(msg.handle_id, msg.is_from_me);
+ let who =
+ self.config
+ .who(msg.handle_id, msg.is_from_me, &msg.destination_caller_id);
// Sticker messages have only one attachment, the sticker image
Ok(if let Some(sticker) = paths.get_mut(0) {
self.format_sticker(sticker, msg)
@@ -456,7 +467,9 @@ impl<'a> Writer<'a> for TXT<'a> {
}
fn format_announcement(&self, msg: &'a Message) -> String {
- let mut who = self.config.who(msg.handle_id, msg.is_from_me);
+ let mut who = self
+ .config
+ .who(msg.handle_id, msg.is_from_me, &msg.destination_caller_id);
// Rename yourself so we render the proper grammar here
if who == ME {
who = self.config.options.custom_name.as_deref().unwrap_or(YOU);
@@ -898,6 +911,7 @@ mod tests {
text: None,
service: Some("iMessage".to_string()),
handle_id: Some(i32::default()),
+ destination_caller_id: None,
subject: None,
date: i64::default(),
date_read: i64::default(),
@@ -932,6 +946,7 @@ mod tests {
query_context: QueryContext::default(),
no_lazy: false,
custom_name: None,
+ use_caller_id: false,
platform: Platform::macOS,
ignore_disk_space: false,
}
From e508b750d1ef46cce95eb5e287bf6ceb088c06b2 Mon Sep 17 00:00:00 2001
From: Christopher Sardegna
Date: Mon, 6 May 2024 09:02:53 -0700
Subject: [PATCH 3/8] Use standard PS1 char for #244
---
imessage-exporter/README.md | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/imessage-exporter/README.md b/imessage-exporter/README.md
index 00db167d..f50c512f 100644
--- a/imessage-exporter/README.md
+++ b/imessage-exporter/README.md
@@ -12,7 +12,7 @@ This binary is available on [crates.io](https://crates.io/crates/imessage-export
`cargo install imessage-exporter` is the best way to install the app for normal use.
-Uninstall steps
% cargo uninstall imessage-exporter
+Uninstall steps
$ cargo uninstall imessage-exporter
### Homebrew
@@ -20,13 +20,13 @@ This binary is available via [`brew`](https://formulae.brew.sh/formula/imessage-
`brew install imessage-exporter` will install the app, but it may not be up to date with the latest release.
-Uninstall steps
% brew uninstall imessage-exporter
+Uninstall steps
$ brew uninstall imessage-exporter
### Prebuilt Binaries
The [releases page](https://github.com/ReagentX/imessage-exporter/releases) provides prebuilt binaries for both Apple Silicon and Intel-based Macs.
-Uninstall steps
% rm path/to/imessage-exporter-binary
+Uninstall steps
$ rm path/to/imessage-exporter-binary
### Installing manually
@@ -98,37 +98,37 @@ The [releases page](https://github.com/ReagentX/imessage-exporter/releases) prov
Export as `html` and copy attachments in web-compatible formats from the default iMessage Database location to your home directory:
```zsh
-% imessage-exporter -f html -c compatible
+$ imessage-exporter -f html -c compatible
```
Export as `txt` and copy attachments in their original formats from the default iMessage Database location to a new folder in the current working directory called `output`:
```zsh
-% imessage-exporter -f txt -o output -c efficient
+$ imessage-exporter -f txt -o output -c efficient
```
Export as `txt` from the an unencrypted iPhone backup located at `~/iphone_backup_latest` to a new folder in the current working directory called `backup_export`:
```zsh
-% imessage-exporter -f txt -p ~/iphone_backup_latest -a iOS -o backup_export
+$ imessage-exporter -f txt -p ~/iphone_backup_latest -a iOS -o backup_export
```
Export as `html` from `/Volumes/external/chat.db` to `/Volumes/external/export` without copying attachments:
```zsh
-% imessage-exporter -f html -c disabled -p /Volumes/external/chat.db -o /Volumes/external/export
+$ imessage-exporter -f html -c disabled -p /Volumes/external/chat.db -o /Volumes/external/export
```
Export as `html` from `/Volumes/external/chat.db` to `/Volumes/external/export` with attachments in `/Volumes/external/Attachments`:
```zsh
-% imessage-exporter -f html -c efficient -p /Volumes/external/chat.db -r /Volumes/external/Attachments -o /Volumes/external/export
+$ imessage-exporter -f html -c efficient -p /Volumes/external/chat.db -r /Volumes/external/Attachments -o /Volumes/external/export
```
Export messages from `2020-01-01` to `2020-12-31` as `txt` from the default macOS iMessage Database location to `~/export-2020`:
```zsh
-% imessage-exporter -f txt -o ~/export-2020 -s 2020-01-01 -e 2021-01-01 -a macOS
+$ imessage-exporter -f txt -o ~/export-2020 -s 2020-01-01 -e 2021-01-01 -a macOS
```
## Features
From 92a2c2ce9a4cc24aae5efc8c5b0f54ffd17f6475 Mon Sep 17 00:00:00 2001
From: Christopher Sardegna
Date: Mon, 6 May 2024 09:17:32 -0700
Subject: [PATCH 4/8] Fix lifetime
---
imessage-exporter/src/app/runtime.rs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/imessage-exporter/src/app/runtime.rs b/imessage-exporter/src/app/runtime.rs
index b688a4cd..5e02ef7c 100644
--- a/imessage-exporter/src/app/runtime.rs
+++ b/imessage-exporter/src/app/runtime.rs
@@ -339,14 +339,13 @@ impl Config {
}
/// Determine who sent a message
- pub fn who<'a, 'b>(
+ pub fn who<'a, 'b: 'a>(
&'a self,
handle_id: Option,
is_from_me: bool,
destination_caller_id: &'b Option,
) -> &'a str {
if is_from_me {
- // TODO: Support destination_caller_id
if let Some(caller_id) = destination_caller_id {
if self.options.use_caller_id {
return caller_id;
From 6139af34c44de5c0aeb99e8ba909aaf82a6b63e8 Mon Sep 17 00:00:00 2001
From: Christopher Sardegna
Date: Mon, 6 May 2024 09:21:59 -0700
Subject: [PATCH 5/8] Add caller ID tests
---
imessage-exporter/src/app/runtime.rs | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/imessage-exporter/src/app/runtime.rs b/imessage-exporter/src/app/runtime.rs
index 5e02ef7c..5b0507d7 100644
--- a/imessage-exporter/src/app/runtime.rs
+++ b/imessage-exporter/src/app/runtime.rs
@@ -714,6 +714,18 @@ mod who_tests {
assert_eq!(who, "Me".to_string());
}
+ #[test]
+ fn can_get_who_me_caller_id() {
+ let mut options = fake_options();
+ options.use_caller_id = true;
+ let app = fake_app(options);
+
+ // Get participant name
+ let caller_id = Some("test".to_string());
+ let who = app.who(Some(0), true, &caller_id);
+ assert_eq!(who, "test".to_string());
+ }
+
#[test]
fn can_get_who_me_custom() {
let mut options = fake_options();
@@ -735,6 +747,18 @@ mod who_tests {
assert_eq!(who, "Me".to_string());
}
+ #[test]
+ fn can_get_who_me_none_caller_id() {
+ let mut options = fake_options();
+ options.use_caller_id = true;
+ let app = fake_app(options);
+
+ // Get participant name
+ let caller_id = Some("test".to_string());
+ let who = app.who(None, true, &caller_id);
+ assert_eq!(who, "test".to_string());
+ }
+
#[test]
fn can_get_who_none_them() {
let options = fake_options();
From f4f18a95f9cc94b395575fa10db5ced98ea22f3b Mon Sep 17 00:00:00 2001
From: Christopher Sardegna
Date: Tue, 7 May 2024 11:04:09 -0700
Subject: [PATCH 6/8] Add tests
---
imessage-exporter/src/app/options.rs | 107 +++++++++++++++++++++++++++
1 file changed, 107 insertions(+)
diff --git a/imessage-exporter/src/app/options.rs b/imessage-exporter/src/app/options.rs
index 694b9c6c..d484ac4c 100644
--- a/imessage-exporter/src/app/options.rs
+++ b/imessage-exporter/src/app/options.rs
@@ -118,6 +118,11 @@ impl Options {
"Option {OPTION_END_DATE} is enabled, which requires `--{OPTION_EXPORT_TYPE}`"
)));
}
+ if use_caller_id && export_file_type.is_none() {
+ return Err(RuntimeError::InvalidOptions(format!(
+ "Option {OPTION_USE_CALLER_ID} is enabled, which requires `--{OPTION_EXPORT_TYPE}`"
+ )));
+ }
// Warn the user if they are exporting to a file type for which lazy loading has no effect
if no_lazy && export_file_type != Some(&"html".to_string()) {
@@ -152,6 +157,11 @@ impl Options {
"Diagnostics are enabled; {OPTION_END_DATE} is disallowed"
)));
}
+ if diagnostic && use_caller_id {
+ return Err(RuntimeError::InvalidOptions(format!(
+ "Diagnostics are enabled; {OPTION_USE_CALLER_ID} is disallowed"
+ )));
+ }
// Ensure that there are no custom name conflicts
if custom_name.is_some() && use_caller_id {
@@ -513,6 +523,19 @@ mod arg_tests {
assert!(actual.is_err());
}
+ #[test]
+ fn cant_build_option_diagnostic_flag_with_caller_id() {
+ // Get matches from sample args
+ let cli_args: Vec<&str> = vec!["imessage-exporter", "-d", "-i"];
+ let command = get_command();
+ let args = command.get_matches_from(cli_args);
+
+ // Build the Options
+ let actual = Options::from_args(&args);
+
+ assert!(actual.is_err());
+ }
+
#[test]
fn can_build_option_export_html() {
// Get matches from sample args
@@ -662,6 +685,90 @@ mod arg_tests {
assert!(actual.is_err());
}
+
+ #[test]
+ fn can_build_option_custom_name() {
+ // Get matches from sample args
+ let cli_args: Vec<&str> = vec!["imessage-exporter", "-f", "txt", "-m", "Name"];
+ let command = get_command();
+ let args = command.get_matches_from(cli_args);
+
+ // Build the Options
+ let actual = Options::from_args(&args).unwrap();
+
+ // Expected data
+ let expected = Options {
+ db_path: default_db_path(),
+ attachment_root: None,
+ attachment_manager: AttachmentManager::default(),
+ diagnostic: false,
+ export_type: Some(ExportType::Txt),
+ export_path: validate_path(None, &None).unwrap(),
+ query_context: QueryContext::default(),
+ no_lazy: false,
+ custom_name: Some("Name".to_string()),
+ use_caller_id: false,
+ platform: Platform::default(),
+ ignore_disk_space: false,
+ };
+
+ assert_eq!(actual, expected);
+ }
+
+ #[test]
+ fn can_build_option_caller_id() {
+ // Get matches from sample args
+ let cli_args: Vec<&str> = vec!["imessage-exporter", "-f", "txt", "-i"];
+ let command = get_command();
+ let args = command.get_matches_from(cli_args);
+
+ // Build the Options
+ let actual = Options::from_args(&args).unwrap();
+
+ // Expected data
+ let expected = Options {
+ db_path: default_db_path(),
+ attachment_root: None,
+ attachment_manager: AttachmentManager::default(),
+ diagnostic: false,
+ export_type: Some(ExportType::Txt),
+ export_path: validate_path(None, &None).unwrap(),
+ query_context: QueryContext::default(),
+ no_lazy: false,
+ custom_name: None,
+ use_caller_id: true,
+ platform: Platform::default(),
+ ignore_disk_space: false,
+ };
+
+ assert_eq!(actual, expected);
+ }
+
+ #[test]
+ fn cant_build_option_custom_name_and_caller_id() {
+ // Get matches from sample args
+ let cli_args: Vec<&str> = vec!["imessage-exporter", "-f", "txt", "-m", "Name", "-i"];
+ let command = get_command();
+ let args = command.get_matches_from(cli_args);
+
+ // Build the Options
+ let actual = Options::from_args(&args);
+
+ assert!(actual.is_err());
+ }
+
+ #[test]
+ fn cant_build_option_caller_id_no_export() {
+ // Get matches from sample args
+ let cli_args: Vec<&str> = vec!["imessage-exporter", "-f", "txt", "-m", "Name", "-i"];
+ let command = get_command();
+ let args = command.get_matches_from(cli_args);
+
+ // Build the Options
+ let actual = Options::from_args(&args);
+
+ assert!(actual.is_err());
+ }
}
#[cfg(test)]
From 685ec6f4ad3783946c98eab93dbeff1b631732e5 Mon Sep 17 00:00:00 2001
From: Christopher Sardegna
Date: Tue, 7 May 2024 20:50:35 -0700
Subject: [PATCH 7/8] Fix typo
---
imessage-exporter/src/app/options.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imessage-exporter/src/app/options.rs b/imessage-exporter/src/app/options.rs
index d484ac4c..e75b9f54 100644
--- a/imessage-exporter/src/app/options.rs
+++ b/imessage-exporter/src/app/options.rs
@@ -63,7 +63,7 @@ pub struct Options {
pub no_lazy: bool,
/// Custom name for database owner in output
pub custom_name: Option,
- /// If true, use the database owners caller ID instead of "Me"
+ /// If true, use the database owner's caller ID instead of "Me"
pub use_caller_id: bool,
/// The database source's platform
pub platform: Platform,
From a7e8effe860e3aa3a6527f665433105a33d3cd2c Mon Sep 17 00:00:00 2001
From: Christopher Sardegna
Date: Tue, 7 May 2024 21:09:18 -0700
Subject: [PATCH 8/8] Simplify logic
---
imessage-exporter/src/app/runtime.rs | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/imessage-exporter/src/app/runtime.rs b/imessage-exporter/src/app/runtime.rs
index 5b0507d7..a2d0e425 100644
--- a/imessage-exporter/src/app/runtime.rs
+++ b/imessage-exporter/src/app/runtime.rs
@@ -346,10 +346,8 @@ impl Config {
destination_caller_id: &'b Option,
) -> &'a str {
if is_from_me {
- if let Some(caller_id) = destination_caller_id {
- if self.options.use_caller_id {
- return caller_id;
- }
+ if self.options.use_caller_id {
+ return destination_caller_id.as_deref().unwrap_or(ME);
}
return self.options.custom_name.as_deref().unwrap_or(ME);
} else if let Some(handle_id) = handle_id {