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

#ifndef REMOTING_HOST_LINUX_GVARIANT_REF_H_
#define REMOTING_HOST_LINUX_GVARIANT_REF_H_

#include <glib-object.h>
#include <glib.h>
#include <glibconfig.h>

#include <algorithm>
#include <array>
#include <compare>
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <map>
#include <optional>
#include <ranges>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>

#include "base/check.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/types/expected.h"
#include "remoting/host/base/loggable.h"
#include "remoting/host/base/pointer_utils.h"
#include "remoting/host/linux/gvariant_type.h"

// Provides a scoped wrapper, GVariantRef, around a GLib Variant to handle
// reference counting, conversion to and from other C++ types, and additional
// convenience methods when the GVariant's type is statically known.
//
// A good familiarity with the GVariant type system is recommended to use or
// read this code: https://docs.gtk.org/glib/struct.VariantType.html

namespace remoting {
namespace gvariant {

// A GVariantRef is a wrapper around a GLib Variant that handles scoped
// reference counting and convenient, typesafe conversion between GVariants and
// standard types.
//
// GVariants are logically immutable once created, and this class's only data
// member is a reference-counted pointer. Thus, instances can be copied and
// passed around cheaply.
//
// Sample usage:
//
// Creating a GVariantRef:
//
//     // Creates a GVariantRef with a compile-time type of "*" (can hold any
//     // value) and initialize it with a value of type "as" (array of strings)
//     // constructed from the provided span.
//     GVariantRef<> variant =
//         GVariantRef<>::From(base::span{{"Bob", "Sally", "Alice"}});
//
// If a type string is provided as a template argument, the GVariantRef may only
// hold values that match that type string, which may be indefinite. E.g., a
// GVariantRef<"s"> may only strings, while a GVariantRef<"{?*}"> may hold any
// dictionary entry. The type may be narrowed using TryFrom:
//
//     GVariantRef<> variant = ...;
//     // expectedVariant will be a base::expected with either the requested
//     // GVariantRef or an error message suitable for logging.
//     auto expectedVariant = GVariantRef<"as">::TryFrom(variant);
//     if (expectedVariant.has_value()) {
//         // expectedVariant.value() is guaranteed to be an array of strings
//         // and can be iterated through, infalibly converted to a
//         // std::vector<std::string>, passed to functions expected GVariantRef
//         // arrays, et cetera.
//     }
//
// Conversion can combine narrowing and converting from C++ types:
//
//     std::tuple<GVariantRef<>> tuple = ...;
//     auto expectedVariant = GVariantRef<"(s)">::TryFrom(tuple);
//
// Getting values from a GVariantRef:
//
//     GVariantRef<> variant = ...;
//     // Returns a base::expected with the value or an error message.
//     auto value = result.TryInto<std::vector<std::string>>();
//
// If the requested conversion is valid for all values of the GVariantRef's
// static type, you can use Into instead to get the value directly:
//
//     GVariantRef<"s"> variant = ...;
//     std::string value = variant.Into<std::string>();
//
// Like TryFrom, TryInto can also be used to attempt to narrow a GVariantRef's
// static type, and can be combined with conversions to C++ types:
//
//     GVariantRef<> variant = ...;  // Compile-time type string is "*";
//     // Value will be returned if variant's run-time type is a subtype of
//     // "a(s*)". Otherwise, an error string will be returned.
//     auto result2 = variant.TryInto<std::vector<GVariantRef<"(s*)">>>();
//
// If the GVariantRef is known to be a container (that is, it's static type is
// a boxed value, an array, a maybe value, a tuple, or a dictionary entry), it
// can be used with range-based for:
//
//     GVariantRef<"a(iu)"> variant1 = ...;
//     for (auto item : variant1) { /* item is a GVariantRef<"(iu)"> */}
//     GVariantRef<"r"> variant2 = ...;
//     for (auto item : variant1) { /* item is a GVariantRef<"*"> */}
//
// If it is known to be a container of a fixed size (boxed value, fixed-size
// tuple (not "r"), or dictionary entry), it can be used with structured
// binding:
//
//     GVariantRef<"(uba{sv})"> variant = ...;
//     // Yields GVariantRef<"u">, GVariantRef<"b">, and GVariantRef<"a{sv}">
//     auto [first, second, third] = variant;
//
// For convenience, GVariantRefs known to be a dictionary type (the compile-time
// type string is a subtype of "a{?*}") provide a LookUp() method to find the
// first value with a matching key.
//
//     GVariantRef<"a{is}"> variant = ...;
//     std::optional<GVariantRef<"s">> value = variant.LookUp(5);
//
// Finally, values can be extracted from containers using Destructure() and
// TryDestructure(). Destructure requires a known fixed-size container, while
// TryDestructure checks if the GVariantRef holds a matching container at
// runtime.
//
//     GVariantRef<"(ii(ssssv))"> variant = ...;
//     std::uint32_t i1, i2;
//     std::string s1, s2, s3, s4;
//     GVariantRef<> v;
//     // Inner containers can be unpacked by passing (possibly nested) tuples
//     // of lvalue references.
//     variant.Destructure(i1, i2, std::forward_as_tuple(s1, s2, s3, s4,
//                                                       std::tie(v)));
//
//     GVariantRef<"mb"> variant = ...;
//     bool b;
//     // result will contain an error if the maybe value is unexpectedly empty.
//     base::expected<void, Loggable> result = variant.TryDestructure(b);
//
// Access to the underlying GVariant pointer can be obtained via raw() and
// release(). A GVariantRef can be created from a raw GVariant pointer via
// Ref(), RefSink(), and Take().
//
// For convenience, the GVariantRef class is exported to the remoting namespace.
//
// This header provides conversions to and from the following types. Additional
// support for additional types can be added by providing an appropriate
// specialization of gvariant::Mapping<T>.
//
// Basic fixed types:
//
// bool (type code "b")
// std::uint8_t (type code "y")
// std::int16_t (type code "n")
// std::uint16_t (type code "q")
// std::int32_t (type code "i")
// std::uint32_t (type code "u")
// std::int64_t (type code "x")
// std::uint64_t (type code "t")
// double (type code "d")
//
// Strings:
//
// std::string (type code "s")
// const char * ([Try]From() only, type code "s")
// std::string_view ([Try]From() only, type code "s")
//
// Note: Strings must be valid UTF-8. For convenience, both From() and TryFrom()
// are provided. If the string (or all strings in an aggregate type) are
// known to be valid UTF-8, From() may be used. Otherwise TryFrom() should be
// used. Calling From() with a string containing invalid UTF-8 will
// result in a crash.
//
// Containers:
//
// std::optional<T> (type code "mT") - To use with From(), the type string of T
//     must be definite. E.g., it may not contain an unboxed heterogeneous
//     std::variant or unconstrained GVariantRef. Otherwise, TryFrom() must be
//     used. Alternatively, GVariantRef::FilledMaybe can be used for indefinite
//     types that will never actually be absent. Note that maybe values are not
//     currently supported by D-Bus.
// std::vector<T> (type code "aT") - To use with From(), the type string of T
//     must be definite. Otherwise, TryFrom() must be used.
// std::map<K, T> (type code "a{KT}") - To use with From(), the type string of K
//     and T must be definite. Otherwise, TryFrom() must be used.
// std::pair<K, T> (type code "{KT}")
// std::ranges::range<T> ([Try]From() only, type code "aE" where
//     E = std::ranges::range_value_t<T>) - The type string of T must be
//     definite to use with From(). Otherwise, TryFrom() must be used.
// std::tuple<...> (type code "(...)")
// std::variant<...> (type code "S", where S is the narrowest common supertype
//     of all of the variant alternatives) - Despite the name, when used with
//     From(), the type will be the type of the active variant, not "v". If the
//     type should be "v", use a gvariant::Boxed<std::variant<...>>. When used
//     with [Try]Into(), each variant alternative will be
//     tried in turn until one succeeds or all are exhausted. Similarly to
//     From(), if the type in the GVariant is expected to be "v" rather than a
//     concrete type, use with gvariant::Boxed. From() and TryFrom() are
//     provided if all of the alternatives provide the respected method.
//     TryInto() is provided if provided by any of the alternatives. Into() is
//     provided if the GVariantRef at hand can be infallibly converted to any
//     of the variant's alternatives. E.g., the following is valid:
//
//         GVariantRef<"i"> gvariant = ...;
//         auto v = gvariant.Into<std::variant<std::int32, bool>>();
//
// Special types:
//
// GVariantRef<C> (type code "C") - GVariantRef can itself be used with
//     [Try]From() or [Try]Into(). This can be used to widen or narrow the
//     type or to hold inner values as nested GVariants. Like with
//     std::variant, the type of the GVariantRef is used directly. If the
//     expected/desired type is "v", wrap it in a gvariant::Boxed.
// gvariant::Ignored ([Try]Into() only, type code "*") - Discard the value at
//     this position instead of decoding it.
// decltype(std::ignore) - Same as gvariant::Ignored. Allows passing std::ignore
//     to [Try]Destructure().
// gvariant::Boxed<T> (type code "v") - Wrapper for a value that appears boxed
//     inside a nested variant, rather than directly. A Boxed<T> is definite
//     regardless of whether the contained type is. Provides [Try]From() and
//     [Try]Into() if the inner type provides them for GVariantRef<"*">.
// gvariant::FilledMaybe<T> (type code "mT") - Wrapper for a value that
//     appears inside a maybe, but will always be present. Presence of
//     [Try]From() and TryInto() mirror the inner type. Into() is never
//     provided.
// gvariant::EmptyArrayOf<C> (type code "aC") - Represents an empty array of
//     the type C, which should be an instance of GVariantRef::Type. C must be
//     definite to use with [Try]From(). Into() is never provided; TryInto()
//     must be used to check if the array is, in fact, empty.
// gvariant::ObjectPath (type code "o") - Wrapper around a std::string that
//     contains a DBus object path.
// gvariant::ObjectPathCStr ([Try]From() only, type code "o") - An owned object
//     path that can be used as a function parameter (somewhat analogous to a
//     string_view) or to hold a compile-time-verified object path constant.
// gvariant::TypeSignature (type code "g") - Wrapper around a std::string
//     that contains a DBus type signature.
// gvariant::TypeSignatureCStr ([Try]From() only, type code "g") - An owned type
//     signature that can be used as a function parameter (somewhat analogous to
//     a string_view) or to hold a compile-time-verified type signature
//     constant.
//
// Convenience functions:
//
// GVariantFrom(value) - Like GVariantRef<C>::From(), but infers C from value's
//     type.
// gvariant::BoxedRef(value) - Returns a gvariant::Boxed that holds a const
//     reference to value. (Whereas Boxed{value} would hold a copy of value.)
// gvariant::FilledMaybeRef(value) - Returns a gvariant::FilledMaybe that holds
//     a const reference to value. (Whereas FilledMaybe{value} would hold a copy
//     of value.)
template <Type C = Type("*")>
class GVariantRef;

// A struct specialized for each type supporting conversion to or from a
// GVariant.
//
// All specializations must provide a kType field, which should be in the form
// of a static constexpr Type. If the exact type can only be determined at
// runtime, an indefinite type can be used (e.g., "*").
//
//     static constexpr Type kType{"i"};
//
// A given type may support conversion to a GVariant, from a GVariant, or
// both. An example of a type that only supports conversion to a GVariant is
// a C-style string (const char *). An example of a type that only supports
// conversion from a GVariant is GVariantRef::Ignored.
//
// To support conversion to a GVariant, the specialization should provide a
// static From or TryFrom method that takes a value of the type and returns a
// GVariantRef<kType>, e.g.,
//
//     static GVariantRef<kType> From(const T&);
//     static base::expected<GVariantRef<kType>, Loggable> TryFrom(const T&);
//
// Usually a specialization will provide one or the other.
//
// To support conversion from a GVariant, the specialization should provide a
// static Into or TryInto method that takes a GVariantRef<kType> and returns a
// value of the type, e.g.,
//
//     static T Into(const GVariantRef<kType>&);
//     static base::expected<T, Loggable> TryInto(const GVariantRef<kType>&);
//
// Usually a specialization will provide one or the other, with one exception:
// In the event that infallible conversion is possible for only some subtypes of
// kType, Into may be specified only for those subtypes. In that case, TryInto
// must also be specified. E.g., if a conversion can take an int or a string,
// the definition might look like this:
//
//     static constexpr Type kType{"?"};
//     static base::expected<T, Loggable> TryInto(const GVariantRef<kType>&);
//     static T Into(const GVariantRef<"i">&);
//     static T Into(const GVariantRef<"s">&);
//
// In the event a Try* conversion fails, the method should return an error
// string suitable for inclusion in a log message.
template <typename T>
struct Mapping;

// Common members of all GVariantRefs.
class GVariantBase {
 public:
  // Allows creating an owned reference given a reference to GVariantBase.
  explicit operator GVariantRef<>() const;

