string.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. #include "jinja/string.h"
  2. #include "jinja/value.h"
  3. #include <algorithm>
  4. #include <functional>
  5. #include <optional>
  6. #include <sstream>
  7. #include <string>
  8. #include <vector>
  9. namespace jinja {
  10. //
  11. // string_part
  12. //
  13. bool string_part::is_uppercase() const {
  14. for (char c : val) {
  15. if (std::islower(static_cast<unsigned char>(c))) {
  16. return false;
  17. }
  18. }
  19. return true;
  20. }
  21. bool string_part::is_lowercase() const {
  22. for (char c : val) {
  23. if (std::isupper(static_cast<unsigned char>(c))) {
  24. return false;
  25. }
  26. }
  27. return true;
  28. }
  29. //
  30. // string
  31. //
  32. void string::mark_input() {
  33. for (auto & part : parts) {
  34. part.is_input = true;
  35. }
  36. }
  37. std::string string::str() const {
  38. if (parts.size() == 1) {
  39. return parts[0].val;
  40. }
  41. std::ostringstream oss;
  42. for (const auto & part : parts) {
  43. oss << part.val;
  44. }
  45. return oss.str();
  46. }
  47. size_t string::length() const {
  48. size_t len = 0;
  49. for (const auto & part : parts) {
  50. len += part.val.length();
  51. }
  52. return len;
  53. }
  54. bool string::all_parts_are_input() const {
  55. for (const auto & part : parts) {
  56. if (!part.is_input) {
  57. return false;
  58. }
  59. }
  60. return true;
  61. }
  62. bool string::is_uppercase() const {
  63. for (const auto & part : parts) {
  64. if (!part.is_uppercase()) {
  65. return false;
  66. }
  67. }
  68. return true;
  69. }
  70. bool string::is_lowercase() const {
  71. for (const auto & part : parts) {
  72. if (!part.is_lowercase()) {
  73. return false;
  74. }
  75. }
  76. return true;
  77. }
  78. // mark this string as input if other has ALL parts as input
  79. void string::mark_input_based_on(const string & other) {
  80. if (other.all_parts_are_input()) {
  81. for (auto & part : parts) {
  82. part.is_input = true;
  83. }
  84. }
  85. }
  86. string string::append(const string & other) {
  87. for (const auto & part : other.parts) {
  88. parts.push_back(part);
  89. }
  90. return *this;
  91. }
  92. // in-place transformation
  93. using transform_fn = std::function<std::string(const std::string&)>;
  94. static string apply_transform(string & self, const transform_fn & fn) {
  95. for (auto & part : self.parts) {
  96. part.val = fn(part.val);
  97. }
  98. return self;
  99. }
  100. string string::uppercase() {
  101. return apply_transform(*this, [](const std::string & s) {
  102. std::string res = s;
  103. std::transform(res.begin(), res.end(), res.begin(), ::toupper);
  104. return res;
  105. });
  106. }
  107. string string::lowercase() {
  108. return apply_transform(*this, [](const std::string & s) {
  109. std::string res = s;
  110. std::transform(res.begin(), res.end(), res.begin(), ::tolower);
  111. return res;
  112. });
  113. }
  114. string string::capitalize() {
  115. return apply_transform(*this, [](const std::string & s) {
  116. if (s.empty()) return s;
  117. std::string res = s;
  118. res[0] = ::toupper(static_cast<unsigned char>(res[0]));
  119. std::transform(res.begin() + 1, res.end(), res.begin() + 1, ::tolower);
  120. return res;
  121. });
  122. }
  123. string string::titlecase() {
  124. return apply_transform(*this, [](const std::string & s) {
  125. std::string res = s;
  126. bool capitalize_next = true;
  127. for (char &c : res) {
  128. if (isspace(static_cast<unsigned char>(c))) {
  129. capitalize_next = true;
  130. } else if (capitalize_next) {
  131. c = ::toupper(static_cast<unsigned char>(c));
  132. capitalize_next = false;
  133. } else {
  134. c = ::tolower(static_cast<unsigned char>(c));
  135. }
  136. }
  137. return res;
  138. });
  139. }
  140. string string::strip(bool left, bool right, std::optional<const std::string_view> chars) {
  141. static auto strip_part = [](const std::string & s, bool left, bool right, std::optional<const std::string_view> chars) -> std::string {
  142. size_t start = 0;
  143. size_t end = s.length();
  144. auto match_char = [&chars](unsigned char c) -> bool {
  145. return chars ? (*chars).find(c) != std::string::npos : isspace(c);
  146. };
  147. if (left) {
  148. while (start < end && match_char(static_cast<unsigned char>(s[start]))) {
  149. ++start;
  150. }
  151. }
  152. if (right) {
  153. while (end > start && match_char(static_cast<unsigned char>(s[end - 1]))) {
  154. --end;
  155. }
  156. }
  157. return s.substr(start, end - start);
  158. };
  159. if (parts.empty()) {
  160. return *this;
  161. }
  162. if (left) {
  163. for (size_t i = 0; i < parts.size(); ++i) {
  164. parts[i].val = strip_part(parts[i].val, true, false, chars);
  165. if (parts[i].val.empty()) {
  166. // remove empty part
  167. parts.erase(parts.begin() + i);
  168. --i;
  169. continue;
  170. } else {
  171. break;
  172. }
  173. }
  174. }
  175. if (right) {
  176. for (size_t i = parts.size(); i-- > 0;) {
  177. parts[i].val = strip_part(parts[i].val, false, true, chars);
  178. if (parts[i].val.empty()) {
  179. // remove empty part
  180. parts.erase(parts.begin() + i);
  181. continue;
  182. } else {
  183. break;
  184. }
  185. }
  186. }
  187. return *this;
  188. }
  189. } // namespace jinja