Files final
final class FilesPure, effectful filesystem operations.
Files provides a static API for common filesystem tasks — reading, writing, copying, deleting, watching, and more — all expressed as IO, Rill, or Resource values that are referentially transparent and only execute when interpreted.
// Read a file as UTF-8 text, line by line:
final lines = Files.readUtf8Lines(Path('readme.md'));
// Write a stream of bytes to a file:
byteRill.through(Files.writeAll(Path('output.bin'))).compile.drain;Properties
hashCode no setter inherited
int get hashCodeThe hash code for this object.
A hash code is a single integer which represents the state of the object that affects operator == comparisons.
All objects have hash codes. The default hash code implemented by Object represents only the identity of the object, the same way as the default operator == implementation only considers objects equal if they are identical (see identityHashCode).
If operator == is overridden to use the object state instead, the hash code must also be changed to represent that state, otherwise the object cannot be used in hash based data structures like the default Set and Map implementations.
Hash codes must be the same for objects that are equal to each other according to operator ==. The hash code of an object should only change if the object changes in a way that affects equality. There are no further requirements for the hash codes. They need not be consistent between executions of the same program and there are no distribution guarantees.
Objects that are not equal are allowed to have the same hash code. It is even technically allowed that all instances have the same hash code, but if clashes happen too often, it may reduce the efficiency of hash-based data structures like HashSet or HashMap.
If a subclass overrides hashCode, it should override the operator == operator as well to maintain consistency.
Inherited from Object.
Implementation
external int get hashCode;runtimeType no setter inherited
Type get runtimeTypeA representation of the runtime type of the object.
Inherited from Object.
Implementation
external Type get runtimeType;Methods
noSuchMethod() inherited
dynamic noSuchMethod(Invocation invocation)Invoked when a nonexistent method or property is accessed.
A dynamic member invocation can attempt to call a member which doesn't exist on the receiving object. Example:
dynamic object = 1;
object.add(42); // Statically allowed, run-time errorThis invalid code will invoke the noSuchMethod method of the integer 1 with an Invocation representing the .add(42) call and arguments (which then throws).
Classes can override noSuchMethod to provide custom behavior for such invalid dynamic invocations.
A class with a non-default noSuchMethod invocation can also omit implementations for members of its interface. Example:
class MockList<T> implements List<T> {
noSuchMethod(Invocation invocation) {
log(invocation);
super.noSuchMethod(invocation); // Will throw.
}
}
void main() {
MockList().add(42);
}This code has no compile-time warnings or errors even though the MockList class has no concrete implementation of any of the List interface methods. Calls to List methods are forwarded to noSuchMethod, so this code will log an invocation similar to Invocation.method(#add, [42]) and then throw.
If a value is returned from noSuchMethod, it becomes the result of the original invocation. If the value is not of a type that can be returned by the original invocation, a type error occurs at the invocation.
The default behavior is to throw a NoSuchMethodError.
Inherited from Object.
Implementation
@pragma("vm:entry-point")
@pragma("wasm:entry-point")
external dynamic noSuchMethod(Invocation invocation);toString() inherited
String toString()A string representation of this object.
Some classes have a default textual representation, often paired with a static parse function (like int.parse). These classes will provide the textual representation as their string representation.
Other classes have no meaningful textual representation that a program will care about. Such classes will typically override toString to provide useful information when inspecting the object, mainly for debugging or logging.
Inherited from Object.
Implementation
external String toString();Operators
operator ==() inherited
bool operator ==(Object other)The equality operator.
The default behavior for all Objects is to return true if and only if this object and other are the same object.
Override this method to specify a different equality relation on a class. The overriding method must still be an equivalence relation. That is, it must be:
Total: It must return a boolean for all arguments. It should never throw.
Reflexive: For all objects
o,o == omust be true.Symmetric: For all objects
o1ando2,o1 == o2ando2 == o1must either both be true, or both be false.Transitive: For all objects
o1,o2, ando3, ifo1 == o2ando2 == o3are true, theno1 == o3must be true.
The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.
If a subclass overrides the equality operator, it should override the hashCode method as well to maintain consistency.
Inherited from Object.
Implementation
external bool operator ==(Object other);Static Properties
lineSeparator final
final String lineSeparatorThe platform-specific line separator ('\n' on Unix, '\r\n' on Windows).
Implementation
static final String lineSeparator = _platform.lineSeparator;tempDirectory no setter
A resource-managed temporary directory that is deleted when released.
Implementation
static Resource<Path> get tempDirectory => Resource.make(
Files.createTempDirectory(),
(p) => Files.deleteIfExists(p).voided(),
);tempFile no setter
A resource-managed temporary file that is deleted when released.
Implementation
static Resource<Path> get tempFile => Resource.make(
Files.createTempFile(),
(p) => Files.deleteIfExists(p).voided(),
);Static Methods
copy()
Copies the file at source to target.
Implementation
static IO<Unit> copy(Path source, Path target) => _platform.copy(source, target);createDirectory()
Creates a directory at path.
If recursive is true, any missing parent directories are also created.
Implementation
static IO<Unit> createDirectory(Path path, {bool? recursive}) =>
_platform.createDirectory(path, recursive: recursive);createFile()
Creates an empty file at path.
If recursive is true, any missing parent directories are created first. If exclusive is true, the operation fails when the file already exists.
Implementation
static IO<Unit> createFile(
Path path, {
bool? recursive,
bool? exclusive,
}) => _platform.createFile(path, recursive: recursive, exclusive: exclusive);createSymbolicLink()
Creates a symbolic link at link pointing to existing.
If recursive is true, any missing parent directories of link are created first.
Implementation
static IO<Unit> createSymbolicLink(
Path link,
Path existing, {
bool recursive = false,
}) => _platform.createSymbolicLink(link, existing);createTempDirectory()
Creates a temporary directory.
The directory is created inside dir (or the system default temp location when None) with an optional name prefix.
Implementation
static IO<Path> createTempDirectory({
Option<Path> dir = const None(),
String prefix = '',
}) => _platform.createTempDirectory(dir: dir, prefix: prefix);createTempFile()
IO<Path> createTempFile({
Option<Path> dir = const None(),
String prefix = '',
String suffix = '.tmp',
})Creates a temporary file.
The file is created inside dir (or the system default temp location when None) with an optional name prefix and suffix.
Implementation
static IO<Path> createTempFile({
Option<Path> dir = const None(),
String prefix = '',
String suffix = '.tmp',
}) => _platform.createTempFile(dir: dir, prefix: prefix, suffix: suffix);delete()
Deletes the file or empty directory at path.
Implementation
static IO<Unit> delete(Path path) => _platform.delete(path);deleteIfExists()
Deletes the file or directory at path if it exists.
Returns true if the entry was deleted, false if it did not exist.
Implementation
static IO<bool> deleteIfExists(Path path) =>
exists(path).ifM(() => delete(path).as(true), () => IO.pure(false));deleteRecursively()
Recursively deletes the file or directory at path and all of its contents.
Implementation
static IO<Unit> deleteRecursively(Path path) => _platform.deleteRecursively(path);exists()
Returns true if a filesystem entry exists at path.
Implementation
static IO<bool> exists(Path path) => _platform.exists(path);isDirectory()
Returns true if path refers to a directory.
Implementation
static IO<bool> isDirectory(Path path) => _platform.isDirectory(path);isRegularFile()
Returns true if path refers to a regular file.
Implementation
static IO<bool> isRegularFile(Path path) => _platform.isRegularFile(path);isSameFile()
Returns true if path1 and path2 refer to the same filesystem entry (e.g. via hard links or symbolic links).
Implementation
static IO<bool> isSameFile(Path path1, Path path2) => _platform.isSameFile(path1, path2);isSymbolicLink()
Returns true if path refers to a symbolic link.
Implementation
static IO<bool> isSymbolicLink(Path path) => _platform.isSymbolicLink(path);list()
Emits the children of the directory at path as a Rill.
When recursive is true, the directory is traversed recursively. When followLinks is true, symbolic links are resolved to their targets.
Implementation
static Rill<Path> list(
Path path, {
bool recursive = false,
bool followLinks = false,
}) => _platform.list(path, recursive: recursive, followLinks: followLinks);move()
Moves (renames) source to target.
Implementation
static IO<Unit> move(Path source, Path target) => _platform.move(source, target);open()
Resource<FileHandle> open(Path path, Flags flags)Opens the file at path with the given flags, returning a resource-managed FileHandle.
The handle is automatically closed when the Resource is released.
Implementation
static Resource<FileHandle> open(Path path, Flags flags) => _platform.open(path, flags);readAll()
Reads the entire contents of the file at path as a byte stream.
Emits chunks of at most chunkSize bytes. Uses Flags.Read by default unless custom flags are specified.
Implementation
static Rill<int> readAll(
Path path, {
Flags? flags,
int chunkSize = _defaultChunkSize,
}) => Rill.resource(Files.readCursor(path, flags ?? Flags.Read)).flatMap((cursor) {
return cursor.readAll(chunkSize).voided.rill;
});readCursor()
Resource<ReadCursor> readCursor(Path path, Flags flags)Opens the file at path and returns a resource-managed ReadCursor positioned at the beginning of the file.
Implementation
static Resource<ReadCursor> readCursor(Path path, Flags flags) =>
open(path, flags).map((fileHandle) => ReadCursor.of(fileHandle, 0));readRange()
Rill<int> readRange(
Path path, {
int chunkSize = _defaultChunkSize,
required int start,
required int end,
})Reads a byte range from the file at path, starting at byte offset start and ending before byte offset end.
Emits chunks of at most chunkSize bytes.
Implementation
static Rill<int> readRange(
Path path, {
int chunkSize = _defaultChunkSize,
required int start,
required int end,
}) => Rill.resource(Files.readCursor(path, Flags.Read)).flatMap((cursor) {
return cursor.seek(start).readUntil(chunkSize, end).voided.rill;
});readUtf8()
Reads the entire file at path and decodes it as UTF-8.
Emits decoded string chunks. Use readUtf8Lines to split on line boundaries instead.
Implementation
static Rill<String> readUtf8(
Path path, {
int chunkSize = _defaultChunkSize,
}) => readAll(path).through(Pipes.text.utf8.decode);readUtf8Lines()
Reads the entire file at path as UTF-8 and splits it into individual lines.
Line terminators are stripped from the output.
Implementation
static Rill<String> readUtf8Lines(
Path path, {
int chunkSize = _defaultChunkSize,
}) => readUtf8(path, chunkSize: chunkSize).through(Pipes.text.lines);size()
Returns the size of the file at path in bytes.
Implementation
static IO<int> size(Path path) => _platform.size(path);tail()
Rill<int> tail(
Path path, {
int chunkSize = _defaultChunkSize,
int offset = 0,
Duration pollDelay = const Duration(seconds: 1),
})Follows a file as it grows, similar to tail -f.
Begins reading at offset (defaults to the start of the file) and emits chunks of at most chunkSize bytes. When the end of the file is reached, waits pollDelay before checking for new content. The resulting stream never completes on its own.
Implementation
static Rill<int> tail(
Path path, {
int chunkSize = _defaultChunkSize,
int offset = 0,
Duration pollDelay = const Duration(seconds: 1),
}) => Rill.resource(readCursor(path, Flags.Read)).flatMap((cursor) {
return cursor.seek(offset).tail(chunkSize, pollDelay).voided.rill;
});watch()
Rill<WatcherEvent> watch(
Path path, {
List<WatcherEventType> types = WatcherEventType.values,
})Watches path for filesystem changes, emitting WatcherEvents.
Only events matching the given types are emitted (defaults to all event types).
Implementation
static Rill<WatcherEvent> watch(
Path path, {
List<WatcherEventType> types = WatcherEventType.values,
}) => _platform.watch(path, types: types);writeAll()
A Pipe that writes all incoming byte chunks to the file at path.
Uses Flags.Write by default (create + truncate) unless custom flags are specified.
Implementation
static Pipe<int, Never> writeAll(Path path, {Flags? flags}) =>
(rill) => Rill.resource(
writeCursor(path, flags ?? Flags.Write),
).flatMap((cursor) => cursor.writeAll(rill).voided.rill);writeCursor()
Resource<WriteCursor> writeCursor(Path path, Flags flags)Opens the file at path and returns a resource-managed WriteCursor.
If flags includes Flag.append, the cursor is positioned at the current end of the file; otherwise it starts at offset 0.
Implementation
static Resource<WriteCursor> writeCursor(Path path, Flags flags) =>
Files.open(path, flags).flatMap((handle) {
final size = flags.contains(Flag.append) ? handle.size : IO.pure(0);
final cursor = size.map((offset) => WriteCursor.of(handle, offset));
return Resource.eval(cursor);
});writeRotate()
A Pipe that writes incoming byte chunks across multiple files, rotating to a new file each time limit bytes have been written.
computePath is evaluated each time a new file is needed, allowing dynamic file naming (e.g. timestamps). The file is opened with the given flags. When Flag.append is included, each file's initial write position accounts for existing content.
Implementation
static Pipe<int, Never> writeRotate(
IO<Path> computePath,
int limit,
Flags flags,
) {
Resource<FileHandle> openNewFile() => Resource.eval(computePath).flatMap((p) => open(p, flags));
IO<WriteCursor> newCursor(FileHandle file) =>
flags.contains(Flag.append)
? file.size.map((size) => WriteCursor.of(file, size))
: IO.pure(WriteCursor.of(file, 0));
Pull<Unit, Unit> go(
Hotswap<FileHandle> fileHotswap,
WriteCursor cursor,
int acc,
Rill<int> s,
) {
final toWrite = limit - acc;
return s.pull.unconsLimit(toWrite).flatMap((hdtl) {
return hdtl.foldN(
() => Pull.done,
(hd, tl) {
final newAcc = acc + hd.size;
return cursor.writePull(hd).flatMap((nextCursor) {
if (newAcc >= limit) {
return Pull.eval(
fileHotswap
.swap(openNewFile())
.flatMap((_) => fileHotswap.current.use(newCursor)),
).flatMap((nextCursor) => go(fileHotswap, nextCursor, 0, tl));
} else {
return go(fileHotswap, nextCursor, newAcc, tl);
}
});
},
);
});
}
return (rill) => Rill.resource(Hotswap.create(openNewFile())).flatMap((fileHotswap) {
return Rill.eval(fileHotswap.current.use(newCursor)).flatMap((cursor) {
return go(fileHotswap, cursor, 0, rill).rill.drain();
});
});
}writeUtf8()
A Pipe that encodes incoming strings as UTF-8 and writes them to the file at path.
Implementation
static Pipe<String, Never> writeUtf8(Path path) =>
(rill) => rill.through(Pipes.text.utf8.encode).through(writeAll(path));writeUtf8Lines()
A Pipe that writes incoming strings to the file at path as UTF-8, separated by lineSeparator, with a trailing line separator.
Implementation
static Pipe<String, Never> writeUtf8Lines(Path path) {
return (rill) {
return rill.pull.uncons
.flatMap((hdtl) {
return hdtl.foldN(
() => Pull.done,
(next, rest) =>
Rill.chunk(next)
.append(() => rest)
.intersperse(lineSeparator)
.append(() => Rill.emit(lineSeparator))
.underlying,
);
})
.rill
.through(writeUtf8(path));
};
}