910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/scanner/scanner_feedback.h"

#include <cstddef>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>

#include "base/containers/to_value_list.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "components/manta/proto/scanner.pb.h"

namespace ash {

namespace {

// Converts a `NewContactAction::EmailAddress` to a `base::Value::Dict`.
base::Value::Dict EmailAddressToDict(
    manta::proto::NewContactAction::EmailAddress&& email_address) {
  base::Value::Dict dict;

  if (!email_address.value().empty()) {
    dict.Set("value", std::move(*email_address.mutable_value()));
  }
  if (!email_address.type().empty()) {
    dict.Set("type", std::move(*email_address.mutable_type()));
  }

  return dict;
}

// Converts a `NewContactAction::PhoneNumber` to a `base::Value::Dict`.
base::Value::Dict PhoneNumberToDict(
    manta::proto::NewContactAction::PhoneNumber&& phone_number) {
  base::Value::Dict dict;

  if (!phone_number.value().empty()) {
    dict.Set("value", std::move(*phone_number.mutable_value()));
  }
  if (!phone_number.type().empty()) {
    dict.Set("type", std::move(*phone_number.mutable_type()));
  }

  return dict;
}

// Converts a `ScannerAction` variant into an "externally tagged"
// `base::Value::Dict`.
base::Value::Dict NewEventToDict(manta::proto::NewEventAction&& new_event) {
  base::Value::Dict dict;

  if (!new_event.title().empty()) {
    dict.Set("title", std::move(*new_event.mutable_title()));
  }
  if (!new_event.description().empty()) {
    dict.Set("description", std::move(*new_event.mutable_description()));
  }
  if (!new_event.dates().empty()) {
    dict.Set("dates", std::move(*new_event.mutable_dates()));
  }
  if (!new_event.location().empty()) {
    dict.Set("location", std::move(*new_event.mutable_location()));
  }

  return base::Value::Dict().Set("new_event", std::move(dict));
}

base::Value::Dict NewContactToDict(
    manta::proto::NewContactAction&& new_contact) {
  base::Value::Dict dict;

  if (!new_contact.given_name().empty()) {
    dict.Set("given_name", std::move(*new_contact.mutable_given_name()));
  }
  if (!new_contact.family_name().empty()) {
    dict.Set("family_name", std::move(*new_contact.mutable_family_name()));
  }
  if (!new_contact.email().empty()) {
    dict.Set("email", std::move(*new_contact.mutable_email()));
  }
  if (!new_contact.phone().empty()) {
    dict.Set("phone", std::move(*new_contact.mutable_phone()));
  }
  if (!new_contact.email_addresses().empty()) {
    dict.Set(
        "email_addresses",
        base::ToValueList(
            *new_contact.mutable_email_addresses(),
            [](manta::proto::NewContactAction::EmailAddress& email_address) {
              return EmailAddressToDict(std::move(email_address));
            }));
  }
  if (!new_contact.phone_numbers().empty()) {
    dict.Set("phone_numbers",
             base::ToValueList(
                 *new_contact.mutable_phone_numbers(),
                 [](manta::proto::NewContactAction::PhoneNumber& phone_number) {
                   return PhoneNumberToDict(std::move(phone_number));
                 }));
  }

  return base::Value::Dict().Set("new_contact", std::move(dict));
}

base::Value::Dict NewGoogleDocToDict(
    manta::proto::NewGoogleDocAction&& new_google_doc) {
  base::Value::Dict dict;

  if (!new_google_doc.title().empty()) {
    dict.Set("title", std::move(*new_google_doc.mutable_title()));
  }
  if (!new_google_doc.html_contents().empty()) {
    dict.Set("html_contents",
             std::move(*new_google_doc.mutable_html_contents()));
  }

  return base::Value::Dict().Set("new_google_doc", std::move(dict));
}

base::Value::Dict NewGoogleSheetToDict(
    manta::proto::NewGoogleSheetAction&& new_google_sheet) {
  base::Value::Dict dict;

  if (!new_google_sheet.title().empty()) {
    dict.Set("title", std::move(*new_google_sheet.mutable_title()));
  }
  if (!new_google_sheet.csv_contents().empty()) {
    dict.Set("csv_contents",
             std::move(*new_google_sheet.mutable_csv_contents()));
  }

  return base::Value::Dict().Set("new_google_sheet", std::move(dict));
}

base::Value::Dict CopyToClipboardToDict(
    manta::proto::CopyToClipboardAction&& copy_to_clipboard) {
  base::Value::Dict dict;

  if (!copy_to_clipboard.plain_text().empty()) {
    dict.Set("plain_text", std::move(*copy_to_clipboard.mutable_plain_text()));
  }
  if (!copy_to_clipboard.html_text().empty()) {
    dict.Set("html_text", std::move(*copy_to_clipboard.mutable_html_text()));
  }

  return base::Value::Dict().Set("copy_to_clipboard", std::move(dict));
}

class UserFacingValueWriter {
 public:
  explicit UserFacingValueWriter(size_t depth_limit, size_t output_limit)
      : user_facing_string_(""),
        depth_limit_(depth_limit),
        output_limit_(output_limit) {}

