Added format_string forwarding tests to color-test, Fixed #3536
Referring to #3536

I was not able to make the suggested change because the color functions must preserve the Char type of the basic_format_string, and this cannot be plumbed through all the way down those overloads due to how ubiquitous the `is_string`, `to_string_view` and `char_t` meta-functions are.

The only viable solution appeared to fix those meta-functions to work with basic_format_string, as it they were clearly intended to. The solution to that particular riddle (without casting too broad of an implicit conversion net) was to Sfinae-switch on the existence of the `.get()` function that `basic_format_string` has to retrieve the underlying `string_view`.

This PR does that and adds the tests for the color function. The added tests fail without this fix. And all other tests pass to.
{"label": "fmtlib:master", "ref": "master", "sha": "661b23edeb52d400cf5812e7330f14f05c072fab"}
I was writing a forwarding wrapper around fmt, something like this (prints "ERROR: " and makes the text red):

```
template <typename... Args>
void error(fmt::format_string<Args...> fmt_str, Args&&... args) {
    const fmt::text_style ts = fmt::emphasis::bold | fg(fmt::color::red);
    fmt::print(stderr, ts, "ERROR: ");
    fmt::print(stderr, ts, fmt_str, std::forward<Args>(args)...);
    fmt::print(stderr, "\n");
}
```

That results in the following errors:

```
In file included from common/logging_test.cc:2:
./common/logging.h:28:5: error: no matching function for call to 'print'
    fmt::print(stderr, ts, fmt_str, std::forward<Args>(args)...);
    ^~~~~~~~~~
common/logging_test.cc:35:33: note: in instantiation of function template specialization 'error<int>' requested here
          EXPECT_NO_MEMORY_ALLOCATIONS(error("foo {}", 42));
                                       ^
external/com_github_fmtlib_fmt/include/fmt/core.h:3293:17: note: candidate function [with T = <const fmt::text_style &, fmt::basic_format_string<char, int> &, int>] not viable: no known conversion from 'FILE *' (aka '_IO_FILE *') to 'format_string<const fmt::text_style &, fmt::basic_format_string<char, int> &, int>' (aka 'basic_format_string<char, const fmt::text_style &, fmt::basic_format_string<char, int> &, int>') for 1st argument
FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
                ^
external/com_github_fmtlib_fmt/include/fmt/core.h:3310:17: note: candidate function [with T = <fmt::basic_format_string<char, int> &, int>] not viable: no known conversion from 'const fmt::text_style' to 'format_string<fmt::basic_format_string<char, int> &, int>' (aka 'basic_format_string<char, fmt::basic_format_string<char, int> &, int>') for 2nd argument
FMT_INLINE void print(std::FILE* f, format_string<T...> fmt, T&&... args) {
                ^
external/com_github_fmtlib_fmt/include/fmt/color.h:508:6: note: candidate template ignored: requirement 'detail::is_string<fmt::basic_format_string<char, int>>::value' was not satisfied [with S = fmt::basic_format_string<char, int>, Args = <int>]
void print(std::FILE* f, const text_style& ts, const S& format_str,
     ^
external/com_github_fmtlib_fmt/include/fmt/color.h:527:6: note: candidate template ignored: requirement 'detail::is_string<fmt::text_style>::value' was not satisfied [with S = fmt::text_style, Args = <fmt::basic_format_string<char, int>, int>]
void print(const text_style& ts, const S& format_str, const Args&... args) {
     ^
```

The failure comes from the fact that the targeted overload (3rd candidate in the above error messages, taking `FILE*`, `const text_style&` and `const S&`) is ignored (by Sfinae) because `fmt::basic_format_string` is not a string (as checked by `detail::is_string`).

I can easily fix this in my code with an explicit conversion to string_view, with `static_cast<fmt::string_view>(fmt_str)`, but it seems like this should be supported, and that it would be an easy fix by just letting `basic_format_string` pass the `detail::is_string` test, unless there is some unintended side-effect I can't see.
Consider format_string as a string for enable-if
diff --git a/include/fmt/core.h b/include/fmt/core.h
index 370b2f1eb3ce..2856a43eaeb2 100644
--- a/include/fmt/core.h
+++ b/include/fmt/core.h
@@ -536,6 +536,11 @@ constexpr auto to_string_view(const S& s)
     -> basic_string_view<typename S::char_type> {
   return basic_string_view<typename S::char_type>(s);
 }
