fd/fmt: Fix for compiled strings

This ensures that we can use the faster variants of complex formatting
strings via pre-compilation.

Change-Id: I6e6433edf1c96d717638555e0734657ae32e3dba
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include-fd/stdplus/fd/fmt.hpp b/include-fd/stdplus/fd/fmt.hpp
index 673a93a..b0e4c11 100644
--- a/include-fd/stdplus/fd/fmt.hpp
+++ b/include-fd/stdplus/fd/fmt.hpp
@@ -5,6 +5,16 @@
 #include <stdplus/fd/intf.hpp>
 #include <stdplus/fd/managed.hpp>
 #include <string_view>
+#include <type_traits>
+
+namespace fmt
+{
+namespace detail
+{
+template <typename T>
+struct is_compiled_string;
+}
+} // namespace fmt
 
 namespace stdplus
 {
@@ -22,13 +32,22 @@
     FormatBuffer& operator=(FormatBuffer&&) = default;
 
     template <typename... Args>
-    void append(fmt::format_string<Args...> fmt, Args&&... args)
+    inline void append(fmt::format_string<Args...> fmt, Args&&... args)
     {
         fmt::format_to(std::back_inserter(buf), fmt,
                        std::forward<Args>(args)...);
         writeIfNeeded();
     }
 
+    template <typename T, typename... Args,
+              std::enable_if_t<fmt::detail::is_compiled_string<T>::value,
+                               bool> = true>
+    inline void append(const T& t, Args&&... args)
+    {
+        fmt::format_to(std::back_inserter(buf), t, std::forward<Args>(args)...);
+        writeIfNeeded();
+    }
+
     void flush();
 
   private:
@@ -50,10 +69,17 @@
     FormatToFile& operator=(FormatToFile&&) = delete;
 
     template <typename... Args>
-    void append(fmt::format_string<Args...> fmt, Args&&... args)
+    inline void append(fmt::format_string<Args...> fmt, Args&&... args)
     {
         buf.append(fmt, std::forward<Args>(args)...);
     }
+    template <typename T, typename... Args,
+              std::enable_if_t<fmt::detail::is_compiled_string<T>::value,
+                               bool> = true>
+    inline void append(const T& t, Args&&... args)
+    {
+        buf.append(t, std::forward<Args>(args)...);
+    }
 
     void commit(const std::filesystem::path& out, int mode = 0644);
 
diff --git a/test/fd/fmt.cpp b/test/fd/fmt.cpp
index fe1fed4..55f6c5d 100644
--- a/test/fd/fmt.cpp
+++ b/test/fd/fmt.cpp
@@ -1,6 +1,7 @@
 #include <gtest/gtest.h>
 
 #include <filesystem>
+#include <fmt/compile.h>
 #include <memory>
 #include <stdplus/fd/fmt.hpp>
 #include <stdplus/fd/managed.hpp>
@@ -8,30 +9,37 @@
 #include <stdplus/util/cexec.hpp>
 #include <sys/mman.h>
 
+#include <string>
+#include <string_view>
+
 namespace stdplus
 {
 namespace fd
 {
 
+using fmt::operator""_cf;
+using std::literals::string_view_literals::operator""sv;
+
 TEST(FormatBuffer, Basic)
 {
     auto fd = ManagedFd(CHECK_ERRNO(memfd_create("test", 0), "memfd_create"));
     {
         FormatBuffer buf(fd, 4096);
         buf.append("hi\n");
+        buf.append("hi\n"sv);
         EXPECT_EQ(0, fd.lseek(0, Whence::Cur));
         buf.flush();
 
-        EXPECT_EQ(3, fd.lseek(0, Whence::Cur));
-        buf.append("{}", std::string(2050, 'a'));
-        EXPECT_EQ(3, fd.lseek(0, Whence::Cur));
-        buf.append("{}", std::string(2050, 'a'));
-        EXPECT_EQ(4103, fd.lseek(0, Whence::Cur));
+        EXPECT_EQ(6, fd.lseek(0, Whence::Cur));
+        buf.append(FMT_COMPILE("{}"), std::string(2050, 'a'));
+        EXPECT_EQ(6, fd.lseek(0, Whence::Cur));
+        buf.append("{}"_cf, std::string(2050, 'a'));
+        EXPECT_EQ(4106, fd.lseek(0, Whence::Cur));
 
-        buf.append("hi\n");
-        EXPECT_EQ(4103, fd.lseek(0, Whence::Cur));
+        buf.append(FMT_STRING("hi\n"));
+        EXPECT_EQ(4106, fd.lseek(0, Whence::Cur));
     }
-    EXPECT_EQ(4106, fd.lseek(0, Whence::Cur));
+    EXPECT_EQ(4109, fd.lseek(0, Whence::Cur));
 }
 
 class FormatToFileTest : public gtest::TestWithTmp
@@ -64,11 +72,14 @@
 TEST_F(FormatToFileTest, Basic)
 {
     file->append("hi\n");
+    file->append("hi\n"sv);
+    file->append(FMT_STRING("hi\n"));
+    file->append(FMT_COMPILE("hi\n"));
     EXPECT_EQ(0, std::filesystem::file_size(tmpname));
     auto filename = fmt::format("{}/out", CaseTmpDir());
     file->commit(filename);
     EXPECT_FALSE(std::filesystem::exists(tmpname));
-    EXPECT_EQ(3, std::filesystem::file_size(filename));
+    EXPECT_EQ(12, std::filesystem::file_size(filename));
 }
 
 } // namespace fd