Ctrl K
ComunidadGuías…

std/fs.zc

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import "./core.zc"
import "./mem.zc"
import "./result.zc"
import "./string.zc"
import "./vec.zc"
import "sys/fs.zc"

def Z_SEEK_SET = 0;
def Z_SEEK_END = 2;
def Z_F_OK = 0;

extern fn _z_fs_mkdir(path: const char*) -> c_int;
extern fn _z_fs_get_metadata(path: const char*, size: u64*, is_dir: c_int*, is_file: c_int*) -> c_int;
extern fn _z_fs_read_entry(dir: void*, out_name: char*, buf_size: c_int, is_dir: c_int*) -> c_int;
extern fn _z_fs_opendir(name: const char*) -> void*;
extern fn _z_fs_closedir(dir: void*) -> c_int;

struct File {
    handle: void*;
}

struct Metadata {
    size: u64;
    is_dir: bool;
    is_file: bool;
}

struct DirEntry {
    name: String;
    is_dir: bool;
}

impl File {
    /// Opens a file with the given mode.
    fn open(path: char*, mode: char*) -> Result<File> {
        let h = fopen(path, mode);
        if (h == NULL) {
            return Result<File>::Err("Failed to open file");
        }
        return Result<File>::Ok(File { handle: h });
    }

    /// Closes the file handle.
    fn close(self) {
        if (self.handle) {
            fclose((FILE*)self.handle);
            self.handle = NULL;
        }
    }

    /// Reads the whole file into a String.
    fn read_to_string(self) -> Result<String> {
        if (self.handle == NULL) {
            return Result<String>::Err("File not open");
        }
        
        fseek((FILE*)self.handle, 0, Z_SEEK_END);
        let size = ftell((FILE*)self.handle);
        fseek((FILE*)self.handle, 0, Z_SEEK_SET);
        
        let buffer: char* = alloc_n<char>((usize)size + 1);
        if (buffer == NULL) {
            return Result<String>::Err("Out of memory");
        }
        
        let read = fread(buffer, 1, size, (FILE*)self.handle);
        buffer[read] = 0;
        
        let s = String::new(buffer);
        dealloc<char>(buffer);
        
        let res = Result<String>::Ok(s);
        s.forget();
        
        let ret = res;
        res.forget();
        return ret;
    }

    /// Reads the whole file into a String.
    fn read_all(path: char*) -> Result<String> {
        let res = File::open(path, "rb");
        if (res.is_err()) {
            return Result<String>::Err(res.err);
        }
        
        let f: File = res.unwrap();
        let s_res = f.read_to_string();
        f.close();
        
        let ret = s_res;
        s_res.forget();
        return ret;
    }

    /// Writes a C string to the file.
    fn write_string(self, content: char*) -> Result<bool> {
        if (self.handle == NULL) {
            return Result<bool>::Err("File not open");
        }
        
        let len = strlen(content);
        let written = fwrite(content, 1, len, (FILE*)self.handle);
        if (written != len) {
            return Result<bool>::Err("Write incomplete");
        }
        return Result<bool>::Ok(true);
    }

    /// Reads all lines into a Vec of Strings.
    fn read_lines(path: char*) -> Result< Vec<String> > {
        let res = File::read_all(path);
        if (res.is_err()) {
            return Result< Vec<String> >::Err(res.err);
        }
        let content = res.unwrap();
        
        let lines = content.split('\n');
        
        // Remove trailing \r from lines if on windows or CRLF
        for (let i: usize = 0; i < lines.len; i = i + 1) {
            let line = lines.get_ref(i);
            let len = line.length();
            if (len > 0) {
                let last_char = line.vec.get(len - 1);
                if (last_char == '\r') {
                    let trimmed = line.substring(0, len - 1);
                    line.destroy();
                    lines.set(i, trimmed);
                }
            }
        }
        
        // Remove the final blank line if ending in a delimiter
        if (lines.len > 0) {
            let last_idx = lines.len - 1;
            let last_line = lines.get_ref(last_idx);
            if (last_line.length() == 0) {
                let empty = lines.pop(); // Safe because it's the element itself
                empty.destroy();
            }
        }
        
        content.destroy();
        
        let ret_res = Result< Vec<String> >::Ok(lines);
        let ret = ret_res;
        return ret;
    }

