Skip to content
Kavachकवच

Files: Writing

● Intermediate3 min

fs.write_text replaces a file in one step; fs.append_text adds to its end.

use std.fs

fn main() ! Error {
    try fs.write_text("visits.log", "Asha\n")
    try fs.append_text("visits.log", "Omar\n")
    try fs.append_text("visits.log", "Zoe\n")
    print(try fs.read_text("visits.log"))
    try fs.write_text("visits.log", "reset\n")
    print(try fs.read_text("visits.log"))
}

The program ran and printed:

kavach run append.kav

AshaOmarZoe reset
append.kav: Kavach code, then what running it prints
  • append_text creates the file if needed, then adds to its end: good for logs.
  • write_text replaces the whole file, atomically: a crash leaves the old text or the new, never half.
  • fs.write_durable also waits until the data is on the disk, so even a power cut keeps it. Databases build on it.

Piece by piece

fs.create_writer(path) gives a FileWriter, which writes a file as its data comes. A large upload or an export is never held in memory whole:

use std.fs

fn main() ! Error {
    const w = try fs.create_writer("export.csv")
    try w.write_text("id,total\n")
    for id in [1, 2, 3] {
        try w.write_text("{id},{id * 10}\n")
    }
    print(try w.finish())
    print(try fs.read_text("export.csv"))
    w.write_text("late") catch e => print(e)
}

The program ran and printed:

kavach run writer.kav

24id,total1,102,203,30 Failed(path: "export.csv", reason: "finished")
writer.kav: Kavach code, then what running it prints
Function or methodGives
fs.create_writer(path) -> FileWriter ! FsErrorA new, empty file at path to write; one already there is emptied
w.write(data) ! FsErrorAdds the Bytes data at the file's end. Writes are gathered in memory and go to the file 64 KB at a time
w.write_text(text) ! FsErrorThe same for text, as UTF-8
w.finish(sync: false) -> Int ! FsErrorWrites what is gathered, closes the file, and gives how many bytes it holds. With sync: true it also forces them to the disk (fsync), so a rename after it can't leave a short file after a crash
  • After finish, write fails with Failed(path, "finished"), as the last line shows, and finish gives the same count again.
  • It needs Fs.write, and with uses Fs("…") the path must be inside its folders, as for every file operation.

A writer let go before finish closes its file and deletes it, so an upload cut short leaves nothing behind:

use std.fs

fn upload(path: String) ! FsError uses Fs {
    const w = try fs.create_writer(path)
    try w.write_text("the first half")
    // returns before finish: the writer is let go
}

fn main() ! Error {
    try upload("photo.jpg")
    print(fs.exists("photo.jpg"))
}

The program ran and printed:

kavach run writer-dropped.kav

false
writer-dropped.kav: Kavach code, then what running it prints

Unlike write_text, the file is written in place, not replaced at once: other programs can see it grow. When that matters, write to another name and rename it, as the spec's example does:

fn save(pieces: [Bytes], path: String) -> Int ! FsError uses Fs.write {
    const w = try fs.create_writer("{path}.part")
    for piece in pieces {
        try w.write(piece)
    }
    const size = try w.finish(sync: true)
    try fs.rename("{path}.part", path)
    size
}
from spec 14.3: Kavach code