#pragma once
#include <std/ownership.h> // std::replace / std::swap, to take a String out of a field
// SafeC Standard Library — String (mutable, heap-allocated)
// A growable UTF-8 byte string with rich manipulation methods.
namespace std {
struct String {
&heap char data; // NUL-terminated heap buffer
unsigned long len; // byte length (not including NUL)
unsigned long cap; // allocated buffer size (including NUL slot)
// ── Construction ────────────────────────────────────────────────────────
static struct String new(); // empty
static struct String from(const &char s); // copy of a C string
static struct String from_bytes([]const char bytes); // copy of the bytes (need not be NUL-terminated)
static struct String from_n_(const &char s, unsigned long n); // internal: from_bytes's (reference, length) core
static struct String with_cap(unsigned long cap); // empty, room for cap bytes
static struct String repeat_str(const &char s, unsigned long n); // s repeated n times
// parts concatenated, `sep` between each (null sep: none)
static struct String join(const ?&char sep, []String parts);
// ── Access ──────────────────────────────────────────────────────────────
unsigned long length() const;
int is_empty() const;
const &char as_ptr() const; // NUL-terminated C string
[]const char as_bytes() const; // the len bytes (no NUL); valid until the next change
int char_at(unsigned long idx) const; // -1 if OOB
void set_char(unsigned long idx, char c);
// ── Capacity ────────────────────────────────────────────────────────────
int reserve(unsigned long additional); // ensure room for len + additional bytes
void shrink_to_fit(); // reduce cap to len + 1
// ── Append ──────────────────────────────────────────────────────────────
int push_char(char c);
int push(const &char cstr);
int push_bytes([]const char bytes); // append the bytes (need not be NUL-terminated)
int push_n_(const &char data, unsigned long n); // internal: push_bytes's (reference, length) core
int push_str(&String other);
int push_int(long long v);
int push_uint(unsigned long long v);
int push_float(double v, int decimals);
int push_bool(int v);
// ── Modification ────────────────────────────────────────────────────────
void clear();
void truncate(unsigned long new_len);
int insert(unsigned long idx, const &char cstr);
int remove_range(unsigned long start, unsigned long end);
void replace_char(char from, char to);
int replace(const &char from, const &char to); // first occurrence
int replace_all(const &char from, const &char to);
void reverse(); // in-place byte reversal
int pop_char(); // remove last byte, return it (-1 if empty)
// ── Search ──────────────────────────────────────────────────────────────
long long index_of(const &char needle) const;
long long last_index_of(const &char needle) const;
int contains(const &char needle) const;
int starts_with(const &char prefix) const;
int ends_with(const &char suffix) const;
int count(const &char needle) const;
long long find_char(char c) const; // index of first c, or -1
long long rfind_char(char c) const; // index of last c, or -1
// ── Transformation (return new String) ──────────────────────────────────
struct String substr(unsigned long start, unsigned long end) const;
struct String to_upper() const;
struct String to_lower() const;
struct String trim() const;
struct String trim_left() const;
struct String trim_right() const;
struct String pad_left(unsigned long width, char fill) const;
struct String pad_right(unsigned long width, char fill) const;
struct String strip_prefix(const &char prefix) const; // remove prefix if present, else clone
struct String strip_suffix(const &char suffix) const; // remove suffix if present, else clone
struct String repeat(unsigned long n) const; // repeat content n times
struct String capitalize() const; // uppercase first byte, lowercase rest
// ── Split ───────────────────────────────────────────────────────────────
// All split methods write into the caller's `out` slots (out.len of them).
// Return value = number of items written.
// If splits exceed out.len, the last slot receives the remainder.
unsigned long split(const &char delim, []String out) const;
unsigned long split_lines([]String out) const;
unsigned long split_whitespace([]String out) const;
// ── Comparison ──────────────────────────────────────────────────────────
int eq(&String other) const;
int eq_cstr(const &char other) const;
int cmp(&String other) const; // <0, 0, >0
int lt(&String other) const;
int gt(&String other) const;
int eq_ignore_case(&String other) const;
int eq_cstr_ignore_case(const &char other) const;
// ── Query ───────────────────────────────────────────────────────────────
int is_ascii() const;
int is_numeric() const;
int is_alphanumeric() const;
// ── Conversion ──────────────────────────────────────────────────────────
long long parse_int(?&int ok) const;
double parse_float(?&int ok) const;
// ── Lifecycle ────────────────────────────────────────────────────────────
struct String clone() const;
void drop(); // frees the buffer (Drop)
void free() consume; // = drop()
// ── Internal ────────────────────────────────────────────────────────────
int reserve_(unsigned long need);
};
// kept for std/ml until its refactor moves to the static method
struct String string_new();
struct String string_from(const &char s);
struct String string_from_n_(const &char s, unsigned long n);
} // namespace std