Skip to main content

imessage_database/util/
bundle_id.rs

1/*!
2 [Apple Bundle ID](https://developer.apple.com/documentation/appstoreconnectapi/bundle-ids) helpers for Messages app balloons.
3*/
4
5/// Parse the app bundle ID from a balloon bundle ID.
6///
7/// For example, a balloon bundle ID like `com.apple.messages.MSMessageExtensionBalloonPlugin:0000000000:com.apple.SafetyMonitorApp.SafetyMonitorMessages`
8/// should get parsed into `com.apple.SafetyMonitorApp.SafetyMonitorMessages`.
9///
10/// # Example
11///
12/// ```
13/// use imessage_database::util::bundle_id::parse_balloon_bundle_id;
14///
15/// let bundle_id = "com.apple.messages.MSMessageExtensionBalloonPlugin:0000000000:com.apple.SafetyMonitorApp.SafetyMonitorMessages";
16/// let parsed = parse_balloon_bundle_id(Some(bundle_id)); // Some("com.apple.SafetyMonitorApp.SafetyMonitorMessages")
17/// ```
18#[must_use]
19pub fn parse_balloon_bundle_id(bundle_id: Option<&str>) -> Option<&str> {
20    bundle_id.and_then(|id| {
21        let mut parts = id.split(':');
22        let first = parts.next();
23
24        // If there is only one part, use that, otherwise get the third part
25        match parts.next() {
26            None => first,
27            // Will be None if there is no third part
28            Some(_) => parts.next(),
29        }
30    })
31}
32
33#[cfg(test)]
34mod tests {
35    use crate::util::bundle_id::parse_balloon_bundle_id;
36
37    #[test]
38    fn can_get_no_balloon_bundle_id() {
39        assert_eq!(parse_balloon_bundle_id(None), None);
40    }
41
42    #[test]
43    fn can_get_balloon_bundle_id() {
44        let cases = [
45            (
46                "os provider without plugin prefix",
47                "com.apple.Handwriting.HandwritingProvider",
48                "com.apple.Handwriting.HandwritingProvider",
49            ),
50            (
51                "url provider without plugin prefix",
52                "com.apple.messages.URLBalloonProvider",
53                "com.apple.messages.URLBalloonProvider",
54            ),
55            (
56                "apple plugin with placeholder team id",
57                "com.apple.messages.MSMessageExtensionBalloonPlugin:0000000000:com.apple.PassbookUIService.PeerPaymentMessagesExtension",
58                "com.apple.PassbookUIService.PeerPaymentMessagesExtension",
59            ),
60            (
61                "third-party plugin with team id",
62                "com.apple.messages.MSMessageExtensionBalloonPlugin:QPU8QS3E62:com.contextoptional.OpenTable.Messages",
63                "com.contextoptional.OpenTable.Messages",
64            ),
65        ];
66        for (label, input, expected) in cases {
67            assert_eq!(
68                parse_balloon_bundle_id(Some(input)),
69                Some(expected),
70                "{label}"
71            );
72        }
73    }
74}