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 #[cfg(feature = "plist")]
73 pub const PLIST_FILE_NAME: &str = "Info.plist";
74
75 #[cfg(feature = "plist")]
76 pub const PLIST_TEST_EXAMPLE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
77<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
78<plist version="1.0">
79<dict>
80 <key>CFBundlePackageType</key>
81 <string>APPL</string>
82 <key>LSApplicationCategoryType</key>
83 <string>public.app-category.business</string>
84 <key>CFBundleIdentifier</key>
85 <string>com.test.test-id</string>
86 <key>CFBundleName</key>
87 <string>Test</string>
88 <key>CFBundleVersion</key>
89 <string>1</string>
90 <key>CFBundleShortVersionString</key>
91 <string>1.0</string>
92 <key>CFBundleInfoDictionaryVersion</key>
93 <string>1.0</string>
94 <key>CFBundleDevelopmentRegion</key>
95 <string>en</string>
96 <key>UILaunchStoryboardName</key>
97 <string>LaunchScreen</string>
98 <key>UISupportedInterfaceOrientations</key>
99 <array>
100 <string>UIInterfaceOrientationPortrait</string>
101 <string>UIInterfaceOrientationPortraitUpsideDown</string>
102 <string>UIInterfaceOrientationLandscapeLeft</string>
103 <string>UIInterfaceOrientationLandscapeRight</string>
104 </array>
105 <key>UIRequiresFullScreen</key>
106 <false/>
107 <key>CFBundleExecutable</key>
108 <string>test</string>
109</dict>
110</plist>"#;
111
112 #[cfg(feature = "plist")]
113 #[test]
114 fn test_plist_equality() {
115 let dir = tempfile::tempdir().unwrap();
116 let properties = InfoPlist {
117 localization: Localization {
118 bundle_development_region: Some("en".to_owned()),
119 ..Default::default()
120 },
121 launch: Launch {
122 bundle_executable: Some("test".to_owned()),
123 ..Default::default()
124 },
125 identification: Identification {
126 bundle_identifier: "com.test.test-id".to_owned(),
127 ..Default::default()
128 },
129 bundle_version: BundleVersion {
130 bundle_version: Some("1".to_owned()),
131 bundle_info_dictionary_version: Some("1.0".to_owned()),
132 bundle_short_version_string: Some("1.0".to_owned()),
133 ..Default::default()
134 },
135 naming: Naming {
136 bundle_name: Some("Test".to_owned()),
137 ..Default::default()
138 },
139 categorization: Categorization {
140 bundle_package_type: Some("APPL".to_owned()),
141 application_category_type: Some(AppCategoryType::Business),
142 },
143 launch_interface: LaunchInterface {
144 launch_storyboard_name: Some("LaunchScreen".to_owned()),
145 ..Default::default()
146 },
147 styling: Styling {
148 requires_full_screen: Some(false),
149 ..Default::default()
150 },
151 orientation: Orientation {
152 supported_interface_orientations: Some(vec![
153 InterfaceOrientation::Portrait,
154 InterfaceOrientation::PortraitUpsideDown,
155 InterfaceOrientation::LandscapeLeft,
156 InterfaceOrientation::LandscapeRight,
157 ]),
158 ..Default::default()
159 },
160 ..Default::default()
161 };
162 let file_path = dir.path().join(PLIST_FILE_NAME);
164 let file = std::fs::File::create(file_path).unwrap();
165 plist::to_writer_xml(file, &properties).unwrap();
167 let file_path = dir.path().join(PLIST_FILE_NAME);
169 let result = std::fs::read_to_string(&file_path).unwrap();
170 assert_eq!(result, PLIST_TEST_EXAMPLE.replace(" ", "\t"));
171 let got_props: InfoPlist = plist::from_bytes(result.as_bytes()).unwrap();
173 assert_eq!(properties, got_props);
174 }
175
176 #[test]
177 fn default_dictionary_reexports_share_one_type() {
178 let dictionary = crate::info_plist::app_execution::DefaultDictionary {
179 default: "value".to_owned(),
180 };
181 let _: crate::info_plist::data_and_storage::DefaultDictionary = dictionary.clone();
182 let _: crate::info_plist::protected_resources::DefaultDictionary = dictionary;
183 }
184
185 #[test]
186 fn opengl_es_3_capability_uses_its_own_plist_value() {
187 assert_eq!(
188 serde_plain::to_string(&DeviceCapabilities::Opengles3).unwrap(),
189 "opengles-3"
190 );
191 }
192}