Files and mapping
API summary (generated from the Beans source by npm run coverage): 3 types · 18 static methods · 35 instance methods.
Beans has three builtin types for working with the file system: File for a single
file, Dir for directories, and MMap for memory-mapped files and shared memory.
They are native builtins with no .b source, reached through the runtime ABI table
in compiler/beans/expression.b.
Because these are builtins, their signatures are positional: the parameter types
are fixed, the names are not part of the signature. Most calls return a
Result because file work can fail. Reads and
writes move Bytes.
An open file with a read/write cursor.
Statics
Section titled “Statics”Call these on the File type itself. Opening is the fallible constructor: it
returns Result<File>.
File.exists(string) -> boolFile.size(string) -> Result<int>File.open(string, string) -> Result<File>File.copy(string, string) -> Result<int>File.remove(string) -> Result<bool>File.rename(string, string) -> Result<bool>The second argument to File.open is the mode, one of:
"r": read only"rw": read and write, must already exist"create": create, or truncate an existing file, for read and write"append": open for adding at the end
File.exists answers whether a file is there without opening it. File.size also
works as a static that takes a path, so you can read a file’s length without an
open handle; the same call exists as a method on an open file (below). File.copy
uses the platform file-copy path when available and returns the byte count.
Methods
Section titled “Methods”File.read(int) -> Result<Bytes>File.read_at(int, int) -> Result<Bytes>File.read_text(int) -> Result<string>File.read_text_at(int, int) -> Result<string>File.write(Bytes) -> Result<int>File.write_at(int, Bytes) -> Result<int>File.write_text(string) -> Result<int>File.write_text_at(int, string) -> Result<int>File.seek(int) -> intFile.seek_from_end(int) -> intFile.tell() -> intFile.size() -> Result<int>File.truncate(int) -> Result<bool>File.sync() -> Result<bool>File.close() -> Result<bool>File.lock() -> Result<bool>File.try_lock() -> Result<bool>File.unlock() -> Result<bool>read(n)reads up tonbytes from the current cursor and moves the cursor forward; a short read at end of file returns the bytes that are there.write(b)writes from the cursor, returns how many bytes went out, and moves the cursor forward.read_at(pos, n)andwrite_at(pos, b)take an absolute position and do not use or move the cursor, so they are safe to call from more than one place in the file.read_textandread_text_atare the text forms. They fill the returned string directly instead of readingBytesand converting it.write_textandwrite_text_atwrite string storage directly. Use them when the data is text; the byte methods keep their ownedBytesbehavior.seek(pos)moves the cursor topos;seek_from_end(off)moves it tooffbytes before the end. Both return the new position and panic if the file is closed.tell()returns the current position.truncate(n)cuts or extends the file to exactlynbytes.sync()flushes the file’s data to disk (fsync).close()releases the descriptor; closing a file that is already closed is an error.lock,try_lock, andunlocktake and release an advisory whole-file lock (flock).lockblocks until it gets the lock;try_lockreturnsok(false)when another holder has it, rather than waiting.
Every file descriptor Beans owns is close-on-exec.
Write a file, then read part of it back through the cursor:
import std.io
fn main() { let f: File = File.open("greeting.txt", "create").expect("open") f.write(Bytes.from("hello world")).expect("write")
f.seek(0) let head: Bytes = f.read(5).expect("read") io.println(head.to_string()) io.println("cursor now at {f.tell()}")
f.close().expect("close")}All directory work is on statics of the Dir type.
Dir.create(string) -> Result<bool>Dir.create_all(string) -> Result<bool>Dir.current() -> stringDir.exists(string) -> boolDir.list(string) -> Result<List<string>>Dir.walk(string) -> Result<List<string>>Dir.remove(string) -> Result<bool>Dir.remove_all(string) -> Result<bool>Dir.sync(string) -> Result<bool>Dir.temp_path() -> stringcreatemakes one directory and fails if a parent is missing;create_allmakes the directory and any missing parents.current()returns the process’s current working directory as an absolute path.listreturns the names directly inside a directory, sorted.walkreturns every file and symlink underneath it, recursive, sorted, each path relative to the directory you passed.removeremoves an empty directory;remove_allremoves a directory and everything in it.syncflushes the directory entry itself.temp_pathreturns the system temporary directory as a plain string; it does not touch the disk.
import std.io
fn main() { let base: string = "{Dir.temp_path()}/beans_docs_demo" Dir.create_all(base).expect("create")
let names: List<string> = Dir.list(Dir.temp_path()).expect("list") io.println("{names.len()} entries in the temp directory")
Dir.remove_all(base).expect("clean up")}MMap maps a file, or a POSIX shared memory object, into memory so you read and
write it like a buffer.
Statics
Section titled “Statics”MMap.open(string, bool) -> Result<MMap>MMap.open_shared_memory(string, int, bool) -> Result<MMap>MMap.unlink_shared_memory(string) -> Result<bool>MMap.open(path, writable)maps the whole file withMAP_SHARED. Passtruefor a writable mapping,falsefor read only.MMap.open_shared_memory(name, size, create)opens a named shared memory object. You give thesizeon every open, andcreatechooses whether to create it if it does not exist.MMap.unlink_shared_memory(name)removes a shared memory object by name.
Methods
Section titled “Methods”MMap.len() -> intMMap.get_u8(int) -> intMMap.get_u16(int) -> intMMap.get_u32(int) -> intMMap.get_u64(int) -> intMMap.get_i64(int) -> intMMap.put_u8(int, int) -> MMapMMap.put_u16(int, int) -> MMapMMap.put_u32(int, int) -> MMapMMap.put_u64(int, int) -> MMapMMap.put_i64(int, int) -> MMapMMap.read(int, int) -> BytesMMap.write(int, Bytes) -> MMapMMap.flush() -> Result<bool>MMap.flush_range(int, int) -> Result<bool>MMap.resize(int) -> Result<bool>MMap.close() -> Result<bool>len()is the mapped size in bytes.- The
get_*readers return an integer read at a byte position, little-endian and bounds-checked; an out-of-range position panics. Theput_*writers write an integer at a position, little-endian and bounds-checked, and return the same mapping so you can chain them. read(pos, n)copiesnbytes atposinto a newBytes.write(pos, b)copiesbinto the mapping atposand returns the mapping.flush()writes all changes back (msync);flush_range(pos, n)flushes only[pos, pos + n).resize(n)resizes the mapping and is not available on shared memory.close()unmaps it.
import std.io
fn main() { let name: string = "beans_docs_shm" let m: MMap = MMap.open_shared_memory(name, 64, true).expect("open")
m.put_u32(0, 123456789).put_u64(8, 42) io.println("{m.get_u32(0)} {m.get_u64(8)} over {m.len()} bytes")
m.flush().expect("flush") m.close().expect("close") MMap.unlink_shared_memory(name).expect("unlink")}Error kinds
Section titled “Error kinds”When a file call fails, the Error.kind slug is one of:
not_found, permission, exists, is_dir, not_dir, not_empty, closed,
io.
You can match on the kind to decide what to do. See Option, Result, and Error.
See also
Section titled “See also”- Bytes, the buffer reads and writes use.
- Option, Result, and Error, handling failures.
- The standard library, higher-level I/O modules.