  // Returns the inner GVariant reference without incrementing the count. It is
  // the caller's responsibility to increment the ref count if it is to be held
  // longer than the containing remoting::GVariant instance. Otherwise, the
  // caller should not call unref on it.
  GVariant* raw() const;

  // Takes owneriship of the inner GVariant, leaving this in a moved-from state.
  // It is the caller's responsibility to call unref when they are done with it.
  [[nodiscard]] GVariant* release() &&;

  // Returns the type of the value currently held in the GVariant. Will always
  // be definite.
  Type<> GetType() const;

  friend bool operator==(const GVariantBase& lhs, const GVariantBase& rhs);

 protected:
  using GVariantPtr = CRefCounted<GVariant,
                                  g_variant_ref,
                                  g_variant_unref,
                                  g_variant_take_ref,
                                  g_variant_ref_sink>;

  GVariantBase();
  explicit GVariantBase(GVariantPtr variant);
  GVariantBase(const GVariantBase& other);
  GVariantBase(GVariantBase&& other);
  GVariantBase& operator=(const GVariantBase& other);
  GVariantBase& operator=(GVariantBase&& other);
  ~GVariantBase();

 private:
  GVariantPtr variant_;
};

bool operator==(const GVariantBase& lhs, const GVariantBase& rhs);

template <Type C>
class GVariantRef : public GVariantBase {
 public:
  static constexpr auto kType = C;

  // Default constructor constructs an empty GVariantRef in the same state as
  // one that has been moved from. No operations are valid until a value has
  // been assigned.
  GVariantRef() = default;

  // Copyable and movable. Copies are cheap, only bumping the reference count.
  // The only valid operations for a moved-from instance are dropping it and
  // assigning a new value.
  GVariantRef(const GVariantRef& other) = default;
  GVariantRef(GVariantRef&& other) = default;
  GVariantRef& operator=(const GVariantRef& other) = default;
  GVariantRef& operator=(GVariantRef&& other) = default;
  ~GVariantRef() = default;

  // Allow implicit conversion from subtype to supertype.
  template <Type D>
  // NOLINTNEXTLINE(google-explicit-constructor)
  GVariantRef(const GVariantRef<D>& other)
    requires(D.IsSubtypeOf(C));

  template <Type D>
  // NOLINTNEXTLINE(google-explicit-constructor)
  GVariantRef(GVariantRef<D>&& other)
    requires(D.IsSubtypeOf(C));

  // Type conversions

  // Constructs a new GVariantRef from the provided value.
  template <typename T>
  static GVariantRef From(const T& value)
    requires(Mapping<T>::kType.IsSubtypeOf(C) &&
             requires { Mapping<T>::From(value); });

  // Constructs a new GVariantRef from the provided value, if possible.
  template <typename T>
  static base::expected<GVariantRef, Loggable> TryFrom(const T& value)
    requires(Mapping<T>::kType.HasCommonTypeWith(C) &&
             (requires { Mapping<T>::TryFrom(value); } ||
              requires { Mapping<T>::From(value); }));

  // Builds a value of the provided type from the contents of the GVariant.
  // Calls will only compile if they are guaranteed to succeed at runtime.
  template <typename T>
  T Into() const
    requires(C.IsSubtypeOf(Mapping<T>::kType) &&
             requires { Mapping<T>::Into(*this); });

  // Builds a value of the provided type from the contents of the GVariant, if
  // possible. Fails to compile if the conversion can statically be determined
  // never to succeed.
  template <typename T>
  base::expected<T, Loggable> TryInto() const
    requires(C.HasCommonTypeWith(Mapping<T>::kType) &&
             (requires(GVariantRef<Mapping<T>::kType> v) {
                Mapping<T>::TryInto(v);
              } ||
              requires(GVariantRef<Mapping<T>::kType> v) {
                Mapping<T>::Into(v);
              }));

  // Unpacks a container GVariant into lvalue references. The GVariant must be a
  // boxed value, a tuple, or a dictionary entry. Each argument must be a lvalue
  // reference or a tuple to unpack a nested container. Each tuple should
  // similarly consist of lvalue references and tuples. std::tie and
  // std::forward_as_tuple can be useful for constructing such tuples. Calls to
  // Destructure will only compile if it can be guaranteed at compile time that
  // the operation will succeed.
  template <typename... Types>
  void Destructure(Types&&... refs) const;

  // As above, but allows conversions that could fail at runtime. The GVariant
  // must be a boxed value, a maybe value, a tuple, an array, or a dictionary
  // entry. If the types or number of values don't match at runtime, an error
  // message will be returned. In this case, the reference lvalues will be in an
  // indeterminate state, as some values may have been read prior to the error
  // occurring.
  template <typename... Types>
  base::expected<void, Loggable> TryDestructure(Types&&... refs) const;

  // Iterate through the values of a container GVariant. The value type will be
  // GVariant<TypeBase::ContainedType(C)>.
  auto begin() const
    requires(C.IsContainer());
  auto end() const
    requires(C.IsContainer());
  std::size_t size() const
    requires(C.IsContainer());

  // Get the Ith element from a fixed-size container GVariant. Specializations
  // of std::tuple_size and std::tuple_element are also provided to make fixed-
  // size container GVariants tuple-like.
  template <std::size_t I>
  auto get() const
    requires(C.IsFixedSizeContainer());