  void BuildString(std::monostate node) { OutputValue("null"); }
  void BuildString(bool node) { OutputValue(node ? "true" : "false"); }
  void BuildString(int node) { OutputValue(base::NumberToString(node)); }
  void BuildString(double node) { OutputValue(base::NumberToString(node)); }
  void BuildString(std::string_view node) { OutputValue(node); }
  void BuildString(const base::Value::BlobStorage& node) {
    user_facing_string_.reset();
  }
  void BuildString(const base::Value::Dict& node) {
    if (path_.size() >= depth_limit_) {
      user_facing_string_.reset();
      return;
    }

    for (auto [key, value] : node) {
      path_.push_back(key);
      value.Visit([this](const auto& member) { BuildString(member); });
      path_.pop_back();
    }
  }
  void BuildString(const base::Value::List& node) {
    if (path_.size() >= depth_limit_) {
      user_facing_string_.reset();
      return;
    }

    size_t i = 0;
    for (const base::Value& value : node) {
      path_.push_back(base::NumberToString(i));
      value.Visit([this](const auto& member) { BuildString(member); });
      path_.pop_back();
      ++i;
    }
  }

  std::optional<std::string>&& user_facing_string() && {
    return std::move(user_facing_string_);
  }

 private:
  void OutputValue(std::string_view value) {
    if (!user_facing_string_.has_value()) {
      return;
    }

    if (!path_.empty()) {
      user_facing_string_->append(path_[0]);
      for (size_t i = 1; i < path_.size(); ++i) {
        user_facing_string_->push_back('.');
        user_facing_string_->append(path_[i]);
      }
      user_facing_string_->append(": ");
    }
    user_facing_string_->append(value);
    user_facing_string_->push_back('\n');

    if (user_facing_string_->size() > output_limit_) {
      user_facing_string_.reset();
      return;
    }
  }

  std::vector<std::string> path_;
  std::optional<std::string> user_facing_string_;
  size_t depth_limit_;
  size_t output_limit_;
};

}  // namespace

base::Value::Dict ScannerActionToDict(manta::proto::ScannerAction action) {
  switch (action.action_case()) {
    case manta::proto::ScannerAction::kNewEvent:
      return NewEventToDict(std::move(*action.mutable_new_event()));

    case manta::proto::ScannerAction::kNewContact:
      return NewContactToDict(std::move(*action.mutable_new_contact()));

    case manta::proto::ScannerAction::kNewGoogleDoc:
      return NewGoogleDocToDict(std::move(*action.mutable_new_google_doc()));

    case manta::proto::ScannerAction::kNewGoogleSheet:
      return NewGoogleSheetToDict(
          std::move(*action.mutable_new_google_sheet()));

    case manta::proto::ScannerAction::kCopyToClipboard:
      return CopyToClipboardToDict(
          std::move(*action.mutable_copy_to_clipboard()));

    case manta::proto::ScannerAction::ACTION_NOT_SET:
      return base::Value::Dict();
  }
}

std::optional<std::string> ValueToUserFacingString(base::ValueView value,
                                                   size_t depth_limit,
                                                   size_t output_limit) {
  UserFacingValueWriter writer(depth_limit, output_limit);
  value.Visit([&writer](const auto& value) { writer.BuildString(value); });
  return std::move(writer).user_facing_string();
}

}  // namespace ash