    /// Writes multiple lines to the file.
    fn write_lines(path: char*, lines: Vec<String>*) -> Result<bool> {
        let res = File::open(path, "wb");
        if (res.is_err()) {
            return Result<bool>::Err(res.err);
        }
        let f = res.unwrap();
        
        for (let i: usize = 0; i < lines.len; i = i + 1) {
            let line = lines.get_ref(i);
            f.write_string(line.c_str());
            f.write_string("\n");
        }
        
        f.close();
        return Result<bool>::Ok(true);
    }

    /// Returns whether the path exists.
    fn exists(path: char*) -> bool {
        let zero: c_int = 0;
        return access(path, Z_F_OK) == zero;
    }

    /// Returns the file metadata, or `None`.
    fn metadata(path: char*) -> Result<Metadata> {
        let size: uint64_t;
        let is_d: c_int;
        let is_f: c_int;
        
        let res = _z_fs_get_metadata(path, &size, &is_d, &is_f);
        let non_zero: c_int = 0;
        if (res != non_zero) {
            return Result<Metadata>::Err("Failed to get metadata");
        }
        
        return Result<Metadata>::Ok(Metadata { 
            size: (u64)size, 
            is_dir: is_d != non_zero, 
            is_file: is_f != non_zero 
        });
    }

    /// Creates a directory.
    fn create_dir(path: char*) -> Result<bool> {
        let status = _z_fs_mkdir(path);
        let zero: c_int = 0;
        if (status != zero) {
            return Result<bool>::Err("Failed to create directory");
        }
        return Result<bool>::Ok(true);
    }

    /// Removes a file.
    fn remove_file(path: char*) -> Result<bool> {
        let status = unlink(path);
        let zero: c_int = 0;
        if (status != zero) {
            return Result<bool>::Err("Failed to remove file");
        }
        return Result<bool>::Ok(true);
    }

    /// Removes a directory.
    fn remove_dir(path: char*) -> Result<bool> {
        let status = rmdir(path);
        let zero: c_int = 0;
        if (status != zero) {
            return Result<bool>::Err("Failed to remove directory");
        }
        return Result<bool>::Ok(true);
    }

    /// Lists a directory as Vec of DirEntry.
    fn read_dir(path: char*) -> Result< Vec<DirEntry> > {
        let dir = _z_fs_opendir(path);
        if (dir == NULL) {
            return Result< Vec<DirEntry> >::Err("Failed to open directory");
        }
        
        let entries = Vec<DirEntry>::new();
        let name_buf: char* = alloc_n<char>(256);
        
        if (name_buf == NULL) {
            _z_fs_closedir(dir);
            return Result< Vec<DirEntry> >::Err("Out of memory");
        }
        
        let is_d: c_int = 0;
        
        let is_d_zero: c_int = 0;
        while (_z_fs_read_entry(dir, name_buf, 256, &is_d)) {
            let zero_cmp: c_int = 0;
            if (strcmp(name_buf, ".") == zero_cmp || strcmp(name_buf, "..") == zero_cmp) {
                continue;
            }
            
            let s = String::new(name_buf);
            let ent = DirEntry {
                name: s,
                is_dir: is_d != is_d_zero
            };
            
            // Transfer ownership: String -> DirEntry
            s.forget();
            
            entries.push(ent);
            
            // Transfer ownership: DirEntry -> Vec
            ent.name.forget();
        }
        
        dealloc<char>(name_buf);
        _z_fs_closedir(dir);
        
        let res = Result< Vec<DirEntry> >::Ok(entries);
        let ret = res;
        return ret;
    }

    /// Returns the current working directory.
    fn current_dir() -> Result<String> {
        let buf: char* = alloc_n<char>(1024);
        if (buf == NULL) return Result<String>::Err("Out of memory");
        
        if (getcwd(buf, 1024) == NULL) {
            dealloc<char>(buf);
            return Result<String>::Err("Failed to get current directory");
        }
        
        let s = String::new(buf);
        dealloc<char>(buf);
        let res = Result<String>::Ok(s);
        s.forget();
        let ret = res;
        res.forget();
        return ret;
    }
}

impl Drop for File {
    fn drop(self) {
        self.close();
    }
}