AUI Framework  master
Cross-platform base for C++ UI apps
Loading...
Searching...
No Matches
AString.h
    1/*
    2 * AUI Framework - Declarative UI toolkit for modern C++20
    3 * Copyright (C) 2020-2025 Alex2772 and Contributors
    4 *
    5 * SPDX-License-Identifier: MPL-2.0
    6 *
    7 * This Source Code Form is subject to the terms of the Mozilla Public
    8 * License, v. 2.0. If a copy of the MPL was not distributed with this
    9 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   10 */
   11
   12#pragma once
   13
   14#include <algorithm>
   15#include <string>
   16#include <iostream>
   17#include "AUI/Core.h"
   18#include "AUI/Traits/values.h"
   19#include <AUI/Common/ASet.h>
   20#include <optional>
   21#include <AUI/Common/AOptional.h>
   22#include <fmt/core.h>
   23
   24class API_AUI_CORE AStringVector;
   25class API_AUI_CORE AByteBuffer;
   26class API_AUI_CORE AByteBufferView;
   27
   37class API_AUI_CORE AString: std::u16string
   38{
   39private:
   40    friend struct std::hash<AString>;
   41    using super = std::u16string;
   42
   43public:
   44
   45    using iterator = super::iterator;
   46    using value_type = super::value_type;
   47    using const_iterator = super::const_iterator;
   48    using reverse_iterator = super::reverse_iterator;
   49    using const_reverse_iterator = super::const_reverse_iterator;
   50    auto constexpr static NPOS = super::npos;
   51
   52
   53    AString(AString&& other) noexcept
   54            : std::u16string(static_cast<basic_string&&>(other))
   55    {
   56    }
   57
   61    AString(const basic_string& other) noexcept
   62            : basic_string<char16_t>(other)
   63    {
   64    }
   65
   69    AString(const std::string& utf8) noexcept;
   70
   71    AString(const AString& other) noexcept
   72            : super(other.c_str())
   73    {
   74    }
   75
   76    AString(const basic_string& rhs, const std::allocator<char16_t>& allocator) noexcept
   77            : basic_string<char16_t>(rhs, allocator)
   78    {
   79    }
   80
   81    template <class Iterator>
   82    AString(Iterator first, Iterator last) noexcept : super(first, last) {}
   83
   84    AString() noexcept
   85    {
   86    }
   87
   88    AString(char16_t c) noexcept : super(&c, &c + 1)
   89    {
   90
   91    }
   92
   96    AString(const char* utf8) noexcept;
   97
  101    AString(std::string_view utf8) noexcept;
  102
  103    explicit AString(const std::allocator<char16_t>& allocator) noexcept
  104            : basic_string<char16_t>(allocator)
  105    {
  106    }
  107
  108    AString(const basic_string& rhs, size_type offset, const std::allocator<char16_t>& allocator) noexcept
  109            : basic_string<char16_t>(rhs, offset, allocator)
  110    {
  111    }
  112
  113    AString(const basic_string& rhs, size_type offset, size_type count, const std::allocator<char16_t>& allocator) noexcept
  114            : basic_string<char16_t>(rhs, offset, count, allocator)
  115    {
  116    }
  117
  118    AString(const char16_t* cStyleString, size_type count) noexcept
  119            : basic_string<char16_t>(cStyleString, count)
  120    {
  121    }
  122
  123    AString(const char16_t* cStyleString, size_type count, const std::allocator<char16_t>& allocator) noexcept
  124            : basic_string<char16_t>(cStyleString, count, allocator)
  125    {
  126    }
  127
  128    AString(const char16_t* cStyleString) noexcept
  129            : basic_string<char16_t>(cStyleString)
  130    {
  131    }
  132
  133    AString(const char16_t* cStyleString, const std::allocator<char16_t>& allocator) noexcept
  134            : basic_string<char16_t>(cStyleString, allocator)
  135    {
  136    }
  137
  138    AString(size_type count, char16_t _Ch) noexcept
  139            : basic_string<char16_t>(count, _Ch)
  140    {
  141    }
  142
  143    AString(size_type count, char16_t _Ch, const std::allocator<char16_t>& allocator) noexcept
  144            : basic_string<char16_t>(count, _Ch, allocator)
  145    {
  146    }
  147
  148    AString(basic_string&& rhs) noexcept
  149            : basic_string<char16_t>(std::move(rhs))
  150    {
  151    }
  152
  153    AString(basic_string&& rhs, const std::allocator<char16_t>& allocator) noexcept
  154            : basic_string<char16_t>(std::move(rhs), allocator)
  155    {
  156    }
  157
  158    AString(std::initializer_list<char16_t> _Ilist) noexcept
  159            : basic_string<char16_t>(_Ilist)
  160    {
  161    }
  162
  163    ~AString() = default;
  164
  165
  166    void push_back(char16_t c) noexcept
  167    {
  168        super::push_back(c);
  169    }
  170    void pop_back() noexcept
  171    {
  172        super::pop_back();
  173    }
  174
  175    AString uppercase() const;
  176    AString lowercase() const;
  177
  178    bool startsWith(const AString& other) const noexcept
  179    {
  180        return rfind(other, 0) == 0;
  181    }
  182    bool startsWith(char16_t c) const noexcept
  183    {
  184        return rfind(c, 0) == 0;
  185    }
  186    bool endsWith(const AString& other) const noexcept
  187    {
  188        if (length() < other.length())
  189        {
  190            return false;
  191        }
  192        size_t offset = length() - other.length();
  193        return super::find(other, offset) == offset;
  194    }
  195    bool endsWith(char16_t c) const noexcept
  196    {
  197        size_t offset = length() - 1;
  198        return super::find(c, offset) == offset;
  199    }
  200
  201    AStringVector split(char16_t c) const noexcept;
  202
  203    size_type find(char c, size_type offset = 0) const noexcept
  204    {
  205        return super::find(c, offset);
  206    }
  207    size_type find(char16_t c, size_type offset = 0) const noexcept
  208    {
  209        return super::find(c, offset);
  210    }
  211    size_type find(const AString& str, size_type offset = 0) const noexcept
  212    {
  213        return super::find(str, offset);
  214    }
  215    size_type rfind(char c, size_type offset = NPOS) const noexcept
  216    {
  217        return super::rfind(c, offset);
  218    }
  219    size_type rfind(char16_t c, size_type offset = NPOS) const noexcept
  220    {
  221        return super::rfind(c, offset);
  222    }
  223    size_type rfind(const AString& str, size_type offset = NPOS) const noexcept
  224    {
  225        return super::rfind(str, offset);
  226    }
  227    size_type length() const noexcept
  228    {
  229        return super::length();
  230    }
  231    AString trimLeft(char16_t symbol = ' ') const noexcept;
  232    AString trimRight(char16_t symbol = ' ') const noexcept;
  233
  234    AString trim(char16_t symbol = ' ') const noexcept
  235    {
  236        return trimRight(symbol).trimLeft(symbol);
  237    }
  238
  239    void reserve(size_t s)
  240    {
  241        super::reserve(s);
  242    }
  243    void resize(size_t s)
  244    {
  245        super::resize(s);
  246    }
  247
  248    AString restrictLength(size_t s, const AString& stringAtEnd = "...") const;
  249
  250    char16_t* data() noexcept
  251    {
  252        return super::data();
  253    }
  254
  255    const char16_t* data() const noexcept
  256    {
  257        return super::data();
  258    }
  259    AString& replaceAll(const AString& from, const AString& to);
  260    [[nodiscard]] AString replacedAll(const AString& from, const AString& to) const;
  261    [[nodiscard]] inline AString replacedAll(char16_t from, char16_t to) const noexcept {
  262        AString copy;
  263        copy.reserve(length() + 10);
  264        for (auto c : *this) {
  265            if (c == from) {
  266                copy << to;
  267            } else {
  268                copy << c;
  269            }
  270        }
  271        return copy;
  272    }
  273    [[nodiscard]] inline AString replacedAll(const ASet<char16_t>& from, char16_t to) const noexcept {
  274        AString copy;
  275        copy.reserve(length() + 10);
  276        for (auto c : *this) {
  277            if (from.contains(c)) {
  278                copy << to;
  279            } else {
  280                copy << c;
  281            }
  282        }
  283        return copy;
  284    }
  285    AString& replaceAll(char16_t from, char16_t to) noexcept;
  286
  287
  293    template<typename OtherContainer>
  294    void insertAll(const OtherContainer& c) noexcept {
  295        super::insert(super::end(), c.begin(), c.end());
  296    }
  297
  304    [[nodiscard]]
  305    AOptional<float> toFloat() const noexcept;
  306
  313    [[nodiscard]]
  314    AOptional<double> toDouble() const noexcept;
  315
  322    [[nodiscard]]
  323    double toDoubleOrException() const noexcept {
  324        return toDouble().valueOrException(fmt::format("bad double: {}", toStdString()).c_str());
  325    }
  326
  334    [[nodiscard]]
  335    AOptional<int> toInt() const noexcept;
  336
  344    [[nodiscard]]
  345    int toIntOrException() const {
  346        return toInt().valueOrException(fmt::format("bad int: {}", toStdString()).c_str());
  347    }
  348
  356    [[nodiscard]]
  357    AOptional<int64_t> toLongInt() const noexcept;
  358
  366    [[nodiscard]]
  367    int64_t toLongIntOrException() const {
  368        return toLongInt().valueOrException(fmt::format("bad to number conversion: {}", toStdString()).c_str());
  369    }
  370
  378    [[nodiscard]]
  379    AOptional<unsigned> toUInt() const noexcept;
  380
  388    [[nodiscard]]
  389    unsigned toUIntOrException() const {
  390        return toUInt().valueOrException(fmt::format("bad to number conversion: {}", toStdString()).c_str());
  391    }
  392
  397    [[nodiscard]]
  398    bool toBool() const noexcept {
  399        return *this == "true";
  400    }
  401
  402    [[nodiscard]]
  403    bool contains(char16_t c) const noexcept
  404    {
  405        return find(c) != npos;
  406    }
  407    [[nodiscard]]
  408    bool contains(const AString& other) const noexcept
  409    {
  410        return find(other) != npos;
  411    }
  412
  413    static AString fromLatin1(const AByteBuffer& buffer);
  414    static AString fromUtf8(const AByteBufferView& buffer);
  415    static AString fromUtf8(const char* buffer, size_t length);
  416    static AString fromLatin1(const char* buffer);
  417
  418    static AString numberHex(int i) noexcept;
  419
  420    template<typename T, std::enable_if_t<std::is_integral_v<std::decay_t<T>> || std::is_floating_point_v<std::decay_t<T>>, int> = 0>
  421    static AString number(T i) noexcept {
  422        if constexpr (std::is_same_v<bool, std::decay_t<T>>) {
  423            if (i)
  424                return "true";
  425            return "false";
  426        } else {
  427            auto v = std::to_string(i);
  428            if constexpr (std::is_floating_point_v<T>) {
  429                // remove trailing zeros
  430                v.erase(v.find_last_not_of('0') + 1, std::u16string::npos);
  431                v.erase(v.find_last_not_of('.') + 1, std::u16string::npos);
  432            }
  433            return v;
  434        }
  435    }
  436
  437    static constexpr auto TO_NUMBER_BASE_BIN = 2;
  438    static constexpr auto TO_NUMBER_BASE_OCT = 8;
  439    static constexpr auto TO_NUMBER_BASE_DEC = 10;
  440    static constexpr auto TO_NUMBER_BASE_HEX = 16;
  441
  442
  447    AOptional<int> toNumber(aui::ranged_number<int, 2, 36> base = TO_NUMBER_BASE_DEC) const noexcept;
  448
  453    int toNumberOrException(aui::ranged_number<int, 2, 36> base = TO_NUMBER_BASE_DEC) const {
  454        return toNumber(base).valueOrException(fmt::format("bad to number conversion: {}", toStdString()).c_str());
  455    }
  456
  457
  461    std::string toStdString() const noexcept;
  462
  463    void resizeToNullTerminator();
  464
  465    iterator erase(const_iterator begin, const_iterator end) noexcept
  466    {
  467        return super::erase(begin, end);
  468    }
  469    iterator erase(const_iterator begin) noexcept
  470    {
  471        return super::erase(begin);
  472    }
  473
  474    AString& erase(size_type offset) noexcept
  475    {
  476        super::erase(offset);
  477        return *this;
  478    }
  479    AString& erase(size_type offset, size_type count) noexcept
  480    {
  481        super::erase(offset, count);
  482        return *this;
  483    }
  484
  485    AByteBuffer toUtf8() const noexcept;
  486
  487    void removeAt(unsigned at) noexcept
  488    {
  489        AUI_ASSERT(at <= length());
  490        erase(begin() + at);
  491    }
  492    AString excessSpacesRemoved() const noexcept;
  493
  494    iterator insert(size_type at, char16_t c) noexcept
  495    {
  496        AUI_ASSERT(at <= length());
  497        return super::insert(begin() + at, 1, c);
  498    }
  499    iterator insert(size_type at, const AString& c) noexcept
  500    {
  501        AUI_ASSERT(at <= length());
  502        return super::insert(begin() + at, c.begin(), c.end());
  503    }
  504
  505    template<typename Iterator>
  506    iterator insert(const_iterator at, Iterator begin, Iterator end) noexcept
  507    {
  508        AUI_ASSERT(std::distance(super::cbegin(), at) <= length());
  509        return super::insert(at, begin, end);
  510    }
  511
  512    AString& operator<<(char c) noexcept
  513    {
  514        append(1, c);
  515        return *this;
  516    }
  517    AString& operator<<(char16_t c) noexcept
  518    {
  519        append(1, c);
  520        return *this;
  521    }
  522
  523    inline ::AString& operator+=(const AString& str) noexcept
  524    {
  525        append(str);
  526        return *this;
  527    }
  528    inline ::AString& operator+=(const char* str) noexcept
  529    {
  530        *this += AString(str);
  531        return *this;
  532    }
  533
  534    [[nodiscard]] bool empty() const noexcept {
  535        return super::empty();
  536    }
  537    [[nodiscard]] size_type size() const noexcept {
  538        return super::size();
  539    }
  540    char16_t operator[](size_type index) const
  541    {
  542        return super::at(index);
  543    }
  544    char16_t& operator[](size_type index)
  545    {
  546        return super::at(index);
  547    }
  548    bool operator<(const AString& other) const noexcept
  549    {
  550        return compare(other) < 0;
  551    }
  552
  553    void clear() noexcept
  554    {
  555        super::clear();
  556    }
  557
  558    char16_t& front() noexcept
  559    {
  560        return super::front();
  561    }
  562    char16_t& back() noexcept
  563    {
  564        return super::back();
  565    }
  566    const char16_t& front() const noexcept
  567    {
  568        return super::front();
  569    }
  570    const char16_t& back() const noexcept
  571    {
  572        return super::back();
  573    }
  574    char16_t& first() noexcept
  575    {
  576        return super::front();
  577    }
  578    char16_t& last() noexcept
  579    {
  580        return super::back();
  581    }
  582    const char16_t& first() const noexcept
  583    {
  584        return super::front();
  585    }
  586    const char16_t& last() const noexcept
  587    {
  588        return super::back();
  589    }
  590
  591    [[nodiscard]]
  592    AOptional<char16_t> firstOpt() const noexcept
  593    {
  594        if (empty()) {
  595            return std::nullopt;
  596        }
  597        return super::front();
  598    }
  599
  600    [[nodiscard]]
  601    AOptional<char16_t> lastOpt() const noexcept
  602    {
  603        if (empty()) {
  604            return std::nullopt;
  605        }
  606        return super::back();
  607    }
  608
  609    const char16_t* c_str() const
  610    {
  611        return super::c_str();
  612    }
  613
  614    iterator begin() noexcept
  615    {
  616        return super::begin();
  617    }
  618    iterator end() noexcept
  619    {
  620        return super::end();
  621    }
  622
  623    const_iterator begin() const noexcept
  624    {
  625        return super::begin();
  626    }
  627    const_iterator end() const noexcept
  628    {
  629        return super::end();
  630    }
  631
  632    reverse_iterator rbegin() noexcept
  633    {
  634        return super::rbegin();
  635    }
  636    reverse_iterator rend() noexcept
  637    {
  638        return super::rend();
  639    }
  640
  641    const_reverse_iterator rbegin() const noexcept
  642    {
  643        return super::rbegin();
  644    }
  645    const_reverse_iterator rend() const noexcept
  646    {
  647        return super::rend();
  648    }
  649
  650    AString& append(const AString& s) noexcept
  651    {
  652        super::append(s);
  653        return *this;
  654    }
  655
  656    AString& append(size_t count, char16_t ch) noexcept
  657    {
  658        super::append(count, ch);
  659        return *this;
  660    }
  661
  662    AString& operator=(const AString& value) noexcept
  663    {
  664        super::operator=(value);
  665        return *this;
  666    }
  667
  668    AString& operator=(AString&& value) noexcept
  669    {
  670        super::operator=(std::move(value));
  671        return *this;
  672    }
  673
  674    bool operator==(const AString& other) const noexcept
  675    {
  676        if (size() != other.size()) {
  677            return false;
  678        }
  679        return std::memcmp(data(), other.data(), sizeInBytes()) == 0;
  680    }
  681    bool operator==(const char16_t* other) const noexcept
  682    {
  683        auto it = begin();
  684        for (; it != end(); ++it, ++other) {
  685            if (*it != *other) {
  686                return false;
  687            }
  688            if (*other == '\0') {
  689                return false;
  690            }
  691        }
  692        return *other == '\0';
  693    }
  694
  695    [[nodiscard]]
  696    size_t sizeInBytes() const noexcept {
  697        return size() * sizeof(super::value_type);
  698    }
  699
  700    bool operator!=(const AString& other) const noexcept
  701    {
  702        return !operator==(other);
  703    }
  704    bool operator!=(const char16_t* other) const noexcept
  705    {
  706        return !operator==(other);
  707    }
  708
  709    bool operator==(const char* other) const noexcept
  710    {
  711        return *this == AString(other);
  712    }
  713
  714    bool operator!=(const char* other) const noexcept
  715    {
  716        return *this != AString(other);
  717    }
  718
  719    template<typename... Args>
  720    inline AString format(Args&&... args) const;
  721
  722    AString processEscapes() const;
  723
  724    AString& removeAll(char16_t c) noexcept {
  725        erase(std::remove(begin(), end(), c), end());
  726        return *this;
  727    }
  728
  729    [[nodiscard]]
  730    AString substr(std::size_t offset, std::size_t count = npos) const {
  731        return super::substr(offset, count);
  732    }
  733
  734private:
  738    template<typename T>
  739    AOptional<T> toNumberImpl() const noexcept;
  740};
  741
  742inline AString operator+(const AString& l, const AString& r) noexcept
  743{
  744    auto x = l;
  745    x.append(r);
  746    return x;
  747}
  748inline AString operator+(const AString& l, char16_t r) noexcept
  749{
  750    auto x = l;
  751    x.append(r);
  752    return x;
  753}
  754inline AString operator+(const AString& one, const char* other) noexcept
  755{
  756    return one + AString(other);
  757}
  758
  759inline AString operator+(const char* other, const AString& one) noexcept {
  760    return AString(other) + one;
  761}
  762
  763inline AString operator+(char lhs, const AString& cs) noexcept
  764{
  765    AString s(lhs);
  766    s += cs;
  767    return s;
  768}
  769
  770inline AString operator""_as(const char* str, size_t len)
  771{
  772    return {str};
  773}
  774
  775inline std::ostream& operator<<(std::ostream& o, const AString& s)
  776{
  777    o << s.toStdString();
  778    return o;
  779}
  780
  781template<>
  782struct std::hash<AString>
  783{
  784    size_t operator()(const AString& t) const noexcept
  785    {
  786        return std::hash<std::u16string>()(t);
  787    }
  788};
  789
  790#if AUI_PLATFORM_WIN
  791namespace aui::win32 {
  792    /*
  793     * On Windows, char16_t == wchar_t. WinAPI interfaces use wchar_t widely, so we have some handy functions to
  794     * convert AString to wchar_t* and back.
  795     */
  796
  803    inline const wchar_t* toWchar(const AString& string) {
  804        // NOLINTNEXTLINE(*-pro-type-reinterpret-cast)
  805        return reinterpret_cast<const wchar_t *const>(string.data());
  806    }
  807
  814    inline wchar_t* toWchar(AString& string) {
  815        // NOLINTNEXTLINE(*-pro-type-reinterpret-cast)
  816        return reinterpret_cast<wchar_t*>(string.data());
  817    }
  818
  825    inline std::wstring_view toWcharView(const AString& string) {
  826        return {toWchar(string), string.length() };
  827    }
  828
  835    inline AString fromWchar(std::wstring_view string) {
  836        // NOLINTNEXTLINE(*-pro-type-reinterpret-cast)
  837        return {reinterpret_cast<const char16_t *>(string.data()), string.size()};
  838    }
  839}
  840#endif
  841
  842template <> struct fmt::detail::is_string<AString>: std::false_type {};
  843
  844template <> struct fmt::formatter<AString>: fmt::formatter<std::string> {
  845    auto format(const AString& s, format_context& ctx) const {
  846        return fmt::formatter<std::string>::format(s.toStdString(), ctx);
  847    }
  848};
  849
  850
  851// gtest printer for AString
  852inline void PrintTo(const AString& s, std::ostream* stream) {
  853    *stream << s.toStdString();
  854}
