aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-01-18 22:40:39 +0800
committercrupest <crupest@outlook.com>2022-01-18 22:40:39 +0800
commitbc37fdb4b746ce0f5f5c49ad9779464acb488284 (patch)
tree53aa5b98ebba25016be1ec74a2ff161a70de4762
parentd28248cc4ccb1a1d81f0d82b8317767f4e2bff44 (diff)
downloadcru-bc37fdb4b746ce0f5f5c49ad9779464acb488284.tar.gz
cru-bc37fdb4b746ce0f5f5c49ad9779464acb488284.tar.bz2
cru-bc37fdb4b746ce0f5f5c49ad9779464acb488284.zip
...
-rw-r--r--include/cru/common/Format.hpp140
-rw-r--r--include/cru/common/Logger.hpp1
-rw-r--r--include/cru/common/String.hpp79
-rw-r--r--include/cru/common/StringUtil.hpp1
-rw-r--r--include/cru/platform/Check.hpp1
-rw-r--r--include/cru/platform/Color.hpp1
-rw-r--r--include/cru/platform/GraphicsBase.hpp1
-rw-r--r--include/cru/platform/gui/InputMethod.hpp25
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/ErrnoException.cpp2
-rw-r--r--src/common/Format.cpp111
-rw-r--r--src/common/String.cpp50
-rw-r--r--src/common/StringUtil.cpp2
-rw-r--r--src/common/io/UnixFileStream.cpp1
-rw-r--r--src/osx/graphics/quartz/Brush.cpp1
-rw-r--r--src/osx/graphics/quartz/TextLayout.cpp1
-rw-r--r--src/parse/Grammar.cpp1
-rw-r--r--src/win/Exception.cpp1
-rw-r--r--src/win/graphics/direct/Font.cpp1
-rw-r--r--test/common/StringTest.cpp1
20 files changed, 269 insertions, 153 deletions
diff --git a/include/cru/common/Format.hpp b/include/cru/common/Format.hpp
new file mode 100644
index 00000000..d53a5720
--- /dev/null
+++ b/include/cru/common/Format.hpp
@@ -0,0 +1,140 @@
+#pragma once
+
+#include "Exception.hpp"
+#include "String.hpp"
+
+namespace cru {
+inline String ToString(bool value) {
+ return value ? String(u"true") : String(u"false");
+}
+
+template <typename T>
+std::enable_if_t<std::is_integral_v<T>, String> ToString(T value) {
+ std::array<char, 50> buffer;
+ auto result =
+ std::to_chars(buffer.data(), buffer.data() + buffer.size(), value);
+
+ if (result.ec == std::errc{}) {
+ } else {
+ throw std::invalid_argument("Failed to convert value to chars.");
+ }
+
+ auto size = result.ptr - buffer.data();
+ auto b = new char16_t[size + 1];
+ b[size] = 0;
+ std::copy(buffer.data(), result.ptr, b);
+ return String::FromBuffer(b, size, size);
+}
+
+template <typename T>
+std::enable_if_t<std::is_floating_point_v<T>, String> ToString(T value) {
+ auto str = std::to_string(value);
+ return String(str.cbegin(), str.cend());
+}
+
+template <typename T>
+std::enable_if_t<
+ std::is_convertible_v<decltype(ToString(std::declval<const T&>)), String>,
+ String>
+ToString(const T& value, StringView option) {
+ return ToString(value);
+}
+
+inline String ToString(String value) { return value; }
+
+namespace details {
+enum class FormatTokenType { PlaceHolder, Text };
+enum class FormatPlaceHolderType { None, Positioned, Named };
+
+struct FormatToken {
+ static FormatToken Text() {
+ return FormatToken{FormatTokenType::Text, {}, {}, 0, {}, {}};
+ }
+
+ static FormatToken NonePlaceHolder(String option) {
+ return FormatToken(FormatTokenType::PlaceHolder, {},
+ FormatPlaceHolderType::None, 0, {}, std::move(option));
+ }
+
+ static FormatToken PositionedPlaceHolder(int position, String option) {
+ return FormatToken(FormatTokenType::PlaceHolder, {},
+ FormatPlaceHolderType::Positioned, position, {},
+ std::move(option));
+ }
+
+ static FormatToken NamedPlaceHolder(String name, String option) {
+ return FormatToken(FormatTokenType::PlaceHolder, {},
+ FormatPlaceHolderType::Named, 0, std::move(name),
+ std::move(option));
+ }
+
+ FormatToken(FormatTokenType type, String data,
+ FormatPlaceHolderType place_holder_type,
+ int place_holder_position, String place_holder_name,
+ String place_holder_option)
+ : type(type),
+ data(std::move(data)),
+ place_holder_type(place_holder_type),
+ place_holder_position(place_holder_position),
+ place_holder_name(std::move(place_holder_name)),
+ place_holder_option(std::move(place_holder_option)) {}
+
+ CRU_DEFAULT_COPY(FormatToken)
+ CRU_DEFAULT_MOVE(FormatToken)
+
+ CRU_DEFAULT_DESTRUCTOR(FormatToken)
+
+ FormatTokenType type;
+ String data;
+ FormatPlaceHolderType place_holder_type;
+ int place_holder_position;
+ String place_holder_name;
+ String place_holder_option;
+};
+
+std::vector<FormatToken> CRU_BASE_API ParseToFormatTokenList(const String& str);
+
+void CRU_BASE_API FormatAppendFromFormatTokenList(
+ String& current, const std::vector<FormatToken>& format_token_list,
+ Index index);
+
+template <typename TA, typename... T>
+void FormatAppendFromFormatTokenList(
+ String& current, const std::vector<FormatToken>& format_token_list,
+ Index index, TA&& args0, T&&... args) {
+ for (Index i = index; i < static_cast<Index>(format_token_list.size()); i++) {
+ const auto& token = format_token_list[i];
+ if (token.type == FormatTokenType::PlaceHolder) {
+ if (token.place_holder_type == FormatPlaceHolderType::None) {
+ current += ToString(std::forward<TA>(args0), token.place_holder_option);
+ FormatAppendFromFormatTokenList(current, format_token_list, i + 1,
+ std::forward<T>(args)...);
+
+ return;
+ } else {
+ throw Exception(
+ u"Currently do not support positional or named place holder.");
+ }
+ } else {
+ current += token.data;
+ }
+ }
+}
+} // namespace details
+
+template <typename... T>
+String Format(const String& format, T&&... args) {
+ String result;
+
+ details::FormatAppendFromFormatTokenList(
+ result, details::ParseToFormatTokenList(format), 0,
+ std::forward<T>(args)...);
+
+ return result;
+}
+
+template <typename... T>
+String String::Format(T&&... args) const {
+ return cru::Format(*this, std::forward<T>(args)...);
+}
+} // namespace cru
diff --git a/include/cru/common/Logger.hpp b/include/cru/common/Logger.hpp
index 7d43fc5a..c4a3e54a 100644
--- a/include/cru/common/Logger.hpp
+++ b/include/cru/common/Logger.hpp
@@ -2,6 +2,7 @@
#include "Base.hpp"
#include "String.hpp"
+#include "Format.hpp"
#include <list>
#include <memory>
diff --git a/include/cru/common/String.hpp b/include/cru/common/String.hpp
index dd3da52f..8db012cb 100644
--- a/include/cru/common/String.hpp
+++ b/include/cru/common/String.hpp
@@ -14,6 +14,7 @@
#include <string>
#include <string_view>
#include <system_error>
+#include <tuple>
#include <type_traits>
#include <vector>
@@ -320,84 +321,6 @@ inline String operator+(const String& left, const String& right) {
return result;
}
-inline String ToString(bool value) {
- return value ? String(u"true") : String(u"false");
-}
-
-template <typename T>
-std::enable_if_t<std::is_integral_v<T>, String> ToString(T value) {
- std::array<char, 50> buffer;
- auto result =
- std::to_chars(buffer.data(), buffer.data() + buffer.size(), value);
-
- if (result.ec == std::errc{}) {
- } else {
- throw std::invalid_argument("Failed to convert value to chars.");
- }
-
- auto size = result.ptr - buffer.data();
- auto b = new char16_t[size + 1];
- b[size] = 0;
- std::copy(buffer.data(), result.ptr, b);
- return String::FromBuffer(b, size, size);
-}
-
-template <typename T>
-std::enable_if_t<std::is_floating_point_v<T>, String> ToString(T value) {
- auto str = std::to_string(value);
- return String(str.cbegin(), str.cend());
-}
-
-inline String ToString(String value) { return value; }
-
-namespace details {
-enum class FormatTokenType { PlaceHolder, Text };
-
-struct FormatToken {
- FormatTokenType type;
- String data;
-};
-
-std::vector<FormatToken> CRU_BASE_API ParseToFormatTokenList(const String& str);
-
-void CRU_BASE_API FormatAppendFromFormatTokenList(
- String& current, const std::vector<FormatToken>& format_token_list,
- Index index);
-
-template <typename TA, typename... T>
-void FormatAppendFromFormatTokenList(
- String& current, const std::vector<FormatToken>& format_token_list,
- Index index, TA&& args0, T&&... args) {
- for (Index i = index; i < static_cast<Index>(format_token_list.size()); i++) {
- const auto& token = format_token_list[i];
- if (token.type == FormatTokenType::PlaceHolder) {
- current += ToString(std::forward<TA>(args0));
- FormatAppendFromFormatTokenList(current, format_token_list, i + 1,
- std::forward<T>(args)...);
- return;
- } else {
- current += token.data;
- }
- }
-}
-} // namespace details
-
-template <typename... T>
-String Format(const String& format, T&&... args) {
- String result;
-
- details::FormatAppendFromFormatTokenList(
- result, details::ParseToFormatTokenList(format), 0,
- std::forward<T>(args)...);
-
- return result;
-}
-
-template <typename... T>
-String String::Format(T&&... args) const {
- return cru::Format(*this, std::forward<T>(args)...);
-}
-
CRU_DEFINE_COMPARE_OPERATORS(StringView)
inline String::iterator String::insert(const_iterator pos, StringView str) {
diff --git a/include/cru/common/StringUtil.hpp b/include/cru/common/StringUtil.hpp
index 3c673db1..2d53fc2b 100644
--- a/include/cru/common/StringUtil.hpp
+++ b/include/cru/common/StringUtil.hpp
@@ -223,6 +223,7 @@ char16_t CRU_BASE_API ToLower(char16_t c);
char16_t CRU_BASE_API ToUpper(char16_t c);
bool CRU_BASE_API IsWhitespace(char16_t c);
+bool CRU_BASE_API IsDigit(char16_t c);
Utf8CodePointIterator CRU_BASE_API CreateUtf8Iterator(const std::byte* buffer,
Index size);
diff --git a/include/cru/platform/Check.hpp b/include/cru/platform/Check.hpp
index 5d99aa9c..121e3505 100644
--- a/include/cru/platform/Check.hpp
+++ b/include/cru/platform/Check.hpp
@@ -3,6 +3,7 @@
#include "Resource.hpp"
#include "cru/common/String.hpp"
+#include "cru/common/Format.hpp"
#include <memory>
#include <type_traits>
diff --git a/include/cru/platform/Color.hpp b/include/cru/platform/Color.hpp
index 94c6c485..cdc6518b 100644
--- a/include/cru/platform/Color.hpp
+++ b/include/cru/platform/Color.hpp
@@ -3,6 +3,7 @@
#include "cru/platform/Base.hpp"
#include "cru/common/String.hpp"
+#include "cru/common/Format.hpp"
#include <cstdint>
#include <optional>
diff --git a/include/cru/platform/GraphicsBase.hpp b/include/cru/platform/GraphicsBase.hpp
index 73b41bb5..088086e9 100644
--- a/include/cru/platform/GraphicsBase.hpp
+++ b/include/cru/platform/GraphicsBase.hpp
@@ -3,6 +3,7 @@
#include "cru/common/Range.hpp"
#include "cru/common/String.hpp"
+#include "cru/common/Format.hpp"
#include <cstdint>
#include <limits>
diff --git a/include/cru/platform/gui/InputMethod.hpp b/include/cru/platform/gui/InputMethod.hpp
index 923a2088..a34aba74 100644
--- a/include/cru/platform/gui/InputMethod.hpp
+++ b/include/cru/platform/gui/InputMethod.hpp
@@ -53,28 +53,3 @@ struct IInputMethodContext : virtual IPlatformResource {
};
} // namespace cru::platform::gui
-// template <>
-// struct fmt::formatter<cru::platform::gui::CompositionText, char16_t>
-// : fmt::formatter<std::u16string_view, char16_t> {
-// auto parse(fmt::basic_format_parse_context<char16_t>& ctx) {
-// return fmt::formatter<std::u16string_view, char16_t>::parse(ctx);
-// }
-
-// template <typename FormatContext>
-// auto format(const cru::platform::gui::CompositionText& ct,
-// FormatContext& ctx) {
-// auto output = ctx.out();
-// output = format_to(output, u"text: {}\n", ct.text);
-// output = format_to(output, u"clauses:\n");
-// for (gsl::index i = 0; i < static_cast<gsl::index>(ct.clauses.size());
-// i++) {
-// const auto& clause = ct.clauses[i];
-// output =
-// format_to(output, u"\t{}. start: {} end: {}{}\n", i, clause.start,
-// clause.end, clause.target ? u" target" : u"");
-// }
-// output = format_to(output, u"selection: position: {} count: {}",
-// ct.selection.position, ct.selection.count);
-// return output;
-// }
-// };
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 300fa03e..40c85ef0 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -1,6 +1,7 @@
add_library(cru_base SHARED
Base.cpp
Exception.cpp
+ Format.cpp
Logger.cpp
PropertyTree.cpp
String.cpp
diff --git a/src/common/ErrnoException.cpp b/src/common/ErrnoException.cpp
index 15d298b3..731ae3e5 100644
--- a/src/common/ErrnoException.cpp
+++ b/src/common/ErrnoException.cpp
@@ -1,5 +1,7 @@
#include "cru/common/ErrnoException.hpp"
+#include "cru/common/Format.hpp"
+
#include <errno.h>
namespace cru {
diff --git a/src/common/Format.cpp b/src/common/Format.cpp
new file mode 100644
index 00000000..2d3b50eb
--- /dev/null
+++ b/src/common/Format.cpp
@@ -0,0 +1,111 @@
+#include "cru/common/Format.hpp"
+
+namespace cru {
+namespace details {
+FormatToken ParsePlaceHolder(String place_holder_string) {
+ if (place_holder_string.empty()) {
+ return FormatToken::NonePlaceHolder({});
+ }
+
+ if (place_holder_string.StartWith(u":")) {
+ if (place_holder_string.Find(u':', 1) != -1) {
+ throw Exception(u"Two ':' inside placeholder.");
+ }
+
+ return FormatToken::NonePlaceHolder(place_holder_string.substr(1));
+ }
+ if (IsDigit(place_holder_string[0])) {
+ int position = 0;
+ int index = 0;
+ while (index < place_holder_string.size() &&
+ IsDigit(place_holder_string[index])) {
+ position = position * 10 + place_holder_string[index] - '0';
+ index++;
+ }
+
+ String option;
+
+ if (index != place_holder_string.size()) {
+ if (place_holder_string[index] != ':') {
+ throw Exception(u"Invalid placeholder in format.");
+ }
+
+ option = place_holder_string.substr(index + 1);
+ }
+
+ return FormatToken::PositionedPlaceHolder(position, std::move(option));
+ }
+
+ auto separator_index = place_holder_string.Find(':');
+ if (separator_index == -1) {
+ return FormatToken::NamedPlaceHolder(place_holder_string, {});
+ } else {
+ return FormatToken::NamedPlaceHolder(
+ place_holder_string.substr(0, separator_index),
+ place_holder_string.substr(separator_index + 1));
+ }
+}
+
+std::vector<FormatToken> ParseToFormatTokenList(const String& str) {
+ std::vector<FormatToken> result;
+
+ auto push_char = [&result](char16_t c) {
+ if (result.empty() || result.back().type == FormatTokenType::PlaceHolder) {
+ result.push_back(FormatToken::Text());
+ }
+ result.back().data.append(c);
+ };
+
+ bool try_to_escape = false;
+ bool is_in_place_holder = false;
+ String place_holder_string;
+
+ for (auto c : str) {
+ if (c == u'{') {
+ if (try_to_escape) {
+ push_char(u'{');
+ try_to_escape = false;
+ is_in_place_holder = false;
+ } else {
+ if (is_in_place_holder) {
+ throw Exception(u"Invalid format string: '{' inside placeholder.");
+ }
+
+ try_to_escape = true;
+ is_in_place_holder = true;
+ }
+ } else if (c == u'}') {
+ if (is_in_place_holder) {
+ is_in_place_holder = false;
+ result.push_back(ParsePlaceHolder(std::move(place_holder_string)));
+ place_holder_string.clear();
+ } else {
+ push_char(u'}');
+ }
+ try_to_escape = false;
+ } else {
+ if (is_in_place_holder) {
+ place_holder_string.push_back(c);
+ } else {
+ push_char(c);
+ }
+ try_to_escape = false;
+ }
+ }
+ return result;
+}
+
+void FormatAppendFromFormatTokenList(
+ String& current, const std::vector<FormatToken>& format_token_list,
+ Index index) {
+ for (Index i = index; i < static_cast<Index>(format_token_list.size()); i++) {
+ const auto& token = format_token_list[i];
+ if (token.type == FormatTokenType::PlaceHolder) {
+ throw Exception(u"More placeholder than args.");
+ } else {
+ current += token.data;
+ }
+ }
+}
+} // namespace details
+} // namespace cru
diff --git a/src/common/String.cpp b/src/common/String.cpp
index 21f3f235..9366abeb 100644
--- a/src/common/String.cpp
+++ b/src/common/String.cpp
@@ -394,56 +394,6 @@ int String::Compare(const String& other) const {
}
}
-namespace details {
-std::vector<FormatToken> ParseToFormatTokenList(const String& str) {
- std::vector<FormatToken> result;
-
- auto push_char = [&result](char16_t c) {
- if (result.empty() || result.back().type == FormatTokenType::PlaceHolder) {
- result.push_back(FormatToken{FormatTokenType::Text, String{}});
- }
- result.back().data.append(c);
- };
-
- bool last_is_left_bracket = false;
- for (auto c : str) {
- if (c == u'{') {
- if (last_is_left_bracket) {
- push_char(u'{');
- last_is_left_bracket = false;
- } else {
- last_is_left_bracket = true;
- }
- } else if (c == u'}') {
- if (last_is_left_bracket) {
- result.push_back(FormatToken{FormatTokenType::PlaceHolder, String{}});
- }
- last_is_left_bracket = false;
- } else {
- if (last_is_left_bracket) {
- push_char(u'{');
- }
- push_char(c);
- last_is_left_bracket = false;
- }
- }
- return result;
-}
-
-void FormatAppendFromFormatTokenList(
- String& current, const std::vector<FormatToken>& format_token_list,
- Index index) {
- for (Index i = index; i < static_cast<Index>(format_token_list.size()); i++) {
- const auto& token = format_token_list[i];
- if (token.type == FormatTokenType::PlaceHolder) {
- current += u"{}";
- } else {
- current += token.data;
- }
- }
-}
-} // namespace details
-
int StringView::Compare(const StringView& other) const {
const_iterator i1 = cbegin();
const_iterator i2 = other.cbegin();
diff --git a/src/common/StringUtil.cpp b/src/common/StringUtil.cpp
index 04435a0a..1f5f6150 100644
--- a/src/common/StringUtil.cpp
+++ b/src/common/StringUtil.cpp
@@ -258,6 +258,8 @@ bool IsWhitespace(char16_t c) {
return c == u' ' || c == u'\t' || c == u'\n' || c == u'\r';
}
+bool IsDigit(char16_t c) { return c >= u'0' && c <= u'9'; }
+
Utf8CodePointIterator CreateUtf8Iterator(const std::byte* buffer, Index size) {
return Utf8CodePointIterator(reinterpret_cast<const char*>(buffer), size);
}
diff --git a/src/common/io/UnixFileStream.cpp b/src/common/io/UnixFileStream.cpp
index 9c9abba5..615c36f1 100644
--- a/src/common/io/UnixFileStream.cpp
+++ b/src/common/io/UnixFileStream.cpp
@@ -1,6 +1,7 @@
#include "cru/common/io/UnixFileStream.hpp"
#include "cru/common/ErrnoException.hpp"
#include "cru/common/io/OpenFileFlag.hpp"
+#include "cru/common/Format.hpp"
#include <fcntl.h>
#include <sys/_types/_s_ifmt.h>
diff --git a/src/osx/graphics/quartz/Brush.cpp b/src/osx/graphics/quartz/Brush.cpp
index 83ac20a2..637ce34f 100644
--- a/src/osx/graphics/quartz/Brush.cpp
+++ b/src/osx/graphics/quartz/Brush.cpp
@@ -1,5 +1,6 @@
#include "cru/osx/graphics/quartz/Brush.hpp"
#include "cru/common/String.hpp"
+#include "cru/common/Format.hpp"
namespace cru::platform::graphics::osx::quartz {
QuartzSolidColorBrush::QuartzSolidColorBrush(IGraphicsFactory* graphics_factory,
diff --git a/src/osx/graphics/quartz/TextLayout.cpp b/src/osx/graphics/quartz/TextLayout.cpp
index de14816d..3f33acdb 100644
--- a/src/osx/graphics/quartz/TextLayout.cpp
+++ b/src/osx/graphics/quartz/TextLayout.cpp
@@ -6,6 +6,7 @@
#include "cru/osx/graphics/quartz/Resource.hpp"
#include "cru/platform/Check.hpp"
#include "cru/platform/graphics/Base.hpp"
+#include "cru/common/Format.hpp"
#include <algorithm>
#include <limits>
diff --git a/src/parse/Grammar.cpp b/src/parse/Grammar.cpp
index 19095150..52933f91 100644
--- a/src/parse/Grammar.cpp
+++ b/src/parse/Grammar.cpp
@@ -1,6 +1,7 @@
#include "cru/parse/Grammar.hpp"
#include "cru/common/String.hpp"
#include "cru/parse/Symbol.hpp"
+#include "cru/common/Format.hpp"
#include <algorithm>
#include <iterator>
diff --git a/src/win/Exception.cpp b/src/win/Exception.cpp
index 2664b092..fb46f184 100644
--- a/src/win/Exception.cpp
+++ b/src/win/Exception.cpp
@@ -1,4 +1,5 @@
#include "cru/win/Exception.hpp"
+#include "cru/common/Format.hpp"
#include <optional>
diff --git a/src/win/graphics/direct/Font.cpp b/src/win/graphics/direct/Font.cpp
index 413c998b..ffc16214 100644
--- a/src/win/graphics/direct/Font.cpp
+++ b/src/win/graphics/direct/Font.cpp
@@ -1,5 +1,6 @@
#include "cru/win/graphics/direct/Font.hpp"
+#include "cru/common/Format.hpp"
#include "cru/win/graphics/direct/Exception.hpp"
#include "cru/win/graphics/direct/Factory.hpp"
diff --git a/test/common/StringTest.cpp b/test/common/StringTest.cpp
index 2dc72478..7198f47b 100644
--- a/test/common/StringTest.cpp
+++ b/test/common/StringTest.cpp
@@ -1,4 +1,5 @@
#include "cru/common/String.hpp"
+#include "cru/common/Format.hpp"
#include <gtest/gtest.h>