1#![allow(clippy::doc_lazy_continuation)]
2
3pub mod entitlements;
16pub mod info_plist;
18#[allow(ambiguous_glob_reexports)]
20pub mod prelude {
21 pub use super::entitlements::prelude::*;
22 pub use super::info_plist::prelude::*;
23 #[cfg(feature = "plist")]
24 pub use plist;
25}
26#[cfg(feature = "plist")]
27pub use plist::{
28 self, from_bytes, from_file, from_reader, from_reader_xml, to_file_binary, to_file_xml,
29 to_writer_binary, to_writer_xml,
30};
31
32use serde::{ser::SerializeSeq, Serialize, Serializer};
33
34fn serialize_enum_option<S: Serializer, T: Serialize>(
35 value: &Option<T>,
36 s: S,
37) -> Result<S::Ok, S::Error> {
38 s.serialize_str(&serde_plain::to_string(value).unwrap())
39}
40
41fn serialize_vec_enum_option<S: Serializer, T: Serialize>(
42 value: &Option<Vec<T>>,
43 s: S,
44) -> Result<S::Ok, S::Error> {
45 match value {
46 Some(ref val) => {
47 let mut seq = s.serialize_seq(Some(val.len()))?;
48 for element in val.iter() {
49 seq.serialize_element(&serde_plain::to_string(element).unwrap())?;
50 }
51 seq.end()
52 }
53 None => panic!("unsupported"),
54 }
55}
56
57fn serialize_option<S, T>(value: &Option<T>, ser: S) -> Result<S::Ok, S::Error>
58where
59 S: Serializer,
60 T: Serialize,
61{
62 value
63 .as_ref()
64 .expect(r#"`serialize_option` must be used with `skip_serializing_if = "Option::is_none"`"#)
65 .serialize(ser)
66}
67
68#[cfg(test)]
69mod tests {
70 use super::prelude::*;
71
72 pub const PLIST_FILE_NAME: &str = "Info.plist";
73
74 pub const PLIST_TEST_EXAMPLE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
75<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
76<plist version="1.0">
77<dict>
78 <key>CFBundlePackageType</key>
79 <string>APPL</string>
80 <key>LSApplicationCategoryType</key>
81 <string>public.app-category.business</string>
82 <key>CFBundleIdentifier</key>
83 <string>com.test.test-id</string>
84 <key>CFBundleName</key>
85 <string>Test</string>
86 <key>CFBundleVersion</key>
87 <string>1</string>
88 <key>CFBundleShortVersionString</key>
89 <string>1.0</string>
90 <key>CFBundleInfoDictionaryVersion</key>
91 <string>1.0</string>
92 <key>CFBundleDevelopmentRegion</key>
93 <string>en</string>
94 <key>UILaunchStoryboardName</key>
95 <string>LaunchScreen</string>
96 <key>UISupportedInterfaceOrientations</key>
97 <array>
98 <string>UIInterfaceOrientationPortrait</string>
99 <string>UIInterfaceOrientationPortraitUpsideDown</string>
100 <string>UIInterfaceOrientationLandscapeLeft</string>
101 <string>UIInterfaceOrientationLandscapeRight</string>
102 </array>
103 <key>UIRequiresFullScreen</key>
104 <false/>
105 <key>CFBundleExecutable</key>
106 <string>test</string>
107</dict>
108</plist>"#;
109
110 #[test]
111 fn test_plist_equality() {
112 let dir = tempfile::tempdir().unwrap();
113 let properties = InfoPlist {
114 localization: Localization {
115 bundle_development_region: Some("en".to_owned()),
116 ..Default::default()
117 },
118 launch: Launch {
119 bundle_executable: Some("test".to_owned()),
120 ..Default::default()
121 },
122 identification: Identification {
123 bundle_identifier: "com.test.test-id".to_owned(),
124 ..Default::default()
125 },
126 bundle_version: BundleVersion {
127 bundle_version: Some("1".to_owned()),
128 bundle_info_dictionary_version: Some("1.0".to_owned()),
129 bundle_short_version_string: Some("1.0".to_owned()),
130 ..Default::default()
131 },
132 naming: Naming {
133 bundle_name: Some("Test".to_owned()),
134 ..Default::default()
135 },
136 categorization: Categorization {
137 bundle_package_type: Some("APPL".to_owned()),
138 application_category_type: Some(AppCategoryType::Business),
139 },
140 launch_interface: LaunchInterface {
141 launch_storyboard_name: Some("LaunchScreen".to_owned()),
142 ..Default::default()
143 },
144 styling: Styling {
145 requires_full_screen: Some(false),
146 ..Default::default()
147 },
148 orientation: Orientation {
149 supported_interface_orientations: Some(vec![
150 InterfaceOrientation::Portrait,
151 InterfaceOrientation::PortraitUpsideDown,
152 InterfaceOrientation::LandscapeLeft,
153 InterfaceOrientation::LandscapeRight,
154 ]),
155 ..Default::default()
156 },
157 ..Default::default()
158 };
159 let file_path = dir.path().join(PLIST_FILE_NAME);
161 let file = std::fs::File::create(file_path).unwrap();
162 plist::to_writer_xml(file, &properties).unwrap();
164 let file_path = dir.path().join(PLIST_FILE_NAME);
166 let result = std::fs::read_to_string(&file_path).unwrap();
167 assert_eq!(result, PLIST_TEST_EXAMPLE.replace(" ", "\t"));
168 let got_props: InfoPlist = plist::from_bytes(result.as_bytes()).unwrap();
170 assert_eq!(properties, got_props);
171 }
172
173 #[test]
174 fn default_dictionary_reexports_share_one_type() {
175 let dictionary = crate::info_plist::app_execution::DefaultDictionary {
176 default: "value".to_owned(),
177 };
178 let _: crate::info_plist::data_and_storage::DefaultDictionary = dictionary.clone();
179 let _: crate::info_plist::protected_resources::DefaultDictionary = dictionary;
180 }
181
182 #[test]
183 fn opengl_es_3_capability_uses_its_own_plist_value() {
184 assert_eq!(
185 serde_plain::to_string(&DeviceCapabilities::Opengles3).unwrap(),
186 "opengles-3"
187 );
188 }
189}