FTL: Allow Concat of bool and char

Also, allow references to integral types.

Bug: 185536303
Test: ftl_test
Change-Id: Ic9be008ed7f72ecdb7a369b01bc5a8235b35ac2c
diff --git a/include/ftl/concat.h b/include/ftl/concat.h
index ded48f7..e0774d3 100644
--- a/include/ftl/concat.h
+++ b/include/ftl/concat.h
@@ -20,7 +20,9 @@
 
 namespace android::ftl {
 
-// Lightweight (not allocating nor sprintf-based) concatenation.
+// Lightweight (not allocating nor sprintf-based) concatenation. The variadic arguments can be
+// values of integral type (including bool and char), string literals, or strings whose length
+// is constrained:
 //
 //   std::string_view name = "Volume";
 //   ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB");
diff --git a/include/ftl/details/concat.h b/include/ftl/details/concat.h
index 8ce949e..726ba02 100644
--- a/include/ftl/details/concat.h
+++ b/include/ftl/details/concat.h
@@ -19,6 +19,7 @@
 #include <functional>
 #include <string_view>
 
+#include <ftl/details/type_traits.h>
 #include <ftl/string.h>
 
 namespace android::ftl::details {
@@ -26,16 +27,42 @@
 template <typename T, typename = void>
 struct StaticString;
 
+// Booleans.
 template <typename T>
-struct StaticString<T, std::enable_if_t<std::is_integral_v<T>>> {
-  static constexpr std::size_t N = to_chars_length_v<T>;
+struct StaticString<T, std::enable_if_t<is_bool_v<T>>> {
+  static constexpr std::size_t N = 5;  // Length of "false".
 
-  explicit StaticString(T v) : view(to_chars(buffer, v)) {}
+  explicit constexpr StaticString(bool b) : view(b ? "true" : "false") {}
 
-  to_chars_buffer_t<T> buffer;
   const std::string_view view;
 };
 
+// Characters.
+template <typename T>
+struct StaticString<T, std::enable_if_t<is_char_v<T>>> {
+  static constexpr std::size_t N = 1;
+
+  explicit constexpr StaticString(char c) : character(c) {}
+
+  const char character;
+  const std::string_view view{&character, 1u};
+};
+
+// Integers, including the integer value of other character types like char32_t.
+template <typename T>
+struct StaticString<
+    T, std::enable_if_t<std::is_integral_v<remove_cvref_t<T>> && !is_bool_v<T> && !is_char_v<T>>> {
+  using U = remove_cvref_t<T>;
+  static constexpr std::size_t N = to_chars_length_v<U>;
+
+  // TODO: Mark this and to_chars as `constexpr` in C++23.
+  explicit StaticString(U v) : view(to_chars(buffer, v)) {}
+
+  to_chars_buffer_t<U> buffer;
+  const std::string_view view;
+};
+
+// Character arrays.
 template <std::size_t M>
 struct StaticString<const char (&)[M], void> {
   static constexpr std::size_t N = M - 1;
@@ -50,6 +77,7 @@
   std::string_view view;
 };
 
+// Strings with constrained length.
 template <std::size_t M>
 struct StaticString<Truncated<M>, void> {
   static constexpr std::size_t N = M;
diff --git a/include/ftl/details/type_traits.h b/include/ftl/details/type_traits.h
index 7092ec5..47bebc5 100644
--- a/include/ftl/details/type_traits.h
+++ b/include/ftl/details/type_traits.h
@@ -24,4 +24,10 @@
 template <typename U>
 using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
 
+template <typename T>
+constexpr bool is_bool_v = std::is_same_v<remove_cvref_t<T>, bool>;
+
+template <typename T>
+constexpr bool is_char_v = std::is_same_v<remove_cvref_t<T>, char>;
+
 }  // namespace android::ftl::details