Acts like std::string_view but for AByteBuffer.
Definition AByteBufferView.h:24
std::vector-like growing array for byte storage.
Definition AByteBuffer.h:31
Utility wrapper implementing the stack-allocated (fast) optional idiom.
Definition AOptional.h:33
An AVector with string-related functions.
Definition AStringVector.h:22
Represents a Unicode character string.
Definition AString.h:38
AString(const char *utf8) noexcept
int toIntOrException() const
Converts the string to int value.
Definition AString.h:345
int64_t toLongIntOrException() const
Converts the string to int value.
Definition AString.h:367
AString(std::string_view utf8) noexcept
AString(const basic_string &other) noexcept
Definition AString.h:61
bool toBool() const noexcept
Converts the string to boolean value.
Definition AString.h:398
void insertAll(const OtherContainer &c) noexcept
Inserts all values of the specified container to the end.
Definition AString.h:294
AOptional< int > toInt() const noexcept
Converts the string to int value.
AOptional< int64_t > toLongInt() const noexcept
Converts the string to int value.
std::string toStdString() const noexcept
AOptional< double > toDouble() const noexcept
Converts the string to a double number.
AOptional< float > toFloat() const noexcept
Converts the string to a float number.
AString(const std::string &utf8) noexcept
AOptional< int > toNumber(aui::ranged_number< int, 2, 36 > base=TO_NUMBER_BASE_DEC) const noexcept
Returns the string converted to an int using base. Returns std::nullopt if the conversion fails.
double toDoubleOrException() const noexcept
Converts the string to a double number.
Definition AString.h:323
unsigned toUIntOrException() const
Converts the string to int value.
Definition AString.h:389
int toNumberOrException(aui::ranged_number< int, 2, 36 > base=TO_NUMBER_BASE_DEC) const
Returns the string converted to an int using base. Throws an exception if the conversion fails.
Definition AString.h:453
AOptional< unsigned > toUInt() const noexcept
Converts the string to int value.
class_of c
Selects views that are of the specified classes.
Definition class_of.h:84
std::wstring_view toWcharView(const AString &string)
AString to wchar_t string view.
Definition AString.h:825
bool contains(const Container &c, const typename Container::const_reference value) noexcept
Definition containers.h:124
const wchar_t * toWchar(const AString &string)
AString to const wchar_t*.
Definition AString.h:803
AString fromWchar(std::wstring_view string)
wchar_t string view to AString.
Definition AString.h:835
#define AUI_ASSERT(condition)
Asserts that the passed condition evaluates to true.
Definition Assert.h:55
Clamps the possible values for a number to the specified range: [min;max].
Definition values.h:423