1#![allow(clippy::not_unsafe_ptr_arg_deref)]
2use error::PlistError;
3#[doc = include_str!("../README.md")]
4use log::{trace, warn};
5use rand::Rng;
6use std::{ffi::CString, fmt::Formatter, os::raw::c_char};
7
8pub mod error;
9mod iterator;
10mod types;
11mod unsafe_bindings;
12
13pub struct Plist {
16 pub(crate) plist_t: unsafe_bindings::plist_t,
17 pub plist_type: PlistType,
18 pub(crate) id: u32,
19 pub(crate) false_drop: bool,
20}
21
22unsafe impl Send for Plist {}
23unsafe impl Sync for Plist {}
24
25#[derive(PartialEq, Eq, Debug, Clone)]
27pub enum PlistType {
28 Boolean,
29 Integer,
30 Real,
31 Date,
32 Data,
33 String,
34 Array,
35 Dictionary,
36 Unknown,
37 Key,
38 Uid,
39 None,
40}
41
42impl Plist {
43 pub fn get_pointer(&self) -> *mut std::ffi::c_void {
46 self.plist_t
47 }
48 pub fn from_xml(xml: String) -> Result<Plist, PlistError> {
50 let xml = match CString::new(xml) {
51 Ok(s) => s,
52 Err(_) => {
53 warn!("Could not convert string to CString");
54 return Err(PlistError::InvalidArg);
55 }
56 };
57 let xml_len = std::convert::TryInto::try_into(xml.as_bytes().len()).unwrap();
58 let mut plist_t = unsafe { std::mem::zeroed() };
59 trace!("Parsing xml");
60 unsafe {
61 unsafe_bindings::plist_from_xml(xml.as_ptr() as *const c_char, xml_len, &mut plist_t)
62 };
63 Ok(plist_t.into())
64 }
65 pub fn from_bin(bin: Vec<u8>) -> Result<Plist, PlistError> {
67 let mut plist_t = unsafe { std::mem::zeroed() };
68 let result = unsafe {
69 unsafe_bindings::plist_from_bin(
70 bin.as_ptr() as *const c_char,
71 bin.len() as u32,
72 &mut plist_t,
73 )
74 };
75 if result != 0 {
76 return Err(result.into());
77 }
78 Ok(plist_t.into())
79 }
80 pub fn from_memory(bin: Vec<u8>) -> Result<Plist, PlistError> {
81 let mut plist_t = unsafe { std::mem::zeroed() };
82 let result = unsafe {
83 unsafe_bindings::plist_from_memory(
84 bin.as_ptr() as *const c_char,
85 bin.len() as u32,
86 &mut plist_t,
87 std::ptr::null_mut(),
88 )
89 };
90 if result != 0 {
91 return Err(result.into());
92 }
93 Ok(plist_t.into())
94 }
95 pub unsafe fn get_parent(self) -> Plist {
101 trace!("Getting parent");
102 unsafe_bindings::plist_get_parent(self.plist_t).into()
103 }
104 pub fn get_node_type(&self) -> PlistType {
106 trace!("Getting node type");
107 unsafe { unsafe_bindings::plist_get_node_type(self.plist_t) }.into() }
109 pub fn is_binary(&self) -> bool {
111 let plist_data = unsafe { std::mem::zeroed() };
112 let plist_len = unsafe { std::mem::zeroed() };
113 trace!("Getting plist data");
114 unsafe {
115 unsafe_bindings::plist_get_data_val(self.plist_t, plist_data, plist_len);
116 }
117 trace!("Checking if plist is binary");
118 !matches!(
119 unsafe {
120 unsafe_bindings::plist_is_binary(*plist_data, (*plist_len).try_into().unwrap())
121 },
122 0
123 )
124 }
125 pub fn access_path(self, plists: Vec<String>) -> Result<Plist, PlistError> {
128 let mut current = self;
129 let mut i = 0;
130 while i < plists.len() {
131 match current.plist_type {
132 PlistType::Array => {
133 current = match current.array_get_item(i as u32) {
134 Ok(item) => item,
135 Err(_) => return Err(PlistError::InvalidArg),
136 };
137 }
138 PlistType::Dictionary => {
139 current = match current.dict_get_item(&plists[i]) {
140 Ok(item) => item,
141 Err(_) => return Err(PlistError::InvalidArg),
142 };
143 }
144 _ => {
145 return Err(PlistError::InvalidArg);
146 }
147 }
148 i += 1;
149 }
150 Ok(current.plist_t.into())
151 }
152
153 pub fn false_drop(mut self) {
159 self.false_drop = true;
160 trace!("False dropping {}", self.id);
161 }
162
163 pub fn compare_node_values(node_l: Plist, node_r: Plist) -> bool {
165 trace!("Comparing node values");
166 matches!(
167 unsafe { unsafe_bindings::plist_compare_node_value(node_l.plist_t, node_r.plist_t) }
168 .to_string()
169 .as_str(),
170 "TRUE"
171 )
172 }
173
174 pub fn get_display_value(&self) -> Result<String, PlistError> {
175 let mut to_return;
176 match self.plist_type {
177 PlistType::Boolean => {
178 to_return = self.get_bool_val()?.to_string();
179 }
180 PlistType::Integer => {
181 to_return = self.get_uint_val()?.to_string();
182 }
183 PlistType::Real => {
184 to_return = self.get_real_val()?.to_string();
185 }
186 PlistType::Data => {
187 to_return = format!("{:?}", self.get_data_val()?);
188 }
189 PlistType::Date => {
190 todo!();
191 }
192 PlistType::String => {
193 to_return = format!("\"{}\"", self.get_string_val()?);
194 }
195 PlistType::Array => {
196 to_return = "[".to_string();
197 for item in self.clone() {
198 println!("Item go!");
199 to_return = format!("{}{}", to_return, item.plist.get_display_value()?);
200 }
201 to_return = format!("{}]", to_return);
202 }
203 PlistType::Dictionary => {
204 to_return = "{ ".to_string();
205 for line in self.clone() {
206 to_return = format!(
207 "{}{}: {}, ",
208 to_return,
209 line.key.unwrap(),
210 line.plist.get_display_value()?
211 );
212 }
213 to_return = format!(
215 "{} }}",
216 to_return
217 .chars()
218 .take(to_return.len() - 2)
219 .collect::<String>()
220 );
221 }
222 PlistType::Uid => {
223 todo!();
224 }
225 PlistType::Key => {
226 todo!();
227 }
228 PlistType::Unknown => {
229 to_return = "Unknown".to_string();
230 }
231 PlistType::None => {
232 to_return = "None".to_string();
233 }
234 }
235
236 Ok(to_return)
237 }
238}
239
240impl From<unsafe_bindings::plist_t> for Plist {
241 fn from(plist_t: unsafe_bindings::plist_t) -> Self {
242 let mut rng = rand::thread_rng();
243 let id = rng.gen::<u32>();
244 trace!("Creating plist from plist_t with id {}", id);
245 Plist {
246 plist_t,
247 plist_type: unsafe { unsafe_bindings::plist_get_node_type(plist_t) }.into(),
248 id,
249 false_drop: false,
250 }
251 }
252}
253
254impl From<Plist> for String {
255 fn from(plist: Plist) -> Self {
256 let plist_t = plist.plist_t;
257 let mut plist_data = std::ptr::null_mut();
258 let mut plist_size = 0;
259 trace!("Converting plist to XML data");
260 unsafe {
261 unsafe_bindings::plist_to_xml(plist_t, &mut plist_data, &mut plist_size);
262 }
263 trace!("Assembling XML data");
264 let plist_data = unsafe {
265 std::slice::from_raw_parts(plist_data as *const u8, plist_size.try_into().unwrap())
266 };
267 let plist_data = std::str::from_utf8(plist_data).unwrap();
268
269 String::from(plist_data)
270 }
271}
272
273impl ToString for Plist {
274 fn to_string(&self) -> String {
275 let mut plist_data = std::ptr::null_mut();
276 let mut plist_size = 0;
277 trace!("Converting plist to XML data");
278 unsafe {
279 unsafe_bindings::plist_to_xml(self.plist_t, &mut plist_data, &mut plist_size);
280 }
281 trace!("Assembling XML data");
282
283 let plist_data =
284 unsafe { std::slice::from_raw_parts(plist_data as *mut u8, plist_size as usize) };
285 let plist_data = std::str::from_utf8(plist_data).unwrap();
286
287 plist_data.to_string()
288 }
289}
290
291impl From<Plist> for Vec<u8> {
292 fn from(plist: Plist) -> Self {
293 let plist_t = plist.plist_t;
294 let mut plist_data = std::ptr::null_mut();
295 let mut plist_size = 0;
296 trace!("Converting plist to binary data");
297 unsafe {
298 unsafe_bindings::plist_to_bin(plist_t, &mut plist_data, &mut plist_size);
299 }
300 trace!("Assembling binary data");
301 let plist_data = unsafe {
302 std::slice::from_raw_parts(plist_data as *const u8, plist_size.try_into().unwrap())
303 };
304
305 plist_data.to_vec()
306 }
307}
308
309impl Clone for Plist {
310 fn clone(&self) -> Self {
311 trace!("Cloning plist");
312 let plist_t = unsafe { unsafe_bindings::plist_copy(self.plist_t) };
313 trace!("Getting type of cloned plist");
314 plist_t.into()
315 }
316}
317
318impl std::fmt::Debug for Plist {
319 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
320 let plist_data = self.to_string();
321 write!(f, "{:?}: {}", self.plist_type, plist_data)
322 }
323}
324
325impl Drop for Plist {
326 fn drop(&mut self) {
327 if !self.false_drop {
328 trace!("Dropping plist {}", self.id);
329 unsafe { unsafe_bindings::plist_free(self.plist_t) }
330 trace!("Plist dropped");
331 }
332 }
333}
334
335impl From<i32> for PlistType {
336 fn from(i: i32) -> Self {
337 match i {
338 0 => PlistType::Boolean,
339 1 => PlistType::Integer,
340 2 => PlistType::Real,
341 3 => PlistType::String,
342 4 => PlistType::Array,
343 5 => PlistType::Dictionary,
344 6 => PlistType::Date,
345 7 => PlistType::Data,
346 8 => PlistType::Key,
347 9 => PlistType::Uid,
348 10 => PlistType::None,
349 _ => PlistType::Unknown,
350 }
351 }
352}
353
354impl From<u32> for PlistType {
355 fn from(i: u32) -> Self {
356 match i {
357 0 => PlistType::Boolean,
358 1 => PlistType::Integer,
359 2 => PlistType::Real,
360 3 => PlistType::String,
361 4 => PlistType::Array,
362 5 => PlistType::Dictionary,
363 6 => PlistType::Date,
364 7 => PlistType::Data,
365 8 => PlistType::Key,
366 9 => PlistType::Uid,
367 10 => PlistType::None,
368 _ => PlistType::Unknown,
369 }
370 }
371}
372
373impl From<PlistType> for String {
374 fn from(plist_type: PlistType) -> String {
375 match plist_type {
376 PlistType::Boolean => "Boolean".to_string(),
377 PlistType::Integer => "Integer".to_string(),
378 PlistType::Real => "Real".to_string(),
379 PlistType::Date => "Date".to_string(),
380 PlistType::Data => "Data".to_string(),
381 PlistType::String => "String".to_string(),
382 PlistType::Array => "Array".to_string(),
383 PlistType::Dictionary => "Dictionary".to_string(),
384 PlistType::Unknown => "Unknown".to_string(),
385 PlistType::Key => "Key".to_string(),
386 PlistType::Uid => "Uid".to_string(),
387 PlistType::None => "None".to_string(),
388 }
389 }
390}