  // Look up the provided key in a dictionary GVariant. The provided argument
  // must be convertible to the key type via TryFrom. Returns a
  // std::optional<GVariantRef<[value type]>> containing the found value, or
  // nullopt if the key was not found. Note that this performs a linear search
  // through the dictionary. If the dictionary is large and many lookups will be
  // performed, it might be more efficient to convert to a std::map or another
  // datastructure first.
  template <typename T>
  auto LookUp(const T& key)
    requires(C.IsSubtypeOf(Type("a{?*}")) && requires {
      decltype((*begin()).template get<0>())::TryFrom(key);
    });

  // Access the contained string without copying. C must be "s", "o", or "g".
  std::string_view string_view() const
    requires(C.IsStringType());

  // Access the contained string without copying. C must be "s", "o", or "g".
  const char* c_string() const
    requires(C.IsStringType());

  // Create from a raw GVariant*. Can only be used with GVariantRef<Type("*")>.
  // To get a narrower GVariantRef, first create one with "*" and then use
  // TryInto (or TryFrom) to convert to the narrower type.

  // Takes ownership of the reference. If it is floating, it will be sunk. May
  // not be null.
  //
  // This is the right method to call with a floating or caller-owned
  // GVariant* when one wants to pass ownership to the new GVariantRef. Most
  // GLib functions returning a GVariant* will return either a floating
  // reference (e.g., `g_variant_new`) or a caller owned reference (e.g.,
  // `get_child_value`), making their return value suitable to be passed to this
  // method (after checking for null, if the function is falible).
  static GVariantRef Take(GVariant* variant)
    requires(C == Type("*"));

  // Takes a new reference unconditionally. If object is floating, it will
  // remain so. May not be null.
  //
  // This is the right method to call with a borrowed GVariant* (such as is
  // returned by `raw()` or might be owned by a different class/struct) or a
  // caller-owned GVariant* of which the caller wants to maintain ownership.
  static GVariantRef Ref(GVariant* variant)
    requires(C == Type("*"));

  // If the reference count is floating, takes ownership and sinks it.
  // Otherwise, takes a new reference. May not be null.
  //
  // This is usually the right method to call with a GVariant* that was passed
  // into the caller as an argument. That gives the caller's caller the
  // flexibility to pass in either a newly-constructed GVariant (in which case
  // the reference will be floating, and GVariantRef will take ownership) or an
  // existing GVariant* (in which case GVariantRef will take its own reference).
  static GVariantRef RefSink(GVariant* variant)
    requires(C == Type("*"));

  // Same as above, but can be used with any GVariantRef type. The caller must
  // ensure the passed pointer is actually of the appropriate type. Passing a
  // pointer that does not match C can result in undefined behavior.
  static GVariantRef TakeUnchecked(GVariant* variant);
  static GVariantRef RefUnchecked(GVariant* variant);
  static GVariantRef RefSinkUnchecked(GVariant* variant);

 private:
  using GVariantBase::GVariantBase;
};

// Constructs a new GVariantRef from the provided value, inferring the type
// string.
template <typename T>
static GVariantRef<Mapping<T>::kType> GVariantFrom(const T& value) {
  return GVariantRef<Mapping<T>::kType>::From(value);
}

// Wrapper types and special types

// Can be used as a nested type in a call to [Try]Into() as a placeholder for a
// value the caller isn't interested in.
struct Ignored {};

// Wrapper for a value to specify that it will appear in the GVariant "boxed".
// That is, as a nested variant (type "v") holding the value, rather than the
// value directly.
template <typename T>
struct Boxed {
  T value;
};

template <typename T, typename U>
bool operator==(const Boxed<T>& lhs, const Boxed<U>& rhs)
  requires requires(T t, U u) { t == u; }
{
  return lhs.value == rhs.value;
}

// Returns a gvariant::Boxed that holds a const reference to value. (Whereas
// Boxed{value} would hold a copy of value.) Useful to avoid making an extra
// copy when constructing a GVariantRef.
template <typename T>
Boxed<const T&> BoxedRef(const T& value LIFETIME_BOUND) {
  return {value};
}

// Wrapper for a value that should appear inside a maybe, but will always be
// present.
template <typename T>
struct FilledMaybe {
  T value;
};

template <typename T, typename U>
bool operator==(const FilledMaybe<T>& lhs, const FilledMaybe<U>& rhs)
  requires requires(T t, U u) { t == u; }
{
  return lhs.value == rhs.value;
}

// Returns a gvariant::FilledMaybe that holds a const reference to value.
// (Whereas FilledMaybe{value} would hold a copy of value.) Useful to avoid
// making an extra copy when constructing a GVariantRef.
template <typename T>
FilledMaybe<const T&> FilledMaybeRef(const T& value LIFETIME_BOUND) {
  return {value};
}

// Represents an empty array of the given type.
template <Type C>
struct EmptyArrayOf {};

class ObjectPath;

// Holds an unowned pointer to a null-terminated string known to be a valid
// D-Bus object path.
class ObjectPathCStr {
 public:
  // Constructs from a compile-time constant. The passed string is checked at
  // compile time to be a valid object path.
  // Allows implicit construction so string constants can easily be passed to
  // ObjectPathCStr parameters.
  // NOLINTNEXTLINE(google-explicit-constructor)
  consteval ObjectPathCStr(const char* path);
  // Constructs from an ObjectPath object. The resulting ObjectPathCStr is only
  // valid as long as the ObjectPath to which it refers.
  // Allows explicit construction so ObjectPaths can easily be passed to
  // ObjectPathCStr parameters.
  // NOLINTNEXTLINE(google-explicit-constructor)
  ObjectPathCStr(const ObjectPath& path LIFETIME_BOUND);

  // Attempts to construct from an existing C string. Returns an error string
  // if |path| is not a valid object path.
  static base::expected<ObjectPathCStr, Loggable> TryFrom(
      const char* path LIFETIME_BOUND);

  // Gets the object path C string.
  constexpr const char* c_str() const LIFETIME_BOUND;

  friend constexpr bool operator==(const ObjectPathCStr& lhs,
                                   const ObjectPathCStr& rhs);

 private:
  struct Checked {};
  // Construct from already-checked path. Extra parameter to avoid conflicting
  // with consteval constructor.
  ObjectPathCStr(const char* path LIFETIME_BOUND, Checked);

  const char* path_;
};

constexpr bool operator==(const ObjectPathCStr& lhs, const ObjectPathCStr& rhs);

// Represents an owned D-Bus object path.
class ObjectPath {
 public:
  // Constructs an instance of the root path "/"
  ObjectPath();

  // Constructs an owned copy of |path|.
  explicit ObjectPath(ObjectPathCStr path);

  // Attempts to construct from an existing std::string. Returns an error string
  // if |path| is not a valid object path.
  static base::expected<ObjectPath, Loggable> TryFrom(std::string path);

  // Gets the object path.
  const std::string& value() const LIFETIME_BOUND;

  // Shorthand for .value().c_str()
  const char* c_str() const LIFETIME_BOUND;

 private:
  // Construct from already-checked path.
  explicit ObjectPath(std::string path);
  std::string path_;
  friend struct Mapping<ObjectPath>;
};

class TypeSignature;

// Holds an unowned pointer to a null-terminated string known to be a valid
// D-Bus type signature.
class TypeSignatureCStr {
 public:
  // Constructs from a compile-time constant. The passed string is checked at
  // compile time to be a valid type signature.
  // Allows implicit construction so string constants can easily be passed to
  // TypeSignatureCStr parameters.
  // NOLINTNEXTLINE(google-explicit-constructor)
  consteval TypeSignatureCStr(const char* signature);

  // Constructs from a TypeSignature object. The resulting TypeSignatureCStr is
  // only valid as long as the TypeSignature to which it refers.
  // Allows implicit construction so TypeSignatures can easily be passed to
  // TypeSignatureCStr parameters.
  // NOLINTNEXTLINE(google-explicit-constructor)
  TypeSignatureCStr(const TypeSignature& signature LIFETIME_BOUND);

  // Attempts to construct from an existing C string. Returns an error if
  // |signature| is not a valid type signature.
  static base::expected<TypeSignatureCStr, Loggable> TryFrom(
      const char* signature LIFETIME_BOUND);

  // Gets the type signature C string.
  constexpr const char* c_str() const LIFETIME_BOUND;

  friend constexpr bool operator==(const TypeSignatureCStr& lhs,
                                   const TypeSignatureCStr& rhs);

 private:
  struct Checked {};
  // Construct from already-checked path. Extra parameter to avoid conflicting
  // with consteval constructor.
  TypeSignatureCStr(const char* signature LIFETIME_BOUND, Checked);

  const char* signature_;
};

constexpr bool operator==(const TypeSignatureCStr& lhs,
                          const TypeSignatureCStr& rhs);

// Represents an owned D-Bus type signature.
class TypeSignature {
 public:
  // Constructs an empty type signature.
  TypeSignature();

  // Constructs an owned copy of |signature|.
  explicit TypeSignature(TypeSignatureCStr signature);

