blob: ee5ba953f29b02631c7630b8982c6c8f3a73772f [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/printing/ppd_metadata_parser.h"
#include <string>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/json/json_reader.h"
#include "base/notreached.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
namespace chromeos {
namespace {
// Attempts to
// 1. parse |input| as a Value having Type::DICTIONARY and
// 2. return Value of |key| having a given |target_type| from the same.
//
// Additionally,
// * this function never returns empty Value objects and
// * |target_type| must appear in the switch statement below.
base::Optional<base::Value> ParseJsonAndUnnestKey(
base::StringPiece input,
base::StringPiece key,
base::Value::Type target_type) {
base::Optional<base::Value> parsed = base::JSONReader::Read(input);
if (!parsed || !parsed->is_dict()) {
return base::nullopt;
}
base::Optional<base::Value> unnested = parsed->ExtractKey(key);
if (!unnested || unnested->type() != target_type) {
return base::nullopt;
}
bool unnested_is_empty = true;
switch (target_type) {
case base::Value::Type::LIST:
unnested_is_empty = unnested->GetList().empty();
break;
case base::Value::Type::DICTIONARY:
unnested_is_empty = unnested->DictEmpty();
break;
default:
NOTREACHED();
break;
}
if (unnested_is_empty) {
return base::nullopt;
}
return unnested;
}
} // namespace
base::Optional<std::vector<std::string>> ParseLocales(
base::StringPiece locales_json) {
const auto as_value =
ParseJsonAndUnnestKey(locales_json, "locales", base::Value::Type::LIST);
if (!as_value.has_value()) {
return base::nullopt;
}
std::vector<std::string> locales;
for (const auto& iter : as_value.value().GetList()) {
std::string locale;
if (!iter.GetAsString(&locale)) {
continue;
}
locales.push_back(locale);
}
if (locales.empty()) {
return base::nullopt;
}
return locales;
}
base::Optional<ParsedManufacturers> ParseManufacturers(
base::StringPiece manufacturers_json) {
const auto as_value = ParseJsonAndUnnestKey(manufacturers_json, "filesMap",
base::Value::Type::DICTIONARY);
if (!as_value.has_value()) {
return base::nullopt;
}
ParsedManufacturers manufacturers;
for (const auto& iter : as_value.value().DictItems()) {
std::string printers_metadata_basename;
if (!iter.second.GetAsString(&printers_metadata_basename)) {
continue;
}
manufacturers[iter.first] = printers_metadata_basename;
}
if (manufacturers.empty()) {
return base::nullopt;
}
return manufacturers;
}
base::Optional<ParsedPrinters> ParsePrinters(base::StringPiece printers_json) {
const auto as_value = ParseJsonAndUnnestKey(printers_json, "modelToEmm",
base::Value::Type::DICTIONARY);
if (!as_value.has_value()) {
return base::nullopt;
}
ParsedPrinters printers;
for (const auto& iter : as_value.value().DictItems()) {
std::string printer_effective_make_and_model;
if (!iter.second.GetAsString(&printer_effective_make_and_model)) {
continue;
}
printers.push_back(
ParsedPrinter{iter.first, printer_effective_make_and_model});
}
if (printers.empty()) {
return base::nullopt;
}
return printers;
}
base::Optional<ParsedReverseIndex> ParseReverseIndex(
base::StringPiece reverse_index_json) {
const base::Optional<base::Value> makes_and_models = ParseJsonAndUnnestKey(
reverse_index_json, "reverseIndex", base::Value::Type::DICTIONARY);
if (!makes_and_models.has_value() || makes_and_models->DictSize() == 0) {
return base::nullopt;
}
ParsedReverseIndex parsed;
for (const auto& kv : makes_and_models->DictItems()) {
if (!kv.second.is_dict()) {
continue;
}
const std::string* manufacturer = kv.second.FindStringKey("manufacturer");
const std::string* model = kv.second.FindStringKey("model");
if (manufacturer && model && !manufacturer->empty() && !model->empty()) {
parsed.insert_or_assign(kv.first,
ReverseIndexLeaf{*manufacturer, *model});
}
}
if (parsed.empty()) {
return base::nullopt;
}
return parsed;
}
} // namespace chromeos