Workspace Guard manifest
Este conteúdo não está disponível em sua língua ainda.
module manifest;
@imports :: root::model :: lib::std::containers::collections :: lib::std::encoding::hex :: lib::std::io::buffered :: lib::std::io::errors as io_errors :: lib::std::io::file :: lib::std::io::filesystem :: lib::std::text::strings :: lib::std::text::utf8;
const stringv HEADER = "FLECHA-WORKSPACE-SNAPSHOT\t1\n";const usize WRITE_CAPACITY = 8192;
export error write( stringv destination, &const collections::Array<model::Entry> entries) { present, existsError := filesystem::exists(destination); if existsError != none { return existsError; } if present { return io_errors::IoError.AlreadyExists; }
options := build file::OpenOptions { self.write = true; self.createNew = true; }; output, openError := file::File::open(destination, options); if openError != none { return openError; }
error result = none; { adapter := output.writer(); writer, writerError := buffered::BufferedWriter::withCapacity(adapter, WRITE_CAPACITY); if writerError != none { result = writerError; } else { result = write_contents(&writer, entries); } }
if result == none { result = output.flush(); } closeError := output.close(); if result == none { result = closeError; }
if result != none { error _ = filesystem::remove_file(destination); } return result;}
error write_contents( &buffered::BufferedWriter writer, &const collections::Array<model::Entry> entries) { error writeError = writer->writeAll(HEADER.cast<[]const u8>); if writeError != none { return writeError; }
values := entries->view(); loop usize index = 0; index < values.len(); index++ { pathHex, pathError := hex::encode(values[index].path()); if pathError != none { return pathError; } sizeText, sizeError := strings::int_to_string<u64>(values[index].byteSize()); if sizeError != none { return sizeError; }
writeError = writer->writeAll( values[index].hash().cast<[]const u8> ); if writeError == none { writeError = writer->writeAll("\t".cast<[]const u8>); } if writeError == none { writeError = writer->writeAll(sizeText.view().cast<[]const u8>); } if writeError == none { writeError = writer->writeAll("\t".cast<[]const u8>); } if writeError == none { writeError = writer->writeAll(pathHex.view().cast<[]const u8>); } if writeError == none { writeError = writer->writeAll("\n".cast<[]const u8>); } if writeError != none { return writeError; } }
return writer->flush();}
export (collections::Array<model::Entry>, error) read(stringv sourcePath) { contents, readError := filesystem::read_file(sourcePath); if readError != none { return (collections::Array<model::Entry>::init(), readError); } return parse(contents.view());}
(collections::Array<model::Entry>, error) parse(stringv contents) { entries := collections::Array<model::Entry>::init(); if contents.len() < HEADER.len() or contents.substr(0, HEADER.len()) != HEADER { return (entries, model::GuardError.InvalidManifest); }
u32 offset = HEADER.len(); while offset < contents.len() { u32 end = offset; while end < contents.len() and contents[end] != '\n' { end++; } if end == contents.len() or end == offset { return (entries, model::GuardError.InvalidManifest); }
entry, entryError := parse_line(contents.substr(offset, end - offset)); if entryError != none { return (entries, entryError); } if !entries.empty() { { values := entries.view(); if !model::text_less( values[values.len() - 1].path(), entry.path() ) { return (entries, model::GuardError.InvalidManifest); } } } pushError := entries.push(entry); if pushError != none { return (entries, pushError); } offset = end + 1; }
return (entries, none);}
(model::Entry, error) parse_line(stringv line) { u32 first = line.len(); u32 second = line.len(); loop u32 index = 0; index < line.len(); index++ { if line[index] != '\t' { proceed; } if first == line.len() { first = index; } else if second == line.len() { second = index; } else { return (none, model::GuardError.InvalidManifest); } } if first == 0 or second <= first + 1 or second + 1 >= line.len() { return (none, model::GuardError.InvalidManifest); }
digestText := line.substr(0, first); sizeText := line.substr(first + 1, second - first - 1); pathText := line.substr(second + 1, line.len() - second - 1);
digestBytes, digestDecodeError := hex::decode_to_bytes(digestText); if digestDecodeError != none or digestBytes.len() != 32 { return (none, model::GuardError.InvalidManifest); } canonicalDigest, digestEncodeError := hex::encode_bytes(digestBytes[:]); if digestEncodeError != none { return (none, digestEncodeError); } if canonicalDigest.view() != digestText { return (none, model::GuardError.InvalidManifest); }
size, sizeParseError := strings::stringv_to_int<u64>(sizeText); if sizeParseError != none { return (none, model::GuardError.InvalidManifest); } canonicalSize, sizeFormatError := strings::int_to_string<u64>(size); if sizeFormatError != none { return (none, sizeFormatError); } if canonicalSize.view() != sizeText { return (none, model::GuardError.InvalidManifest); }
pathValue, pathDecodeError := hex::decode(pathText); if pathDecodeError != none or utf8::validate(pathValue.view()) != none or !model::valid_relative_path(pathValue.view()) { return (none, model::GuardError.InvalidManifest); } canonicalPath, pathEncodeError := hex::encode(pathValue.view()); if pathEncodeError != none { return (none, pathEncodeError); } if canonicalPath.view() != pathText { return (none, model::GuardError.InvalidManifest); }
return (model::Entry::init(pathValue, canonicalDigest, size), none);}