  // Attempts to construct from an existing std::string. Returns an error if
  // |signature| is not a valid type signature.
  static base::expected<TypeSignature, Loggable> TryFrom(std::string signature);

  // Gets the type signature.
  const std::string& value() const LIFETIME_BOUND;

  // Shorthand for .value().c_str()
  const char* c_str() const LIFETIME_BOUND;

 private:
  // Construct from already-checked signature.
  explicit TypeSignature(std::string);
  std::string signature_;
  friend struct Mapping<TypeSignature>;
};

// Iterator type for a container GVariant
template <Type C>
class Iterator;

template <Type C>
Iterator<C> operator+(std::ptrdiff_t n, const Iterator<C>& iter);

template <Type C>
class Iterator {
 public:
  Iterator() = default;
  // Copyable and movable
  Iterator(const Iterator& other) = default;
  Iterator(Iterator&& other) = default;
  Iterator& operator=(const Iterator& other) = default;
  Iterator& operator=(Iterator&& other) = default;

  // Iterator interface

  // LegacyForwardIterator requires reference_type be &value_type or
  // const &value_type, so this implementation is only a LegacyInputIterator.
  using iterator_category = std::input_iterator_tag;

  // The std::forward_iterator and higher concepts impose no such requirement,
  // so this can be a full random-access iterator.
  using iterator_concept = std::random_access_iterator_tag;

  using value_type = GVariantRef<C>;
  using reference_type = GVariantRef<C>;
  using difference_type = std::ptrdiff_t;

  // input iterator
  value_type operator*() const;
  Iterator& operator++();
  Iterator operator++(int);

  // forward iterator
  bool operator==(const Iterator& other) const;

  // bidirectional iterator
  Iterator& operator--();
  Iterator operator--(int);

  // random access iterator
  std::partial_ordering operator<=>(const Iterator& other) const;
  difference_type operator-(const Iterator& other) const;
  Iterator operator+(difference_type n) const;
  friend Iterator operator+ <C>(difference_type n, const Iterator& iter);
  Iterator operator-(difference_type n) const;
  Iterator& operator+=(difference_type n);
  Iterator& operator-=(difference_type n);
  value_type operator[](difference_type i) const;

 private:
  Iterator(GVariantRef<> variant, std::size_t i);
  std::size_t i_ = 0;
  GVariantRef<> variant_;

