Reference · reference

fs module

3 min read · 2026-10-19 · config-weave 0.1.0

use fs gives a script file input and output. It replaces the standalone fs module of wscript-std, which config-weave does not register. Every function that can fail returns Result[…, string], where the error string names the path and the operating system error. The predicates exists, lexists, is_file and is_dir return a plain bool. Path strings are passed through to the operating system unchanged; use the path module to build them.

read and read_bytes

rust
fs::read(path: string) -> Result[string, string]
fs::read_bytes(path: string) -> Result[List[int], string]

read returns the file as text. read_bytes returns the raw bytes as a list of integers. Both follow a final symlink.

read_with and read_bytes_with

rust
fs::read_with(path: string, opts: Value) -> Result[string, string]
fs::read_bytes_with(path: string, opts: Value) -> Result[List[int], string]

The same as read and read_bytes, with the options write_with takes: Value::Null for the defaults, or {follow_symlinks: bool}. An unknown key returns Err. With follow_symlinks set to false, a path whose final component is a symlink returns Err("cannot read <path>: it is a symlink and follow_symlinks is false"). Use it when a root-run script reads a file in a user-owned directory, where a planted symlink could point the read at a file such as /etc/shadow. The Unix and Windows guarantees are the same as for write_with below.

write, write_bytes and append

rust
fs::write(path: string, content: string) -> Result[unit, string]
fs::write_bytes(path: string, bytes: List[int]) -> Result[unit, string]
fs::append(path: string, content: string) -> Result[unit, string]

write and write_bytes replace the file's contents. append adds text to the end of the file and creates the file when it is absent.

write, write_bytes and append follow a final symlink: they change the file the link points to.

write_with and write_bytes_with

rust
fs::write_with(path: string, content: string, opts: Value) -> Result[unit, string]
fs::write_bytes_with(path: string, bytes: List[int], opts: Value) -> Result[unit, string]

The same as write and write_bytes, with options. opts is Value::Null for the defaults or a map with the key below. An unknown key returns Err. With Value::Null each call behaves exactly like write or write_bytes.

KeyTypeDefaultMeaning
follow_symlinksbooltruefalse refuses a final symlink with Err instead of writing through it

With follow_symlinks set to false, a path whose final component is a symlink returns Err("cannot write <path>: it is a symlink and follow_symlinks is false") and the link's target is not touched. Use it when a root-run script writes into a user-owned directory, where a planted symlink would otherwise redirect the write onto another file. Symlinks earlier in the path are followed. On Unix the refusal is part of the open (O_NOFOLLOW), so it has no race. On Windows the path is checked for a symlink or other reparse point first and then opened, so a link swapped in between the two steps is followed.

rust
let no_follow = Value::Map(#{"follow_symlinks": Value::Bool(false)})
fs::write_with("/home/alice/.ssh/authorized_keys", keys, no_follow)?

copy and move

rust
fs::copy(from: string, to: string) -> Result[unit, string]
fs::move(from: string, to: string) -> Result[unit, string]

copy copies one file. move renames a file or a directory.

delete and delete_dir

rust
fs::delete(path: string) -> Result[unit, string]
fs::delete_dir(path: string) -> Result[unit, string]

delete removes a file or a symlink. delete_dir removes a directory and everything in it.

mkdir

rust
fs::mkdir(path: string) -> Result[unit, string]

Creates a directory, including any missing parents.

exists, lexists, is_file and is_dir

rust
fs::exists(path: string) -> bool
fs::lexists(path: string) -> bool
fs::is_file(path: string) -> bool
fs::is_dir(path: string) -> bool

Each predicate answers one question about a path. A path that cannot be read answers false.

exists, is_file and is_dir follow symlinks: they describe the link's target, so a dangling symlink answers false from all three. lexists does not follow a final symlink: it answers true for any symlink, dangling or not. Use lexists to ask whether something occupies a path before creating a link there.

list_dir

rust
fs::list_dir(path: string) -> Result[List[string], string]

Returns the names of the entries in a directory, sorted.

metadata

rust
fs::metadata(path: string) -> Result[Value, string]

Returns a map describing the path. Read it with the accessors of the Value type. metadata does not follow a final symlink: for a link it describes the link itself, with is_symlink set to true, and it succeeds for a dangling link.

KeyTypeMeaning
sizeintsize in bytes
modifiedintmodification time, seconds since the Unix epoch
readonlyboolthe read-only attribute
is_fileboolthe path is a regular file
is_dirboolthe path is a directory
is_symlinkboolthe path is a symlink
modeintUnix permission bits (st_mode & 0o7777, so 0o644 reads as 420), 0 on other platforms
uidintUnix owner user id, Value::Null on other platforms
gidintUnix owner group id, Value::Null on other platforms

glob

rust
fs::glob(pattern: string) -> Result[List[string], string]

Expands a glob pattern to the matching paths, sorted.

temp_file and temp_dir

rust
fs::temp_file() -> Result[string, string]
fs::temp_dir() -> Result[string, string]

Each creates a fresh temporary file or directory and returns its path.

rust
fs::symlink(target: string, link: string) -> Result[unit, string]
fs::read_link(path: string) -> Result[string, string]

symlink creates a link at link that points to target. read_link returns the target a symlink points to.

chown

rust
fs::chown(path: string, owner: Value, group: Value) -> Result[unit, string]

Changes the owner and the group of a path. Unix only: on Windows every call returns Err. owner and group each take one of three values.

ValueMeaning
Value::String(name)a user or group name; an all-digit string that names no account is used as a numeric id
Value::Int(id)a numeric uid or gid
Value::Nullleave that part unchanged

An unknown name returns Err("unknown owner '<name>'") or Err("unknown group '<name>'"). chown does not follow a final symlink: on a link it changes the link itself, never the file the link points to. This stops a symlink planted in a user-owned directory from redirecting a root-run chown onto another file.

rust
fs::write("/home/alice/.ssh/known_hosts", content)?
fs::chown("/home/alice/.ssh/known_hosts", Value::String("alice"), Value::Null)?

chmod

rust
fs::chmod(path: string, mode: Value) -> Result[unit, string]

Sets the permission bits of a path. Unix only: on Windows every call returns Err. mode is Value::Int(n), where 448 is 0o700, or Value::String of octal digits, as chmod(1) reads them: "700", "0700" and "0440" all work. Any other value, a string with a non-octal digit, or a mode above 0o7777 returns Err. Read the result back from the mode key of metadata.

chmod refuses a final symlink instead of following it, and returns Err("cannot chmod <path>: it is a symlink, which chmod does not follow"). Linux cannot change the mode of a link itself, and following it would let a symlink planted in a user-owned directory redirect a root-run chmod onto another file, as with chown. Symlinks earlier in the path are followed.

rust
fs::chmod("/home/alice/.ssh", Value::String("0700"))?
rust
use fs
use hash

fn check(props: Value) -> Result[CheckResult, string] {
    let path = "/etc/app/app.conf"
    if !fs::is_file(path) { return Ok(CheckResult::NotConfigured) }
    let current = hash::sha256_file(path)?
    let wanted = hash::sha256(props.get("content").unwrap().as_string().unwrap())
    if current == wanted { Ok(CheckResult::AlreadyConfigured) } else { Ok(CheckResult::NotConfigured) }
}