diff options
Diffstat (limited to 'src/libutil/strings.hh')
-rw-r--r-- | src/libutil/strings.hh | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/src/libutil/strings.hh b/src/libutil/strings.hh new file mode 100644 index 000000000..daeb5be50 --- /dev/null +++ b/src/libutil/strings.hh @@ -0,0 +1,256 @@ +#pragma once +///@file + +#include "error.hh" +#include "types.hh" + +#include <boost/lexical_cast.hpp> +#include <vector> + +namespace nix { + +/** + * Tree formatting. + */ +constexpr char treeConn[] = "├───"; +constexpr char treeLast[] = "└───"; +constexpr char treeLine[] = "│ "; +constexpr char treeNull[] = " "; + +/** + * Convert a list of strings to a null-terminated vector of `char + * *`s. The result must not be accessed beyond the lifetime of the + * list of strings. + */ +std::vector<char *> stringsToCharPtrs(const Strings & ss); + + +MakeError(FormatError, Error); + + +/** + * String tokenizer. + */ +template<class C> C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r"); + + +/** + * Concatenate the given strings with a separator between the + * elements. + */ +template<class C> +std::string concatStringsSep(const std::string_view sep, const C & ss) +{ + size_t size = 0; + // need a cast to string_view since this is also called with Symbols + for (const auto & s : ss) size += sep.size() + std::string_view(s).size(); + std::string s; + s.reserve(size); + for (auto & i : ss) { + if (s.size() != 0) s += sep; + s += i; + } + return s; +} + +template<class ... Parts> +auto concatStrings(Parts && ... parts) + -> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), std::string> +{ + std::string_view views[sizeof...(parts)] = { parts... }; + return concatStringsSep({}, views); +} + + +/** + * Add quotes around a collection of strings. + */ +template<class C> Strings quoteStrings(const C & c) +{ + Strings res; + for (auto & s : c) + res.push_back("'" + s + "'"); + return res; +} + +/** + * Remove trailing whitespace from a string. + * + * \todo return std::string_view. + */ +std::string chomp(std::string_view s); + + +/** + * Remove whitespace from the start and end of a string. + */ +std::string trim(std::string_view s, std::string_view whitespace = " \n\r\t"); + + +/** + * Replace all occurrences of a string inside another string. + */ +std::string replaceStrings( + std::string s, + std::string_view from, + std::string_view to); + + +/** + * Rewrites a string given a map of replacements, applying the replacements in + * sorted order, only once, considering only the strings appearing in the input + * string in performing replacement. + * + * - Replacements are not performed on intermediate strings. That is, for an input + * `"abb"` with replacements `{"ab" -> "ba"}`, the result is `"bab"`. + * - Transitive replacements are not performed. For example, for the input `"abcde"` + * with replacements `{"a" -> "b", "b" -> "c", "e" -> "b"}`, the result is + * `"bccdb"`. + */ +class Rewriter +{ +private: + std::string initials; + std::map<std::string, std::string> rewrites; + +public: + explicit Rewriter(std::map<std::string, std::string> rewrites); + + std::string operator()(std::string s); +}; + +inline std::string rewriteStrings(std::string s, const StringMap & rewrites) +{ + return Rewriter(rewrites)(s); +} + + + +/** + * Parse a string into an integer. + */ +template<class N> +std::optional<N> string2Int(const std::string_view s) +{ + if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed) + return std::nullopt; + try { + return boost::lexical_cast<N>(s.data(), s.size()); + } catch (const boost::bad_lexical_cast &) { + return std::nullopt; + } +} + +/** + * Like string2Int(), but support an optional suffix 'K', 'M', 'G' or + * 'T' denoting a binary unit prefix. + */ +template<class N> +N string2IntWithUnitPrefix(std::string_view s) +{ + N multiplier = 1; + if (!s.empty()) { + char u = std::toupper(*s.rbegin()); + if (std::isalpha(u)) { + if (u == 'K') multiplier = 1ULL << 10; + else if (u == 'M') multiplier = 1ULL << 20; + else if (u == 'G') multiplier = 1ULL << 30; + else if (u == 'T') multiplier = 1ULL << 40; + else throw UsageError("invalid unit specifier '%1%'", u); + s.remove_suffix(1); + } + } + if (auto n = string2Int<N>(s)) + return *n * multiplier; + throw UsageError("'%s' is not an integer", s); +} + +/** + * Parse a string into a float. + */ +template<class N> +std::optional<N> string2Float(const std::string_view s) +{ + try { + return boost::lexical_cast<N>(s.data(), s.size()); + } catch (const boost::bad_lexical_cast &) { + return std::nullopt; + } +} + + +/** + * Convert a little-endian integer to host order. + */ +template<typename T> +T readLittleEndian(unsigned char * p) +{ + T x = 0; + for (size_t i = 0; i < sizeof(x); ++i, ++p) { + x |= ((T) *p) << (i * 8); + } + return x; +} + +/** + * Convert a string to lower case. + */ +std::string toLower(const std::string & s); + + +/** + * Escape a string as a shell word. + */ +std::string shellEscape(const std::string_view s); + +/** + * Base64 encoding/decoding. + */ +std::string base64Encode(std::string_view s); +std::string base64Decode(std::string_view s); + + +/** + * Remove common leading whitespace from the lines in the string + * 's'. For example, if every line is indented by at least 3 spaces, + * then we remove 3 spaces from the start of every line. + */ +std::string stripIndentation(std::string_view s); + +/** + * Get the prefix of 's' up to and excluding the next line break (LF + * optionally preceded by CR), and the remainder following the line + * break. + */ +std::pair<std::string_view, std::string_view> getLine(std::string_view s); + +std::string showBytes(uint64_t bytes); + + +/** + * Provide an addition operator between strings and string_views + * inexplicably omitted from the standard library. + */ +inline std::string operator + (const std::string & s1, std::string_view s2) +{ + auto s = s1; + s.append(s2); + return s; +} + +inline std::string operator + (std::string && s, std::string_view s2) +{ + s.append(s2); + return std::move(s); +} + +inline std::string operator + (std::string_view s1, const char * s2) +{ + std::string s; + s.reserve(s1.size() + strlen(s2)); + s.append(s1); + s.append(s2); + return s; +} + +} |