  template <Type D>
  friend class GVariantRef;
};

// GVariantRef implementation

template <Type C>
template <Type D>
GVariantRef<C>::GVariantRef(const GVariantRef<D>& other)
  requires(D.IsSubtypeOf(C))
    : GVariantBase(other) {}

template <Type C>
template <Type D>
GVariantRef<C>::GVariantRef(GVariantRef<D>&& other)
  requires(D.IsSubtypeOf(C))
    : GVariantBase(std::move(other)) {}

// static
template <Type C>
template <typename T>
GVariantRef<C> GVariantRef<C>::From(const T& value)
  requires(Mapping<T>::kType.IsSubtypeOf(C) &&
           requires { Mapping<T>::From(value); })
{
  return Mapping<T>::From(value);
}

// static
template <Type C>
template <typename T>
base::expected<GVariantRef<C>, Loggable> GVariantRef<C>::TryFrom(
    const T& value)
  requires(Mapping<T>::kType.HasCommonTypeWith(C) &&
           (requires { Mapping<T>::TryFrom(value); } ||
            requires { Mapping<T>::From(value); }))
{
  if constexpr (requires { Mapping<T>::TryFrom(value); }) {
    return Mapping<T>::TryFrom(value).and_then(
        [](auto value) { return value.template TryInto<GVariantRef>(); });
  } else {
    return Mapping<T>::From(value).template TryInto<GVariantRef>();
  }
}

template <Type C>
template <typename T>
T GVariantRef<C>::Into() const
  requires(C.IsSubtypeOf(Mapping<T>::kType) &&
           requires { Mapping<T>::Into(*this); })
{
  return Mapping<T>::Into(*this);
}

template <Type C>
template <typename T>
base::expected<T, Loggable> GVariantRef<C>::TryInto() const
  requires(C.HasCommonTypeWith(Mapping<T>::kType) &&
           (requires(GVariantRef<Mapping<T>::kType> v) {
              Mapping<T>::TryInto(v);
            } ||
            requires(GVariantRef<Mapping<T>::kType> v) {
              Mapping<T>::Into(v);
            }))
{
  if constexpr (!C.IsSubtypeOf(Mapping<T>::kType)) {
    if (GetType().IsSubtypeOf(Mapping<T>::kType)) {
      return GVariantRef<Mapping<T>::kType>::RefUnchecked(raw())
          .template TryInto<T>();
    } else {
      return base::unexpected(Loggable(
          FROM_HERE,
          base::StrCat({"Expected type: ", Mapping<T>::kType.string_view(),
                        " Found: ", GetType().string_view()})));
    }
  } else if constexpr (requires { Mapping<T>::TryInto(*this); }) {
    return Mapping<T>::TryInto(*this);
  } else {
    return base::ok(Mapping<T>::Into(*this));
  }
}

template <Type C>
template <typename... Types>
void GVariantRef<C>::Destructure(Types&&... refs) const {
  static_assert((... && (std::is_lvalue_reference_v<Types> ||
                         requires { std::tuple_size_v<Types>; })));
  constexpr auto contained_types = TypeBase::Unpack<C>();
  static_assert(std::tuple_size_v<decltype(contained_types)> == sizeof...(refs),
                "Incorrect number of elements.");
  [&]<std::size_t... Is>(std::index_sequence<Is...>) {
    (
        [&]() {
          auto inner_variant =
              GVariantRef<std::get<Is>(contained_types)>::TakeUnchecked(
                  g_variant_get_child_value(raw(), Is));
          if constexpr (std::is_lvalue_reference_v<Types>) {
            refs =
                inner_variant.template Into<std::remove_reference_t<Types>>();
          } else {
            std::apply(
                [&]<typename... Ts>(Ts&&... subref) {
                  return inner_variant.Destructure(std::forward<Ts>(subref)...);
                },
                std::forward<Types>(refs));
          }
        }(),
        ...);
  }(std::index_sequence_for<Types...>());
}

template <Type C>
template <typename... Types>
base::expected<void, Loggable> GVariantRef<C>::TryDestructure(
    Types&&... refs) const {
  static_assert((... && (std::is_lvalue_reference_v<Types> ||
                         requires { std::tuple_size_v<Types>; })));

  if (!g_variant_is_container(raw())) {
    return base::unexpected(
        Loggable(FROM_HERE, "Destructured GVariant is not a container."));
  }

  if (std::size_t size = g_variant_n_children(raw()); size != sizeof...(refs)) {
    return base::unexpected(Loggable(
        FROM_HERE, base::StringPrintf(
                       "Incorrect number of elements. Expected: %zd Found: %zd",
                       sizeof...(refs), size)));
  }

  base::expected<void, Loggable> result;

  [&]<std::size_t... Is>(std::index_sequence<Is...>) {
    ([&]() {
      GVariantRef<> inner_variant =
          GVariantRef<>::Take(g_variant_get_child_value(raw(), Is));
      if constexpr (std::is_lvalue_reference_v<Types>) {
        auto value = inner_variant.TryInto<std::remove_reference_t<Types>>();
        if (!value.has_value()) {
          result = std::move(value).error().UnexpectedWithContext(
              FROM_HERE, "While destructuring container");
          return false;
        }
        refs = value.value();
      } else {
        auto nested_result = std::apply(
            [&]<typename... Ts>(Ts&&... subref) {
              return inner_variant.TryDestructure(std::forward<Ts>(subref)...);
            },
            std::forward<Types>(refs));
        if (!nested_result.has_value()) {
          result = std::move(nested_result);
          return false;
        }
      }
      return true;
    }() &&
     ...);
  }(std::index_sequence_for<Types...>());

  return result;
}

template <Type C>
auto GVariantRef<C>::begin() const
  requires(C.IsContainer())
{
  return Iterator<TypeBase::ContainedType<C>()>(*this, 0);
}

template <Type C>
auto GVariantRef<C>::end() const
  requires(C.IsContainer())
{
  return Iterator<TypeBase::ContainedType<C>()>(*this,
                                                g_variant_n_children(raw()));
}

template <Type C>
std::size_t GVariantRef<C>::size() const
  requires(C.IsContainer())
{
  return g_variant_n_children(raw());
}

template <Type C>
template <std::size_t I>
auto GVariantRef<C>::get() const
  requires(C.IsFixedSizeContainer())
{
  return GVariantRef<std::get<I>(TypeBase::Unpack<C>())>::TakeUnchecked(
      g_variant_get_child_value(raw(), I));
}

// Define free version as well for generic code that calls get(tuple_like) using
// argument-dependent lookup.
template <std::size_t I, Type C>
  requires(C.IsFixedSizeContainer())
auto get(const GVariantRef<C>& variant) {
  return variant.template get<I>();
}

template <Type C>
template <typename T>
auto GVariantRef<C>::LookUp(const T& needle)
  requires(C.IsSubtypeOf(Type("a{?*}")) &&
           requires {
             decltype((*begin()).template get<0>())::TryFrom(needle);
           })
{
  using KeyType = decltype((*begin()).template get<0>());
  using ValueType = decltype((*begin()).template get<1>());
  auto needle_variant = KeyType::TryFrom(needle);
  if (!needle_variant.has_value()) {
    // If the value is something that can't be converted to a GVariant (e.g.,
    // a non-UTF-8 string), it's probably safe to say it's not in the
    // dictionary.
    return std::optional<ValueType>();
  }
  for (auto [key, value] : *this) {
    if (key == needle_variant.value()) {
      return std::optional<ValueType>(std::move(value));
    }
  }
  return std::optional<ValueType>();
}

template <Type C>
std::string_view GVariantRef<C>::string_view() const
  requires(C.IsStringType())
{
  gsize length;
  const char* string = g_variant_get_string(raw(), &length);
  return std::string_view(string, length);
}

template <Type C>
const char* GVariantRef<C>::c_string() const
  requires(C.IsStringType())
{
  return g_variant_get_string(raw(), nullptr);
}

// static
template <Type C>
GVariantRef<C> GVariantRef<C>::TakeUnchecked(GVariant* variant) {
  DCHECK(g_variant_is_of_type(variant, C.gvariant_type()));
  return GVariantRef<C>(GVariantPtr::Take(variant));
}

// static
template <Type C>
GVariantRef<C> GVariantRef<C>::RefUnchecked(GVariant* variant) {
  DCHECK(g_variant_is_of_type(variant, C.gvariant_type()));
  return GVariantRef<C>(GVariantPtr::Ref(variant));
}

// static
template <Type C>
GVariantRef<C> GVariantRef<C>::RefSinkUnchecked(GVariant* variant) {
  DCHECK(g_variant_is_of_type(variant, C.gvariant_type()));
  return GVariantRef<C>(GVariantPtr::RefSink(variant));
}

// Wrapper implementation

consteval ObjectPathCStr::ObjectPathCStr(const char* path) {
  // SAFETY: Since this constructor is consteval, it can only execute at compile
  // time. Thus, a read past the end triggered by a missing null terminator will
  // result in a compile-time error, with no risk at runtime.

  // TODO: bug 400761089 - Remove UNSAFE_BUFFERS annotations when Clang no
  // longer flags consteval code.

  // CHECKs cannot actually print messages at compile time, but a failed check
  // will trigger a compiler error pointing to the failed check, allowing the
  // message to be seen in the source code.
  CHECK_EQ(*path, '/') << "Path must start with a '/'";
  const char* prev_char = path;
  const char* current_char = UNSAFE_BUFFERS(path + 1);
  while (*current_char != '\0') {
    CHECK((*current_char >= 'A' && *current_char <= 'Z') ||
          (*current_char >= 'a' && *current_char <= 'z') ||
          (*current_char >= '0' && *current_char <= '9') ||
          *current_char == '_' || *current_char == '/')
        << "Path contains invalid character";
    CHECK(*prev_char != '/' || *current_char != '/')
        << "Two '/' characters may not appear in a row";
    UNSAFE_BUFFERS(++prev_char);
    UNSAFE_BUFFERS(++current_char;)
  }
  CHECK(*prev_char != '/' || prev_char == path)
      << "Path may not end in '/' unless the whole path is only a single '/' "
         "referring to the root path";
  path_ = path;
}

constexpr const char* ObjectPathCStr::c_str() const {
  return path_;
}

constexpr bool operator==(const ObjectPathCStr& lhs,
                          const ObjectPathCStr& rhs) {
  if (std::is_constant_evaluated()) {
    return std::string_view(lhs.c_str()) == std::string_view(rhs.c_str());
  } else {
    return UNSAFE_TODO(std::strcmp(lhs.c_str(), rhs.c_str())) == 0;
  }
}

consteval TypeSignatureCStr::TypeSignatureCStr(const char* signature) {
  // SAFETY: Since this constructor is consteval, it can only execute at compile
  // time. Thus, a read past the end triggered by a missing null terminator will
  // result in a compile-time error, with no risk at runtime.

  // TODO: bug 400761089 - Remove UNSAFE_BUFFERS annotations when Clang no
  // longer flags consteval code.

  // CHECKs cannot actually print messages at compile time, but a failed check
  // will trigger a compiler error pointing to the failed check, allowing the
  // message to be seen in the source code.
  CHECK(Type("(", signature, ")").IsValid()) << "Not a valid signature";
  CHECK(Type("(", signature, ")").IsDefinite()) << "Signature must be definite";
  char prev_char = '\0';
  for (const char* current_char = signature; *current_char != '\0';
       UNSAFE_BUFFERS(++current_char)) {
    CHECK(*current_char != 'm') << "Maybe type not valid in D-Bus signature";
    CHECK(*current_char != '{' || prev_char == 'a')
        << "Dict entry not part of a dictionary.";
    prev_char = *current_char;
  }
  signature_ = signature;
}

constexpr const char* TypeSignatureCStr::c_str() const {
  return signature_;
}

constexpr bool operator==(const TypeSignatureCStr& lhs,
                          const TypeSignatureCStr& rhs) {
  if (std::is_constant_evaluated()) {
    return std::string_view(lhs.c_str()) == std::string_view(rhs.c_str());
  } else {
    return UNSAFE_TODO(std::strcmp(lhs.c_str(), rhs.c_str())) == 0;
  }
}

// Iterator implementation

template <Type C>
GVariantRef<C> Iterator<C>::operator*() const {
  return GVariantRef<C>::TakeUnchecked(
      g_variant_get_child_value(variant_.raw(), i_));
}

template <Type C>
Iterator<C>& Iterator<C>::operator++() {
  ++i_;
  return *this;
}

template <Type C>
Iterator<C> Iterator<C>::operator++(int) {
  return Iterator(variant_, i_++);
}

template <Type C>
bool Iterator<C>::operator==(const Iterator& other) const {
  return variant_.raw() == other.variant_.raw() && i_ == other.i_;
}

template <Type C>
Iterator<C>& Iterator<C>::operator--() {
  --i_;
  return *this;
}

template <Type C>
Iterator<C> Iterator<C>::operator--(int) {
  return Iterator(variant_, i_--);
}

template <Type C>
std::partial_ordering Iterator<C>::operator<=>(const Iterator& other) const {
  if (variant_.raw() != other.variant_.raw()) {
    return std::partial_ordering::unordered;
  }
  return i_ <=> other.i_;
}

template <Type C>
std::ptrdiff_t Iterator<C>::operator-(const Iterator& other) const {
  return static_cast<std::ptrdiff_t>(i_) -
         static_cast<std::ptrdiff_t>(other.i_);
}

template <Type C>
Iterator<C> Iterator<C>::operator+(std::ptrdiff_t n) const {
  return Iterator(variant_, static_cast<std::ptrdiff_t>(i_) + n);
}

template <Type C>
Iterator<C> operator+(std::ptrdiff_t n, const Iterator<C>& iter) {
  return Iterator<C>(iter.variant_, n + static_cast<std::ptrdiff_t>(iter.i_));
}

template <Type C>
Iterator<C> Iterator<C>::operator-(std::ptrdiff_t n) const {
  return Iterator(variant_, static_cast<std::ptrdiff_t>(i_) - n);
}

template <Type C>
Iterator<C>& Iterator<C>::operator+=(std::ptrdiff_t n) {
  i_ = static_cast<std::size_t>(static_cast<std::ptrdiff_t>(i_) + n);
  return *this;
}

template <Type C>
Iterator<C>& Iterator<C>::operator-=(std::ptrdiff_t n) {
  i_ = static_cast<std::size_t>(static_cast<std::ptrdiff_t>(i_) - n);
  return *this;
}

template <Type C>
GVariantRef<C> Iterator<C>::operator[](std::ptrdiff_t i) const {
  return GVariantRef<C>::TakeUnchecked(g_variant_get_child_value(
      variant_.raw(),
      static_cast<std::size_t>(static_cast<std::ptrdiff_t>(i_) + i)));
}

template <Type C>
Iterator<C>::Iterator(GVariantRef<> variant, std::size_t i)
    : i_(i), variant_(variant) {}

// Mapping implementations

// Possibly cv-qualified reference can be used with *From() but not *Into().
template <typename T>
  requires(!std::same_as<T, std::decay_t<T>>)
struct Mapping<T> {
  static constexpr Type kType = Mapping<std::decay_t<const T&>>::kType;

  static auto From(const T& value)
      // Typically one wouldn't want a requires clause that just mirrors the
      // function body. Unfortunately, it is necessary to allow other generic
      // mappings to detect when From is absent, since requires expressions only
      // care about what is valid according to the declaration, not whether the
      // resulting instantiation would actually compile.
    requires(requires {
      GVariantRef<kType>::template From<std::decay_t<const T&>>(value);
    })
  {
    return GVariantRef<kType>::template From<std::decay_t<const T&>>(value);
  }

