1919
2020#include < QFileInfo>
2121#include < QHash>
22- #include < QJsonDocument>
23- #include < QJsonObject>
2422#include < QSysInfo>
2523
24+ #include < boost/json.hpp>
25+
2626#include < multipass/constants.h>
2727#include < multipass/exceptions/manifest_exceptions.h>
2828#include < multipass/settings/settings.h>
@@ -39,24 +39,22 @@ const QHash<QString, QString> arch_to_manifest{{"x86_64", "amd64"},
3939 {" power64" , " ppc64el" },
4040 {" power64le" , " ppc64el" }};
4141
42- QJsonObject parse_manifest (const QByteArray& json )
42+ const boost::json::object* if_contains_object (const boost::json::value& data, std::string_view key )
4343{
44- QJsonParseError parse_error;
45- const auto doc = QJsonDocument::fromJson (json, &parse_error);
46- if (doc.isNull ())
47- throw mp::GenericManifestException (parse_error.errorString ().toStdString ());
48-
49- if (!doc.isObject ())
50- throw mp::GenericManifestException (" invalid manifest object" );
51- return doc.object ();
44+ if (auto elem = data.as_object ().if_contains (key))
45+ {
46+ if (auto obj = elem->if_object ())
47+ return obj;
48+ }
49+ return nullptr ;
5250}
5351
54- QString latest_version_in (const QJsonObject & versions)
52+ QString latest_version_in (const boost::json::object & versions)
5553{
5654 QString max_version;
57- for (auto it = versions. constBegin (); it != versions. constEnd (); ++it )
55+ for (const auto & [key, _] : versions)
5856 {
59- const auto version = it. key ( );
57+ const auto version = QString::fromStdString (key );
6058 if (version < max_version)
6159 continue ;
6260 max_version = version;
@@ -96,102 +94,95 @@ std::unique_ptr<mp::SimpleStreamsManifest> mp::SimpleStreamsManifest::fromJson(
9694 const std::optional<QByteArray>& json_from_mirror,
9795 const QString& host_url,
9896 std::function<bool (VMImageInfo&)> mutator)
97+ try
9998{
10099 // Get the official manifest products
101- const auto manifest_from_official = parse_manifest (json_from_official);
102- const auto updated = manifest_from_official[" updated" ].toString ();
103-
104- const auto manifest_products_from_official = manifest_from_official[" products" ].toObject ();
105- if (manifest_products_from_official.isEmpty ())
100+ const auto manifest_from_official = boost::json::parse (std::string_view (json_from_official));
101+ const auto updated = lookup_or<QString>(manifest_from_official, " updated" , " " );
102+ const auto & manifest_products_from_official = manifest_from_official.at (" products" ).as_object ();
103+ if (manifest_products_from_official.empty ())
106104 throw mp::GenericManifestException (" No products found" );
107105
108106 auto arch = QSysInfo::currentCpuArchitecture ();
109107 auto mapped_arch = arch_to_manifest.value (arch, arch);
110108
111109 // Get the mirror manifest products, if any
112- std::optional<QJsonObject > manifest_products_from_mirror = std::nullopt ;
110+ std::optional<boost::json::object > manifest_products_from_mirror = std::nullopt ;
113111 if (json_from_mirror)
114112 {
115- const auto manifest_from_mirror = parse_manifest (json_from_mirror.value ());
116- const auto products_from_mirror = manifest_from_mirror[" products" ].toObject ();
117- manifest_products_from_mirror = std::make_optional (products_from_mirror);
113+ const auto manifest_from_mirror = boost::json::parse (std::string_view (*json_from_mirror));
114+ manifest_products_from_mirror = manifest_from_mirror.at (" products" ).as_object ();
118115 }
119-
120- const QJsonObject manifest_products =
116+ const auto & manifest_products =
121117 manifest_products_from_mirror.value_or (manifest_products_from_official);
122118
123119 std::vector<VMImageInfo> products;
124- for (auto it = manifest_products. constBegin (); it != manifest_products. constEnd (); ++it )
120+ for (const auto & [product_key, product] : manifest_products)
125121 {
126- const auto product_key = it.key ();
127- const QJsonValue product = it.value ();
128-
129- if (product[" arch" ].toString () != mapped_arch)
122+ if (lookup_or<QString>(product, " arch" , " " ) != mapped_arch)
130123 continue ;
131124
132- const auto official_product = manifest_products_from_official[product_key]. toObject ( );
133- if (official_product. isEmpty () )
125+ const auto * official_product = manifest_products_from_official. if_contains (product_key );
126+ if (! official_product)
134127 continue ;
135128
136- auto product_aliases = product[ " aliases" ]. toString ( ).split (" ," );
129+ auto product_aliases = lookup_or<QString>( product, " aliases" , " " ).split (" ," );
137130
138- const auto image_type = product[" image_type" ].toString ();
139- const auto os = product[" os" ].toString ();
140- const auto release = product[" release" ].toString ();
141- const auto release_title = product[" release_title" ].toString ();
142- const auto release_codename = product[" release_codename" ].toString ();
143- const auto supported = product[" supported" ].toBool () || product_aliases.contains (" devel" ) ||
131+ const auto image_type = lookup_or<QString>(product, " image_type" , " " );
132+ const auto os = lookup_or<QString>(product, " os" , " " );
133+ const auto release = lookup_or<QString>(product, " release" , " " );
134+ const auto release_title = lookup_or<QString>(product, " release_title" , " " );
135+ const auto release_codename = lookup_or<QString>(product, " release_codename" , " " );
136+ const auto supported = lookup_or<bool >(product, " supported" , false ) ||
137+ product_aliases.contains (" devel" ) ||
144138 (os == " ubuntu-core" && image_type == " stable" );
145139
146- const auto versions = product[ " versions" ]. toObject ( );
147- if (versions. isEmpty ())
140+ const auto * versions = if_contains_object ( product, " versions" );
141+ if (! versions || versions-> empty ())
148142 continue ;
149- const auto official_versions = official_product[ " versions" ]. toObject ( );
150- if (official_versions. isEmpty ())
143+ const auto * official_versions = if_contains_object (* official_product, " versions" );
144+ if (! official_versions || official_versions-> empty ())
151145 continue ;
152146
153- const auto latest_version = latest_version_in (versions);
154-
155- for (auto it = versions.constBegin (); it != versions.constEnd (); ++it)
147+ const auto latest_version = latest_version_in (*versions);
148+ for (const auto & [version_string, version] : *versions)
156149 {
157- const auto version_string = it.key ();
158- const auto version = versions[version_string].toObject ();
159- const auto official_version = official_versions[version_string].toObject ();
160- if (version != official_version)
150+ const auto * official_version = official_versions->if_contains (version_string);
151+ if (!official_version || version != *official_version)
161152 continue ;
162153
163- const auto items = version[ " items" ]. toObject ( );
164- if (items. isEmpty ())
154+ const auto * items = if_contains_object ( version, " items" );
155+ if (! items || items-> empty ())
165156 continue ;
166157
167158 const auto & driver = MP_SETTINGS.get (mp::driver_key);
168159
169- QJsonObject image;
170160 QString sha256, image_location;
171161 int size = -1 ;
172162
173- QString image_key;
163+ std::string image_key;
174164 // Prioritize UEFI images
175- if (items. contains (" uefi1.img" ))
165+ if (items-> contains (" uefi1.img" ))
176166 image_key = " uefi1.img" ;
177167 // For Ubuntu Core images
178- else if (items. contains (" img.xz" ) && os == " ubuntu-core" )
168+ else if (items-> contains (" img.xz" ) && os == " ubuntu-core" )
179169 image_key = " img.xz" ;
180170 // Last resort, use img
181- else if (items. contains (" disk1.img" ))
171+ else if (items-> contains (" disk1.img" ))
182172 image_key = " disk1.img" ;
183173 // Otherwise, give up
184174 else
185175 continue ;
186176
187- image = items[image_key]. toObject ( );
188- image_location = host_url + image[ " path" ]. toString ( );
189- sha256 = image[ " sha256" ]. toString ( );
190- size = image[ " size" ]. toInt ( -1 );
177+ const auto & image = items-> at (image_key );
178+ image_location = host_url + value_to<QString>( image. at ( " path" ) );
179+ sha256 = lookup_or<QString>( image, " sha256" , " " );
180+ size = lookup_or< int >( image, " size" , -1 );
191181
182+ const auto version_qstring = QString::fromStdString (version_string);
192183 // Aliases always alias to the latest version
193184 const QStringList& aliases =
194- version_string == latest_version ? product_aliases : QStringList ();
185+ version_qstring == latest_version ? product_aliases : QStringList ();
195186
196187 VMImageInfo info{aliases,
197188 " Ubuntu" ,
@@ -202,7 +193,7 @@ std::unique_ptr<mp::SimpleStreamsManifest> mp::SimpleStreamsManifest::fromJson(
202193 image_location,
203194 sha256,
204195 host_url,
205- version_string ,
196+ version_qstring ,
206197 size,
207198 true };
208199
@@ -212,9 +203,12 @@ std::unique_ptr<mp::SimpleStreamsManifest> mp::SimpleStreamsManifest::fromJson(
212203 }
213204 }
214205 }
215-
216206 if (products.empty ())
217207 throw mp::EmptyManifestException (" No supported products found." );
218208
219209 return std::make_unique<SimpleStreamsManifest>(updated, std::move (products));
220210}
211+ catch (const boost::system::system_error& e)
212+ {
213+ throw GenericManifestException (e.what ());
214+ }
0 commit comments