%% Copyright (c) 2017 Guilherme Andrade %% %% Permission is hereby granted, free of charge, to any person obtaining a %% copy of this software and associated documentation files (the "Software"), %% to deal in the Software without restriction, including without limitation %% the rights to use, copy, modify, merge, publish, distribute, sublicense, %% and/or sell copies of the Software, and to permit persons to whom the %% Software is furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER %% DEALINGS IN THE SOFTWARE. %% %% Yabko is an independent project and has not been authorized, sponsored, %% or otherwise approved by Apple Inc. -module(yabko). -include("yabko_common.hrl"). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -endif. %% ------------------------------------------------------------------ %% API Function Exports %% ------------------------------------------------------------------ -export([decode/1]). -ignore_xref({decode,1}). %% ------------------------------------------------------------------ %% Type Definitions %% ------------------------------------------------------------------ -type object() :: undefined | boolean() | int64() | float() | calendar:datetime() | {uid, uint64()} | [object()] | #{ binary() => object() }. -export_type([object/0]). -type int64() :: -9223372036854775808..9223372036854775807. -export_type([int64/0]). -type uint64() :: 0..18446744073709551615. -export_type([uint64/0]). %% ------------------------------------------------------------------ %% API Function Definitions %% ------------------------------------------------------------------ -spec decode(Data) -> {ok, DecodedObject} | {error, Error} when Data :: iodata(), DecodedObject :: object(), Error :: {exception, atom(), term(), [term()]}. decode(<<"bplist", Version:2/binary, EncodedPList/binary>>) when Version =:= <<"00">>; Version =:= <<"01">> -> try yabko_bin:decode(EncodedPList, 8) of PList -> {ok, PList} catch Class:Reason -> {error, {exception, Class, Reason, erlang:get_stacktrace()}} end; decode(<>) -> try yabko_xml:decode(EncodedPList) of PList -> {ok, PList} catch Class:Reason -> {error, {exception, Class, Reason, erlang:get_stacktrace()}} end; decode(IoData) -> Binary = iolist_to_binary(IoData), decode(Binary). %% ------------------------------------------------------------------ %% Unit Tests %% ------------------------------------------------------------------ -ifdef(TEST). %% @private bin_decode_test() -> run_test__("test_data/test.bin.plist", {ok, expected_generic_test_data()}). %% @private xml_decode_test() -> run_test__("test_data/test.xml.plist", {ok, expected_generic_test_data()}). %% @private uid_decode_bin_test() -> % taken from https://github.com/rodneyrehm/CFPropertyList run_test__("test_data/uid-list.plist", {ok, expected_uid_test_data()}). %% @private uid_decode_xml_test() -> % taken from https://github.com/rodneyrehm/CFPropertyList run_test__("test_data/uid-list.xml", {ok, expected_uid_test_data()}). %% @private bin_unicode_decode_test() -> run_test__("test_data/unicode.bin.plist", {ok, expected_unicode_test_data()}). %% @private xml_unicode_decode_test() -> run_test__("test_data/unicode.xml.plist", {ok, expected_unicode_test_data()}). %% @private float32_decode_test() -> run_test__("test_data/float32.bin.plist", {ok, #{<<"etc etc..">> => 1.0}}). %% @private run_test__(Path, ExpectedResult) -> {ok, Encoded} = file:read_file(Path), ?assertEqual(ExpectedResult, decode(Encoded)). %% @private expected_generic_test_data() -> #{<<"Lincoln">> => #{<<"DOB">> => {{1809,2,12},{9,18,0}}, <<"Eulogy">> => <<183,5,15,253,33,21,131,249,92,108,122,222,146,36,48,160, 55,207,61,19,93,90,230,12,62,148,58,9,80,96,143,66,31, 185,212,80,46,38,211,39,178,206,171,74,45,85,147,156, 163,192,214,241,175,82,155,80,17,145,231,188,98,37,206, 74,238,249,180,115,103,174,202,250,152,186,185,42,89,84, 189,191,69,196,134,21,63,96,106,0,52,224,177,7,41,108, 77,50,128,206,137,167,52,237,20,147,139,204,227,182,93, 79,2,206,138,30,60,3,248,35,31,164,98,207,116,224,244, 24,204,63,173,160,113,53,114,230,166,104,141,6,0,151>>, <<"IsNamedGeorge">> => false, <<"Name">> => <<"Abraham Lincoln">>, <<"Scores">> => [8,-8,512,-512,65536,-65536,4294967296,-4294967296,3.14, -3.14,4.900000095367432,-4.900000095367432]}, <<"Washington">> => #{<<"DOB">> => {{1732,2,17},{1,32,0}}, <<"Eulogy">> => <<19,220,90,23,220,229,7,1,57,36,3,160,249,63,76,151,169, 162,164,181>>, <<"IsNamedGeorge">> => true, <<"Name">> => <<"George Washington">>, <<"Scores">> => [6,4.599999904632568,6]}}. %% @private expected_uid_test_data() -> #{<<"small">> => {uid,1}, <<"medium">> => {uid,256}, <<"large">> => {uid,65536}, <<"huge">> => {uid,4294967296}}. %% @private expected_unicode_test_data() -> #{<<"A thing">> => <<"他度於樂。實術醫收參不民對續子義發性親的論草像終因:" "地己關物理應在她決精……在論被主馬路兩關保分怕件比人" "……孩經感發特,那經情你相,破族木相資個外生" "。熱民待……天女度之竟賽外的史次道就就只……動技會," "世國系笑早,四司作方極中面:晚寫許或美大下正人濟" "保樂這候可著了下供半於受下唱素動古下。"/utf8>>}. -endif.