  static auto TryFrom(const T& value)
    requires(requires {
      GVariantRef<kType>::template TryFrom<std::decay_t<const T&>>(value);
    })
  {
    return GVariantRef<kType>::template TryFrom<std::decay_t<const T&>>(value);
  }
};

// Basic fixed values.

template <>
struct Mapping<bool> {
  static constexpr Type kType{"b"};
  static GVariantRef<kType> From(bool value);
  static bool Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<std::uint8_t> {
  static constexpr Type kType{"y"};
  static GVariantRef<kType> From(std::uint8_t value);
  static std::uint8_t Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<std::int16_t> {
  static constexpr Type kType{"n"};
  static GVariantRef<kType> From(std::int16_t value);
  static std::int16_t Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<std::uint16_t> {
  static constexpr Type kType{"q"};
  static GVariantRef<kType> From(std::uint16_t value);
  static std::uint16_t Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<std::int32_t> {
  static constexpr Type kType{"i"};
  static GVariantRef<kType> From(std::int32_t value);
  static std::int32_t Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<std::uint32_t> {
  static constexpr Type kType{"u"};
  static GVariantRef<kType> From(std::uint32_t value);
  static std::uint32_t Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<std::int64_t> {
  static constexpr Type kType{"x"};
  static GVariantRef<kType> From(std::int64_t value);
  static std::int64_t Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<std::uint64_t> {
  static constexpr Type kType{"t"};
  static GVariantRef<kType> From(std::uint64_t value);
  static std::uint64_t Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<double> {
  static constexpr Type kType{"d"};
  static GVariantRef<kType> From(double value);
  static double Into(const GVariantRef<kType>& variant);
};

// Strings.

template <>
struct Mapping<std::string> {
  static constexpr Type kType{"s"};
  // Crashes if string is not valid UTF-8.
  static GVariantRef<kType> From(const std::string& value);
  // Fails if string is not valid UTF-8.
  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const std::string& value);
  static std::string Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<std::string_view> {
  static constexpr Type kType{"s"};
  // Crashes if string is not valid UTF-8.
  static GVariantRef<kType> From(std::string_view value);
  // Fails if string is not valid UTF-8.
  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      std::string_view value);
};

template <>
struct Mapping<const char*> {
  static constexpr Type kType{"s"};
  // Crashes if string is not valid UTF-8.
  static GVariantRef<kType> From(const char* value);
  // Fails if string is not valid UTF-8.
  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const char* value);
};

// Containers

template <typename T>
struct Mapping<std::optional<T>> {
  static constexpr Type kInnerType = Mapping<T>::kType;
  static constexpr Type kType{"m", kInnerType};

  static GVariantRef<kType> From(const std::optional<T>& value)
    requires(kInnerType.IsDefinite() &&
             requires(T v) { GVariantRef<kInnerType>::From(v); })
  {
    std::optional<GVariantRef<kInnerType>> variant;
    GVariant* child = nullptr;
    if (value) {
      variant = GVariantRef<kInnerType>::From(*value);
      child = variant->raw();
    }
    return GVariantRef<kType>::TakeUnchecked(
        g_variant_new_maybe(kInnerType.gvariant_type(), child));
  }

  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const std::optional<T>& value)
    requires(requires(T v) { GVariantRef<kInnerType>::TryFrom(v); })
  {
    if (value.has_value()) {
      auto result = GVariantRef<kInnerType>::TryFrom(*value);
      if (!result.has_value()) {
        return base::unexpected(std::move(result).error());
      }
      return base::ok(GVariantRef<kType>::From(FilledMaybe{result.value()}));
    } else if constexpr (kInnerType.IsDefinite()) {
      return base::ok(
          GVariantRef<kType>::From(std::optional<GVariantRef<kInnerType>>()));
    } else {
      return base::unexpected(Loggable(
          FROM_HERE, "Can't convert indefinite optional with no value."));
    }
  }

  static std::optional<T> Into(const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<kInnerType> v) { v.template Into<T>(); })
  {
    GVariant* child = g_variant_get_maybe(variant.raw());
    if (child) {
      return GVariantRef<kInnerType>::TakeUnchecked(child).template Into<T>();
    } else {
      return std::nullopt;
    }
  }

  static base::expected<std::optional<T>, Loggable> TryInto(
      const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<kInnerType> v) { v.template TryInto<T>(); })
  {
    auto optional =
        variant.template Into<std::optional<GVariantRef<kInnerType>>>();

    if (optional.has_value()) {
      return optional->template TryInto<T>();
    } else {
      return base::ok(std::nullopt);
    }
  }
};

namespace internal {
template <Type kType, Type kInnerType, typename R>
GVariantRef<kType> FromRange(const R& value)
  requires(kInnerType.IsDefinite())
{
  GVariantBuilder builder;
  g_variant_builder_init(&builder, kType.gvariant_type());
  for (const auto& item : value) {
    g_variant_builder_add_value(&builder,
                                GVariantRef<kInnerType>::From(item).raw());
  }
  return GVariantRef<kType>::TakeUnchecked(g_variant_builder_end(&builder));
}

template <Type kType, Type kInnerType, typename R>
static base::expected<GVariantRef<kType>, Loggable> TryFromRange(
    const R& value) {
  if (!kInnerType.IsDefinite() && value.empty()) {
    return base::unexpected(
        Loggable(FROM_HERE, "Can't convert empty indefinite array"));
  }

  std::optional<Type<>> inner_type;
  GVariantBuilder builder;
  g_variant_builder_init(&builder, kType.gvariant_type());
  for (const auto& item : value) {
    auto converted = GVariantRef<kInnerType>::TryFrom(item);
    if (!converted.has_value()) {
      g_variant_builder_clear(&builder);
      return base::unexpected(std::move(converted).error());
    }
    if constexpr (!kInnerType.IsDefinite()) {
      if (!inner_type.has_value()) {
        inner_type = converted->GetType();
      } else if (!converted->GetType().IsSubtypeOf(inner_type.value())) {
        g_variant_builder_clear(&builder);
        return base::unexpected(
            Loggable(FROM_HERE, "Mismatched types in array"));
      }
    }
    g_variant_builder_add_value(&builder, converted->raw());
  }
  return base::ok(
      GVariantRef<kType>::TakeUnchecked(g_variant_builder_end(&builder)));
}
}  // namespace internal

// If needed, a further specialization could be added for vectors and
// contiguous ranges of fixed basic types (bools, bytes, ints, and doubles)
// to use g_variant_{new,get}_fixed_array() rather than processing each
// element individually. This would make handling, e.g., large blobs of binary
// data much more efficient.

template <typename T>
struct Mapping<std::vector<T>> {
  static constexpr Type kInnerType = Mapping<T>::kType;
  static constexpr Type kType{"a", kInnerType};

  static GVariantRef<kType> From(const std::vector<T>& value)
    requires(kInnerType.IsDefinite() &&
             requires(T v) { GVariantRef<kInnerType>::From(v); })
  {
    return internal::FromRange<kType, kInnerType>(value);
  }

  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const std::vector<T>& value)
    requires(requires(T v) { GVariantRef<kInnerType>::TryFrom(v); })
  {
    return internal::TryFromRange<kType, kInnerType>(value);
  }

  static std::vector<T> Into(const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<kInnerType> v) { v.template Into<T>(); })
  {
    std::vector<T> result;
    GVariantIter iter;
    g_variant_iter_init(&iter, variant.raw());
    result.reserve(g_variant_iter_n_children(&iter));
    while (GVariant* next = g_variant_iter_next_value(&iter)) {
      auto item_gvariant = GVariantRef<kInnerType>::TakeUnchecked(next);
      result.push_back(item_gvariant.template Into<T>());
    }
    return result;
  }

  static base::expected<std::vector<T>, Loggable> TryInto(
      const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<kInnerType> v) { v.template TryInto<T>(); })
  {
    std::vector<T> result;
    GVariantIter iter;
    g_variant_iter_init(&iter, variant.raw());
    result.reserve(g_variant_iter_n_children(&iter));
    while (GVariant* next = g_variant_iter_next_value(&iter)) {
      auto item_gvariant = GVariantRef<>::Take(next);
      auto item_result = item_gvariant.TryInto<T>();
      if (item_result.has_value()) {
        result.push_back(std::move(item_result).value());
      } else {
        return base::unexpected(std::move(item_result).error());
      }
    }
    return result;
  }
};

template <typename K, typename T>
  requires(Mapping<K>::kType.IsBasic())
struct Mapping<std::map<K, T>> {
  static constexpr Type kKeyType = Mapping<K>::kType;
  static constexpr Type kValueType = Mapping<T>::kType;
  static constexpr Type kInnerType{"{", kKeyType, kValueType, "}"};
  static constexpr Type kType{"a", kInnerType};

  static GVariantRef<kType> From(const std::map<K, T>& value)
    requires(kInnerType.IsDefinite() &&
             requires(std::pair<K, T> v) { GVariantRef<kInnerType>::From(v); })
  {
    return internal::FromRange<kType, kInnerType>(value);
  }

  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const std::map<K, T>& value)
    requires(requires(std::pair<K, T> v) {
      GVariantRef<kInnerType>::TryFrom(v);
    })
  {
    return internal::TryFromRange<kType, kInnerType>(value);
  }

