:: lib::std::text::strings;
export error GuardError {
Entry Entry::init(string relativePath, string digest, u64 size) override {
self.relativePath = relativePath;
stringv Entry::path(&const self) <-self {
return self->relativePath.view();
stringv Entry::hash(&const self) <-self {
return self->digest.view();
u64 Entry::byteSize(&const self) {
export bool text_less(stringv left, stringv right) {
if right.len() < common {
loop u32 index = 0; index < common; index++ {
if left[index] < right[index] {
if left[index] > right[index] {
return left.len() < right.len();
export bool entry_less(&const Entry left, &const Entry right) {
return text_less(left->path(), right->path());
export bool same_content(&const Entry left, &const Entry right) {
return left->byteSize() == right->byteSize() and
left->hash() == right->hash();
export (string, error) copy_text(stringv value) {
return strings::string_from_bytes(value.cast<[]const u8>);
export (string, error) child_path(stringv parent, stringv name) {
usize capacity = parent.len().cast<usize> + name.len().cast<usize> + 1;
if capacity <= parent.len().cast<usize> {
return (none, AllocationError.OutOfMemory);
builder, allocationError := strings::StringBuilder::withCapacity(capacity);
if allocationError != none {
return (none, allocationError);
if builder.append(parent) != none or builder.appendChar('/') != none or
builder.append(name) != none {
return (none, AllocationError.OutOfMemory);
return builder.toString();
export bool valid_relative_path(stringv value) {
if value.len() == 0 or value[0] == '/' or
value[value.len() - 1] == '/' {
loop u32 index = 0; index <= value.len(); index++ {
const char BYTE = value[index];
const u8 BYTE_VALUE = BYTE.cast<u8>;
if BYTE_VALUE < 32 or BYTE_VALUE == 127 or BYTE == '\\' or
BYTE == ':' or BYTE == '*' or BYTE == '?' or BYTE == '"' or
BYTE == '<' or BYTE == '>' or BYTE == '|' {
const u32 LENGTH = index - segmentStart;
segment := value.substr(segmentStart, LENGTH);
if segment == "." or segment == ".." {
segmentStart = index + 1;