+// Catch basic_format_string for any char type.
+template <typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
+constexpr auto to_string_view(const S& s) -> decltype(s.get()) {
+  return s.get();
+}
 void to_string_view(...);
 
 // Specifies whether S is a string type convertible to fmt::basic_string_view.

diff --git a/test/color-test.cc b/test/color-test.cc
index c2ba13a977db..d9291b89b4f8 100644
--- a/test/color-test.cc
+++ b/test/color-test.cc
@@ -58,6 +58,13 @@ TEST(color_test, format) {
             "\x1b[4m\x1b[38;2;000;000;255mbar\x1b[0m");
 }
 
+TEST(color_test, format_forwarding) {
+  const fmt::format_string<int,int,int> format_str_int = "rgb(255,20,30){}{}{}";
+  auto sv = fmt::detail::to_string_view(format_str_int);
+  EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), format_str_int, 1, 2, 3),
+            "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m");
+}
+
 TEST(color_test, format_to) {
   auto out = std::string();
   fmt::format_to(std::back_inserter(out), fg(fmt::rgb(255, 20, 30)),
@@ -66,7 +73,24 @@ TEST(color_test, format_to) {
             "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m");
 }
 
+TEST(color_test, format_to_forwarding) {
+  auto out = std::string();
+  const fmt::format_string<int,int,int> format_str_int = "rgb(255,20,30){}{}{}";
+  fmt::format_to(std::back_inserter(out), fg(fmt::rgb(255, 20, 30)),
+                 format_str_int, 1, 2, 3);
+  EXPECT_EQ(fmt::to_string(out),
+            "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m");
+}
+
 TEST(color_test, print) {
   EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
                "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
 }
+
+TEST(color_test, print_forwarding) {
+  const fmt::format_string<int,int,int> format_str_int = "rgb(255,20,30){}{}{}";
+  EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), format_str_int, 1, 2, 3),
+               "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m");
+  EXPECT_WRITE(stdout, fmt::print(stdout, fg(fmt::rgb(255, 20, 30)), format_str_int, 1, 2, 3),
+               "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m");
+}

{"compile-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "ostream-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "ranges-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "std-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "core-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "scan-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "color-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "chrono-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "format-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "gtest-extra-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "args-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "compile-fp-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "unicode-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "assert-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "posix-mock-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "printf-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "enforce-checks-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "format-impl-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "os-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "xchar-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}}
{"compile-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "ostream-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "ranges-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "std-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "core-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "scan-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "color-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "chrono-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "format-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "gtest-extra-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "args-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "compile-fp-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "unicode-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "assert-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "posix-mock-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "printf-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "enforce-checks-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "format-impl-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "os-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}, "xchar-test": {"run": "PASS", "test": "NONE", "fix": "PASS"}}
{"passed_count": 20, "failed_count": 0, "skipped_count": 0, "passed_tests": ["compile-test", "core-test", "chrono-test", "gtest-extra-test", "compile-fp-test", "unicode-test", "assert-test", "enforce-checks-test", "format-impl-test", "os-test", "xchar-test", "ostream-test", "ranges-test", "std-test", "scan-test", "color-test", "format-test", "args-test", "posix-mock-test", "printf-test"], "failed_tests": [], "skipped_tests": []}
{"passed_count": 0, "failed_count": 0, "skipped_count": 0, "passed_tests": [], "failed_tests": [], "skipped_tests": []}
{"passed_count": 20, "failed_count": 0, "skipped_count": 0, "passed_tests": ["compile-test", "core-test", "chrono-test", "gtest-extra-test", "compile-fp-test", "unicode-test", "assert-test", "enforce-checks-test", "format-impl-test", "os-test", "xchar-test", "ostream-test", "ranges-test", "std-test", "scan-test", "color-test", "format-test", "args-test", "posix-mock-test", "printf-test"], "failed_tests": [], "skipped_tests": []}