  static std::map<K, T> Into(const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<kInnerType> v) {
      v.template Into<std::pair<K, T>>();
    })
  {
    std::map<K, T> result;
    GVariantIter iter;
    g_variant_iter_init(&iter, variant.raw());
    while (GVariant* next = g_variant_iter_next_value(&iter)) {
      auto item_gvariant = GVariantRef<kInnerType>::TakeUnchecked(next);
      result.insert(item_gvariant.template Into<std::pair<K, T>>());
    }
    return result;
  }

  static base::expected<std::map<K, T>, Loggable> TryInto(
      const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<kInnerType> v) {
      v.template TryInto<std::pair<K, T>>();
    })
  {
    std::map<K, T> result;
    GVariantIter iter;
    g_variant_iter_init(&iter, variant.raw());
    while (GVariant* next = g_variant_iter_next_value(&iter)) {
      auto item_gvariant = GVariantRef<>::Take(next);
      auto item_result = item_gvariant.TryInto<std::pair<K, T>>();
      if (item_result.has_value()) {
        result.insert(std::move(item_result).value());
      } else {
        return base::unexpected(std::move(item_result).error());
      }
    }
    return result;
  }
};

template <typename K, typename T>
  requires(Mapping<K>::kType.IsBasic())
struct Mapping<std::pair<K, T>> {
  static constexpr Type kKeyType = Mapping<K>::kType;
  static constexpr Type kValueType = Mapping<T>::kType;
  static constexpr Type kType{"{", kKeyType, kValueType, "}"};

  static GVariantRef<kType> From(const std::pair<K, T>& pair)
    requires(requires(K k, T v) {
      GVariantRef<kKeyType>::From(k);
      GVariantRef<kValueType>::From(v);
    })
  {
    auto key = GVariantRef<kKeyType>::From(pair.first);
    auto value = GVariantRef<kValueType>::From(pair.second);
    return GVariantRef<kType>::TakeUnchecked(
        g_variant_new_dict_entry(key.raw(), value.raw()));
  }

  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const std::pair<K, T>& pair)
    requires(requires(K k, T v) {
      GVariantRef<kKeyType>::TryFrom(k);
      GVariantRef<kValueType>::TryFrom(v);
    })
  {
    auto key = GVariantRef<kKeyType>::TryFrom(pair.first);
    if (!key.has_value()) {
      return base::unexpected(std::move(key).error());
    }
    auto value = GVariantRef<kValueType>::TryFrom(pair.second);
    if (!value.has_value()) {
      return base::unexpected(std::move(value).error());
    }
    return GVariantRef<kType>::From(std::pair(key.value(), value.value()));
  }

  static std::pair<K, T> Into(const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<kKeyType> k, GVariantRef<kValueType> v) {
      k.template Into<K>();
      v.template Into<T>();
    })
  {
    GVariantIter iter;
    g_variant_iter_init(&iter, variant.raw());
    auto key_gvariant =
        GVariantRef<kKeyType>::TakeUnchecked(g_variant_iter_next_value(&iter));
    auto value_gvariant = GVariantRef<kValueType>::TakeUnchecked(
        g_variant_iter_next_value(&iter));
    return std::pair(key_gvariant.template Into<K>(),
                     value_gvariant.template Into<T>());
  }

  static base::expected<std::pair<K, T>, Loggable> TryInto(
      const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<kKeyType> k, GVariantRef<kValueType> v) {
      k.template TryInto<K>();
      v.template TryInto<T>();
    })
  {
    auto gvariants = variant.template Into<
        std::pair<GVariantRef<kKeyType>, GVariantRef<kValueType>>>();
    auto key_result = gvariants.first.template TryInto<K>();
    if (!key_result.has_value()) {
      return base::unexpected(std::move(key_result).error());
    }
    auto value_result = gvariants.second.template TryInto<T>();
    if (!value_result.has_value()) {
      return base::unexpected(std::move(value_result).error());
    }
    return std::pair(std::move(key_result).value(),
                     std::move(value_result).value());
  }
};

template <typename R>
// If the type can decay, let that happen first to avoid ambiguities.
// E.g., a std::vector<double>& could either be used as a range directly or
// decayed into a std::vector<double>. Resolving in favor of decay is
// desirable so that string constants decay into a const char* and are treated
// as a C string rather than a range of chars.
  requires(std::ranges::range<R> && std::same_as<R, std::decay_t<R>>)
struct Mapping<R> {
  static constexpr Type kInnerType =
      Mapping<std::ranges::range_value_t<R>>::kType;
  static constexpr Type kType{"a", kInnerType};

  static GVariantRef<kType> From(const R& value)
    requires(kInnerType.IsDefinite() &&
             requires(std::ranges::range_value_t<R> v) {
               GVariantRef<kInnerType>::From(v);
             })
  {
    return internal::FromRange<kType, kInnerType>(value);
  }

  static base::expected<GVariantRef<kType>, Loggable> TryFrom(const R& value)
    requires(requires(std::ranges::range_value_t<R> v) {
      GVariantRef<kInnerType>::TryFrom(v);
    })
  {
    return internal::TryFromRange<kType, kInnerType>(value);
  }
};

template <typename... Types>
struct Mapping<std::tuple<Types...>> {
  static constexpr Type kType{"(", Mapping<Types>::kType..., ")"};

  static GVariantRef<kType> From(const std::tuple<Types...>& value)
    requires(requires(Types... v) {
      (GVariantRef<Mapping<Types>::kType>::From(v), ...);
    })
  {
    GVariantBuilder builder;
    g_variant_builder_init(&builder, kType.gvariant_type());

    std::apply(
        [&](const Types&... values) {
          (g_variant_builder_add_value(
               &builder,
               GVariantRef<Mapping<Types>::kType>::From(values).raw()),
           ...);
        },
        value);

    return GVariantRef<kType>::TakeUnchecked(g_variant_builder_end(&builder));
  }

  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const std::tuple<Types...>& value)
    requires(requires(Types... v) {
      (GVariantRef<Mapping<Types>::kType>::TryFrom(v), ...);
    })
  {
    auto conversion_result = std::apply(
        [](const Types&... item) { return TupleTryFrom<Types...>(item...); },
        value);
    if (!conversion_result.has_value()) {
      return base::unexpected(std::move(conversion_result).error());
    }
    return GVariantRef<kType>::From(conversion_result.value());
  }

  static std::tuple<Types...> Into(const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<Mapping<Types>::kType>... v) {
      (v.template Into<Types>(), ...);
    })
  {
    GVariantIter iter;
    g_variant_iter_init(&iter, variant.raw());
    // Must use uniform-initialization syntax since (in contrast to
    // function-call syntax) it is specified to evalulate values in order.
    auto gvariant_items =
        std::tuple{GVariantRef<Mapping<Types>::kType>::TakeUnchecked(
            g_variant_iter_next_value(&iter))...};
    return std::apply(
        [](const GVariantRef<Mapping<Types>::kType>&... items) {
          return std::tuple(items.template Into<Types>()...);
        },
        gvariant_items);
  }

  static base::expected<std::tuple<Types...>, Loggable> TryInto(
      const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<Mapping<Types>::kType>... v) {
      (v.template TryInto<Types>(), ...);
    })
  {
    auto gvariant_items =
        variant
            .template Into<std::tuple<GVariantRef<Mapping<Types>::kType>...>>();
    return std::apply(
        [](const GVariantRef<Mapping<Types>::kType>&... items) {
          return TupleTryInto<Types...>(items...);
        },
        gvariant_items);
  }

 private:
  // Attempt to turn a tuple of Ts into a tuple of GVariantRefs
  template <typename T = void>
  static base::expected<std::tuple<>, Loggable> TupleTryFrom() {
    return base::ok(std::tuple());
  }

  template <typename T, typename... Ts>
  static base::expected<std::tuple<GVariantRef<Mapping<T>::kType>,
                                   GVariantRef<Mapping<Ts>::kType>...>,
                        Loggable>
  TupleTryFrom(const T& first, const Ts&... rest) {
    auto first_result = GVariantRef<Mapping<T>::kType>::TryFrom(first);
    if (!first_result.has_value()) {
      return base::unexpected(std::move(first_result).error());
    }
    auto rest_result = TupleTryFrom<Ts...>(rest...);
    if (!rest_result.has_value()) {
      return base::unexpected(std::move(first_result).error());
    }
    return std::tuple_cat(std::tuple(first_result.value()),
                          rest_result.value());
  }

  // Attempt to turn a tuple of GVariantRefs into a tuple of Ts
  template <typename T = void>
  static base::expected<std::tuple<>, Loggable> TupleTryInto() {
    return base::ok(std::tuple());
  }

  template <typename T, typename... Ts>
  static base::expected<std::tuple<T, Ts...>, Loggable> TupleTryInto(
      const GVariantRef<Mapping<T>::kType>& first,
      const GVariantRef<Mapping<Ts>::kType>&... rest) {
    auto first_result = first.template TryInto<T>();
    if (!first_result.has_value()) {
      return base::unexpected(std::move(first_result).error());
    }
    auto rest_result = TupleTryInto<Ts...>(rest...);
    if (!rest_result.has_value()) {
      return base::unexpected(std::move(first_result).error());
    }
    return std::tuple_cat(std::tuple(first_result.value()),
                          rest_result.value());
  }
};

