Property lists
Apple property lists, XML and binary: Info.plist, profiles and preferences read as values, and values written back as either.
Version 1.0.0 · 5 functions · 2 crates
Functions
What use plist.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use plist_parse, plist_decode from plist (Quillo 0.6).
plist_parse(xml: str) → anyor an errorAn XML property list's text as a value: dictionaries as maps, <data> as base64 text, <date> as 2026-11-02T09:00:00Z.
plist_decode(base64_text: str) → anyor an errorA property list given as base64, binary (bplist00) or XML, as a value; as plist_parse gives it.
plist_read_file(path: str) → anyor an errorA .plist file, binary or XML, as a value.
plist_to_xml(value: any) → stror an errorvalue as an XML property list (with its <?xml and <!DOCTYPE plist lines), as Xcode and profile tools write it. Keys holding null are left out; ints are <integer>, floats <real>.
plist_to_binary(value: any) → stror an errorvalue as a binary property list (bplist00), in base64.
Get it
$ quillo install plist # plist.qwer into this folder$ quillo install plist --example # and its example, plist.qweOr with curl:
$ curl -O https://quillolang.org/plugins/files/plist.qwer$ curl -O https://quillolang.org/plugins/files/plist.qwe$ curl -O https://quillolang.org/plugins/files/plist_sample.plistThen, in your program, all of it or only what you call:
use plist.qweruse plist_parse, plist_decode from plist // Quillo 0.6: only theseIt uses these crates, which cargo fetches the first time the plugin is built:
plist = "1"base64 = "0.22"Example
// An app-release dashboard reading an iOS app's Info.plist, and a binary plist made and read back.//// quillo run plist.qwe then open http://localhost:8080/appuse "plist.qwer"route GET /app { let info = plist_read_file("plist_sample.plist")? { name: info.CFBundleDisplayName, bundle: info.CFBundleIdentifier, version: `${info.CFBundleShortVersionString} (${info.CFBundleVersion})`, built: info.BuildDate, icon_hash: info.IconHash, orientations: len(info.UISupportedInterfaceOrientations) }}route GET /binary { let packed = plist_to_binary({ server: "https://shop.example.com", retries: 3, timeout: 2.5, beta: true, unset: null })? { packed, binary: starts_with(packed, "YnBsaXN0"), back: plist_decode(packed)? }}route GET /profile.plist = respond(200, plist_to_xml({ PayloadDisplayName: "Quillo Shop Wi-Fi", PayloadType: "Configuration", PayloadVersion: 1, PayloadContent: [{ SSID_STR: "QuilloGuest", EncryptionType: "WPA2" }] })?, { "content-type": "application/xml" })$ quillo run plist.qwe$ curl 'localhost:8080/app'$ curl 'localhost:8080/binary'$ curl 'localhost:8080/profile.plist'Source
//! Apple property lists (.plist), XML and binary: Info.plist, .mobileconfig profiles, macOS preferences and iOS exports read as values, and values written back as either.//! version: 1.0.0//!//! [dependencies]//! plist = "1"//! base64 = "0.22"use base64::engine::general_purpose::STANDARD;use base64::Engine;use plist::Value as P;use serde_json::{Map, Value};/// Property lists up to 50 MB are read.const MAX: usize = 50 * 1024 * 1024;fn to_json(v: P) -> Value { match v { P::Array(a) => Value::Array(a.into_iter().map(to_json).collect()), P::Dictionary(d) => Value::Object(d.into_iter().map(|(k, v)| (k, to_json(v))).collect::<Map<_, _>>()), P::Boolean(b) => Value::Bool(b), // Data has no JSON form: it comes back as base64 text. P::Data(d) => Value::String(STANDARD.encode(d)), P::Date(d) => Value::String(d.to_xml_format()), P::Real(f) => serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null), P::Integer(i) => i.as_signed().map(Value::from).or_else(|| i.as_unsigned().map(Value::from)).unwrap_or(Value::Null), P::String(s) => Value::String(s), P::Uid(u) => Value::from(u.get()), _ => Value::Null, }}fn from_json(v: &Value) -> Option<P> { Some(match v { // A property list has no null: keys holding null are left out. Value::Null => return None, Value::Bool(b) => P::Boolean(*b), Value::Number(n) => match n.as_i64() { Some(i) => P::Integer(i.into()), None => P::Real(n.as_f64().unwrap_or(0.0)), }, Value::String(s) => P::String(s.clone()), Value::Array(a) => P::Array(a.iter().filter_map(from_json).collect()), Value::Object(m) => P::Dictionary(m.iter().filter_map(|(k, v)| from_json(v).map(|p| (k.clone(), p))).collect()), })}fn decode(bytes: &[u8]) -> Result<Value, String> { if bytes.len() > MAX { return Err("property lists up to 50 MB".into()); } P::from_reader(std::io::Cursor::new(bytes)).map(to_json).map_err(|e| format!("not a property list: {}", e))}/// An XML property list's text as a value: dictionaries as maps, `<data>` as base64 text, `<date>` as `2026-11-02T09:00:00Z`.pub fn plist_parse(xml: &str) -> Result<Value, String> { decode(xml.as_bytes())}/// A property list given as base64, binary (`bplist00`) or XML, as a value; as plist_parse gives it.pub fn plist_decode(base64_text: &str) -> Result<Value, String> { decode(&STANDARD.decode(base64_text.trim()).map_err(|_| "not base64".to_string())?)}/// A .plist file, binary or XML, as a value.pub fn plist_read_file(path: &str) -> Result<Value, String> { let size = std::fs::metadata(path).map_err(|e| format!("{}: {}", path, e))?.len(); if size as usize > MAX { return Err(format!("{}: property lists up to 50 MB", path)); } decode(&std::fs::read(path).map_err(|e| format!("{}: {}", path, e))?)}/// `value` as an XML property list (with its `<?xml` and `<!DOCTYPE plist` lines), as Xcode and profile tools write it./// Keys holding null are left out; ints are `<integer>`, floats `<real>`.pub fn plist_to_xml(value: serde_json::Value) -> Result<String, String> { let p = from_json(&value).ok_or("a property list cannot be null")?; let mut out = Vec::new(); p.to_writer_xml(&mut out).map_err(|e| e.to_string())?; String::from_utf8(out).map_err(|e| e.to_string())}/// `value` as a binary property list (`bplist00`), in base64.pub fn plist_to_binary(value: serde_json::Value) -> Result<String, String> { let p = from_json(&value).ok_or("a property list cannot be null")?; let mut out = Vec::new(); p.to_writer_binary(&mut out).map_err(|e| e.to_string())?; Ok(STANDARD.encode(out))}Related
hcl.qwer · 1.0.0HCL, the language of Terraform, Nomad and Packer files: read as values, expressions worked out from your variables, and written back.7 views · 6 downloadsINI ini.qwer · 1.0.0INI files read and written, sections and keys: php.ini, setup.cfg, .gitconfig and the settings of older software.8 views · 7 downloadsJava properties java_properties.qwer · 1.0.0Java .properties files read and written: Spring settings and translation bundles, with continued lines and \u escapes.6 views · 8 downloadsJSON5 json5.qwer · 1.0.0JSON5 and JSON with comments read as values: tsconfig.json and editor settings, with comments, trailing commas and unquoted keys.7 views · 8 downloads.env files dotenv.qwer · 1.0.0.env files read with their quoting rules and ${VAR} expansion, linted and compared with .env.example, and written or changed one key at a time with comments kept.8 views · 9 downloadsAvro avro.qwer · 1.0.0Apache Avro data files: rows written with a schema, as Kafka, Hadoop and BigQuery use, and read back with the schema they carry.9 views · 6 downloadsThread 0 posts
On Property lists: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.