Files, paths and time
Whole files and streams
Section titled “Whole files and streams”std::fs::read_file and write_file move a whole file in one call. For more control, open a
file: it reads and writes through a buffer, and closes itself when it goes out of scope. A file is a
writer, so std::write formats straight into it.
use std::io;use std::fmt;
extern "C" fn getpid() -> i32;
fn main() -> !void { var name = std::string::from("/tmp/volt-files-doc-"); name.append_int(getpid()); val path = name.as_str(); { var log = try std::fs::open(path, "w"); try log.write("start\n"); std::write(&log, "{} items\n", 3); try log.flush(); } try std::fs::append_file(path, "done\n"); var f = try std::fs::open(path); for (line, i) in f.lines() { std::println("{}: {}", i, line.as_str()); } try f.seek(-5, std::fs::seek_from::END); std::println("{} {}", (try f.read_all()).as_str().trim(), try std::fs::size(path)); try std::fs::remove_file(path);}// expect: 0: start// expect: 1: 3 items// expect: 2: done// expect: done 19open’s mode is C’s: "r" (the default) reads, "w" creates or empties the file, "a" appends,
and "r+", "w+", "a+" both read and write. Files are always binary: on Windows too, what’s
written is what’s read, with no "\r\n" added. read_line gives the next line without its line
ending, or null at the end; lines() loops over them. A line can be any length and hold NUL
bytes. read(buf) reads up to a buffer’s worth of bytes (0 at the end). seek counts from
std::fs::seek_from::START (the default), CURRENT or END, and position() says where the file
is.
Directories
Section titled “Directories”use std::io;use std::text;
extern "C" fn getpid() -> i32;
fn main() -> !void { var name = std::string::from("/tmp/volt-dirs-doc-"); name.append_int(getpid()); val root = name.as_str(); try std::fs::create_dir_all(std::path::join(root, "src/util").as_str()); try std::fs::write_file(std::path::join(root, "src/main.volt").as_str(), ""); try std::fs::write_file(std::path::join(root, "src/util/strings.volt").as_str(), ""); val top = try std::fs::list_dir(root); val all = try std::fs::walk(root); std::println("{} | {}", std::text::join(top.items(), ","), all.len); std::fs::remove_dir(root) catch |e| { std::println("can't remove a full directory: {}", e); }; try std::fs::remove_all(root); std::println("{}", std::fs::exists(root));}// expect: src | 2// expect: can't remove a full directory: NOT_EMPTY// expect: falselist_dir gives the names in a directory, sorted; walk every file below it as a path. Neither
follows links to directories, and remove_all removes a link rather than what it points at.
create_dir makes one directory, whose parent has to exist. exists, is_dir and is_file ask
what’s at a path and size gives a file’s length; rename moves a file, and copy_file copies
one, each replacing a file already at the destination. Errors
are std::fs::fs_error: NOT_FOUND, EXISTS, NOT_EMPTY, PERMISSION, NOT_A_DIR, IS_A_DIR,
IO, BAD_PATH, the same on every system.
std::fs has code for Linux, macOS, FreeBSD and Windows (64-bit). On Windows, std::path takes
\ as a separator too and C:\ as a root, rename replaces a file that’s there as it does
elsewhere, and paths go to the system in its “ANSI” code page: a path with non-ASCII characters
works only where that code page is UTF-8.
std::path works on the text of a path and never touches the disk. join(a, b) puts b inside
a (an absolute b replaces it); normalize drops . parts, repeated separators and x/.. pairs
by the text alone, without following links; is_absolute asks whether a path starts at the root.
use std::io;
fn main() -> void { val p = "src/../lib/./parser.tar.gz"; val clean = std::path::normalize(p); std::println("{} {} {} {}", clean.as_str(), std::path::parent(p), std::path::file_name(p), std::path::extension(p)); std::println("{} {}", std::path::stem("notes.md"), std::path::join("/home", "volt").as_str());}// expect: lib/parser.tar.gz src/../lib/. parser.tar.gz gz// expect: notes /home/voltThe working directory and the environment are in std::process: cwd(), set_cwd(path),
env(name), set_env(name, value) and unset_env(name). exe_path() is the program’s own file,
as an absolute path, for finding what’s installed beside it.
std::time::now() reads the monotonic clock, which only goes forward, for measuring;
unix_nanos() reads the wall clock. Durations are made with nanos, micros, millis and secs,
and read back with as_nanos, as_micros, as_millis, as_secs (whole units) or as_secs_f64.
An instant’s elapsed() is the time since it, and since(earlier) the time between two.
utc_iso8601 writes a wall-clock time as a UTC date, with milliseconds when there are any.
use std::io;
fn main() -> void { val start = std::time::now(); std::time::sleep(std::time::millis(5)); val took = start.elapsed(); val stamp = std::time::utc_iso8601(1000000000123000000); std::println("{} {}", took.as_millis() >= 5, stamp.as_str());}// expect: true 2001-09-09T01:46:40.123Z