template <typename... Types>
  requires(sizeof...(Types) > 0)
struct Mapping<std::variant<Types...>> {
  static constexpr Type kType =
      TypeBase::CommonSuperTypeOf<Mapping<Types>::kType...>();

  static GVariantRef<kType> From(const std::variant<Types...>& value)
    requires(requires(Types... v) { (GVariantRef<kType>::From(v), ...); })
  {
    return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
      std::optional<GVariantRef<kType>> result;
      ((std::ignore =
            value.index() == Is &&
            (result.emplace(GVariantRef<kType>::From(std::get<Is>(value))),
             false)),
       ...);
      return std::move(*result);
    }(std::index_sequence_for<Types...>());
  }

  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const std::variant<Types...>& value)
    requires(requires(Types... v) { (GVariantRef<kType>::TryFrom(v), ...); })
  {
    return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
      std::optional<base::expected<GVariantRef<kType>, Loggable>> result;
      ((std::ignore =
            value.index() == Is &&
            (result.emplace(GVariantRef<kType>::TryFrom(std::get<Is>(value))),
             false)),
       ...);
      return std::move(*result);
    }(std::index_sequence_for<Types...>());
  }

  template <Type C>
  static std::variant<Types...> Into(const GVariantRef<C>& variant)
      // Infallible Into is provided if at least one alternative provides an
      // infallible Into for the provided type.
    requires(... || requires { variant.template Into<Types>(); })
  {
    // Try TryInto() first so where possible Into and TryInto produce the same
    // variant.
    auto result = VariantTryInto<0, Types...>(variant);
    if (result.has_value()) {
      return std::move(result).value();
    }

    // To get here, one of the types must provide both a fallible and infallible
    // conversion, and the fallible version failed. Loop through again and call
    // the infallible version to get a value to return.
    return VariantInto<C, 0, Types...>(variant);
  }

  static base::expected<std::variant<Types...>, Loggable> TryInto(
      const GVariantRef<kType>& variant)
      // TryInto is only provided if it is provided by at least one alternative.
    requires(... || requires { variant.template TryInto<Types>(); })
  {
    return VariantTryInto<0, Types...>(variant);
  }

 private:
  template <std::size_t I>
  static base::expected<std::variant<Types...>, Loggable> VariantTryInto(
      const GVariantRef<kType>& variant) {
    return base::unexpected(
        Loggable(FROM_HERE, "No variant alternative could decode value"));
  }

  template <std::size_t I, typename T, typename... Ts>
  static base::expected<std::variant<Types...>, Loggable> VariantTryInto(
      const GVariantRef<kType>& variant) {
    if constexpr (requires { variant.template TryInto<T>(); }) {
      auto alternative_result = variant.template TryInto<T>();
      if (alternative_result.has_value()) {
        return base::ok(std::variant<Types...>(
            std::in_place_index<I>, std::move(alternative_result).value()));
      }
    }
    return VariantTryInto<I + 1, Ts...>(variant);
  }

  // No base case needed, as at least one type is guaranteed to provide an
  // infallible conversion.
  template <Type C, std::size_t I, typename T, typename... Ts>
  static std::variant<Types...> VariantInto(const GVariantRef<C>& variant) {
    if constexpr (requires { variant.template Into<T>(); }) {
      return std::variant<Types...>(std::in_place_index<I>,
                                    variant.template Into<T>());
    } else {
      return VariantInto<C, I + 1, Ts...>(variant);
    }
  }
};

// Special Types

template <Type C>
struct Mapping<GVariantRef<C>> {
  static constexpr Type kType = C;
  static GVariantRef<kType> From(GVariantRef<kType> value) { return value; }
  static GVariantRef<kType> Into(GVariantRef<kType> value) { return value; }
};

template <>
struct Mapping<Ignored> {
  static constexpr Type kType{"*"};
  static Ignored Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<decltype(std::ignore)> {
  static constexpr Type kType{"*"};
  static decltype(std::ignore) Into(const GVariantRef<kType>& variant);
};

template <typename T>
struct Mapping<Boxed<T>> {
  static constexpr Type kType{"v"};

  static GVariantRef<kType> From(const Boxed<T>& value)
    requires(requires(T v) { GVariantRef<>::From(v); })
  {
    return GVariantRef<kType>::TakeUnchecked(
        g_variant_new_variant(GVariantRef<>::From(value.value).raw()));
  }

  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const Boxed<T>& value)
    requires(requires(T v) { GVariantRef<>::TryFrom(v); })
  {
    auto result = GVariantRef<>::TryFrom(value.value);
    if (!result.has_value()) {
      return base::unexpected(std::move(result).error());
    }
    return base::ok(GVariantRef<kType>::From(Boxed{result.value()}));
  }

  static Boxed<T> Into(const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<> v) { v.Into<T>(); })
  {
    return Boxed{
        GVariantRef<>::Take(g_variant_get_variant(variant.raw())).Into<T>()};
  }

  static base::expected<Boxed<T>, Loggable> TryInto(
      const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<> v) { v.TryInto<T>(); })
  {
    return variant.Into<Boxed<GVariantRef<>>>().value.TryInto<T>().transform(
        [](auto v) { return Boxed{std::move(v)}; });
  }
};

template <typename T>
struct Mapping<FilledMaybe<T>> {
  static constexpr Type kInnerType = Mapping<T>::kType;
  static constexpr Type kType{"m", kInnerType};

  static GVariantRef<kType> From(const FilledMaybe<T>& value)
    requires(requires(T v) { GVariantRef<kInnerType>::From(v); })
  {
    return GVariantRef<kType>::TakeUnchecked(g_variant_new_maybe(
        nullptr, GVariantRef<kInnerType>::From(value.value).raw()));
  }

  static base::expected<GVariantRef<kType>, Loggable> TryFrom(
      const FilledMaybe<T>& value)
    requires(requires(T v) { GVariantRef<kInnerType>::TryFrom(v); })
  {
    auto result = GVariantRef<kInnerType>::TryFrom(value.value);
    if (!result.has_value()) {
      return base::unexpected(std::move(result).error());
    }
    return base::ok(GVariantRef<kType>::From(FilledMaybe{result.value()}));
  }

  static base::expected<FilledMaybe<T>, Loggable> TryInto(
      const GVariantRef<kType>& variant)
    requires(requires(GVariantRef<kInnerType> v) { v.template TryInto<T>(); })
  {
    GVariant* contents = g_variant_get_maybe(variant.raw());
    if (!contents) {
      return base::unexpected(
          Loggable(FROM_HERE, "Maybe value unexpectedly empty"));
    }

    return GVariantRef<kInnerType>::TakeUnchecked(contents)
        .template TryInto<T>()
        .transform([](auto v) { return FilledMaybe{std::move(v)}; });
  }
};

template <Type C>
struct Mapping<EmptyArrayOf<C>> {
  static constexpr Type kType{"a", C};

  static GVariantRef<kType> From(const EmptyArrayOf<C>& value)
    requires(C.IsDefinite())
  {
    return GVariantRef<kType>::TakeUnchecked(
        g_variant_new_array(C.gvariant_type(), nullptr, 0));
  }

  static base::expected<EmptyArrayOf<C>, Loggable> TryInto(
      const GVariantRef<kType>& variant) {
    if (auto size = g_variant_n_children(variant.raw()); size != 0) {
      return base::unexpected(
          Loggable(FROM_HERE, "Array unexpectedly not empty."));
    }

    return EmptyArrayOf<C>{};
  }
};

template <>
struct Mapping<ObjectPathCStr> {
  static constexpr Type kType{"o"};
  static GVariantRef<kType> From(const ObjectPathCStr& value);
};

template <>
struct Mapping<ObjectPath> {
  static constexpr Type kType{"o"};
  static GVariantRef<kType> From(const ObjectPath& value);
  static ObjectPath Into(const GVariantRef<kType>& variant);
};

template <>
struct Mapping<TypeSignatureCStr> {
  static constexpr Type kType{"g"};
  static GVariantRef<kType> From(const TypeSignatureCStr& value);
};

template <>
struct Mapping<TypeSignature> {
  static constexpr Type kType{"g"};
  static GVariantRef<kType> From(const TypeSignature& value);
  static TypeSignature Into(const GVariantRef<kType>& variant);
};

}  // namespace gvariant

using gvariant::GVariantFrom;
using gvariant::GVariantRef;

}  // namespace remoting

// Make tuple-like
template <remoting::gvariant::Type C>
  requires(C.IsFixedSizeContainer())
struct std::tuple_size<remoting::GVariantRef<C>>
    : public std::tuple_size<
          decltype(remoting::gvariant::TypeBase::Unpack<C>())> {};

template <std::size_t I, remoting::gvariant::Type C>
  requires(C.IsFixedSizeContainer())
struct std::tuple_element<I, remoting::GVariantRef<C>> {
  using type = remoting::GVariantRef<std::get<I>(
      remoting::gvariant::TypeBase::Unpack<C>())>;
};

#endif  // REMOTING_HOST_LINUX_GVARIANT_REF_H_