/* * Copyright 2026 Flix Authors * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */////// Effect for reading the access, creation, and modification times of a file, and its functions.///pubmod Fs.FileTime {use Fs.FileStatuse Fs.FileTimeuse Fs.FsLayer.{logPre, logPost}////// Effect for reading the access, creation, and modification times of a file.///pubeffFileTime {////// Returns the last access time of the given file `f` in milliseconds since the epoch.///defaccessTime(f: String): Result[IoError, Int64]////// Returns the creation time of the given file `f` in milliseconds since the epoch.///defcreationTime(f: String): Result[IoError, Int64]////// Returns the last-modified timestamp of the given file `f` in milliseconds since the epoch.///defmodificationTime(f: String): Result[IoError, Int64] }////// Handles the `FileTime` effect of the given function `f`.////// In other words, re-interprets the `FileTime` effect using the `IO` effect.///pubdefhandle(f: a -> b \ ef): a -> b \ (ef - FileTime) + IO = x ->checked_ecast(Fs.FileSystem.handle(y ->checked_ecast(Fs.FileStat.runWithFileSystem(() -> runWithFileStat(() -> f(y)) ) ) )(x) )////// Runs the `FileTime` effect of the given function `f`.////// In other words, re-interprets the `FileTime` effect using the `IO` effect.///@DefaultHandlerpubdefrunWithIO(f: Unit -> a \ ef): a \ (ef - FileTime) + IO = handle(f)()////// Handles the `FileTime` effect of the given function `f` using the `FileStat` effect.///pubdefrunWithFileStat(f: Unit -> a \ ef): a \ (ef - FileTime) + FileStat =run { f() } withhandlerFileTime {defaccessTime(filename, k) = k(Fs.FileStat.accessTime(filename))defcreationTime(filename, k) = k(Fs.FileStat.creationTime(filename))defmodificationTime(filename, k) = k(Fs.FileStat.modificationTime(filename)) }////// Middleware that intercepts the `FileTime` effect, logging each operation/// and its result via the `Logger` effect.////// Successful operations are logged at `Debug` level; errors at `Warn`.///pubdefwithLogging(f: Unit -> a \ ef): a \ (ef - FileTime) + { FileTime, Logger } =run { f() } withhandlerFileTime {defaccessTime(filename, k) = {logPre("accessTime", filename);letresult = FileTime.accessTime(filename);logPost("accessTime", filename, t -> "${t}ms", result);k(result) }defcreationTime(filename, k) = {logPre("creationTime", filename);letresult = FileTime.creationTime(filename);logPost("creationTime", filename, t -> "${t}ms", result);k(result) }defmodificationTime(filename, k) = {logPre("modificationTime", filename);letresult = FileTime.modificationTime(filename);logPost("modificationTime", filename, t -> "${t}ms", result);k(result) } }////// Middleware that intercepts the `FileTime` effect, resolving all file paths/// relative to the given `baseDir` using Java's `Path.resolve` semantics.////// Absolute paths are passed through unchanged.///pubdefwithBaseDir(baseDir: String, f: Unit -> a \ ef): a \ (ef - FileTime) + FileTime =letr = p -> Fs.FsLayer.resolve(baseDir, p);run { f() } withhandlerFileTime {defaccessTime(filename, k) = k(FileTime.accessTime(r(filename)))defcreationTime(filename, k) = k(FileTime.creationTime(r(filename)))defmodificationTime(filename, k) = k(FileTime.modificationTime(r(filename))) }////// Middleware that intercepts the `FileTime` effect, validating that all file paths/// resolve within the given `chrootDir`. Rejects paths that escape the chroot with/// a `PermissionDenied` error.///pubdefwithChroot(chrootDir: String, f: Unit -> a \ ef): a \ (ef - FileTime) + FileTime =letc = p -> Fs.FsLayer.chroot(chrootDir, p);run { f() } withhandlerFileTime {defaccessTime(filename, k) = k(Result.flatMap(p -> FileTime.accessTime(p), c(filename)))defcreationTime(filename, k) = k(Result.flatMap(p -> FileTime.creationTime(p), c(filename)))defmodificationTime(filename, k) = k(Result.flatMap(p -> FileTime.modificationTime(p), c(filename))) }////// Middleware that intercepts the `FileTime` effect, validating that all file paths/// resolve within one of the given `allowedDirs`. Rejects paths outside the/// allow list with a `PermissionDenied` error.///pubdefwithAllowList(allowedDirs: Nel[String], f: Unit -> a \ ef): a \ (ef - FileTime) + FileTime =letc = p -> Fs.FsLayer.allowList(allowedDirs, p);run { f() } withhandlerFileTime {defaccessTime(filename, k) = k(Result.flatMap(p -> FileTime.accessTime(p), c(filename)))defcreationTime(filename, k) = k(Result.flatMap(p -> FileTime.creationTime(p), c(filename)))defmodificationTime(filename, k) = k(Result.flatMap(p -> FileTime.modificationTime(p), c(filename))) }////// Middleware that intercepts the `FileTime` effect, validating that all file paths/// do not resolve within any of the given `deniedDirs`. Rejects paths inside a/// denied directory with a `PermissionDenied` error.///pubdefwithDenyList(deniedDirs: Nel[String], f: Unit -> a \ ef): a \ (ef - FileTime) + FileTime =letc = p -> Fs.FsLayer.denyList(deniedDirs, p);run { f() } withhandlerFileTime {defaccessTime(filename, k) = k(Result.flatMap(p -> FileTime.accessTime(p), c(filename)))defcreationTime(filename, k) = k(Result.flatMap(p -> FileTime.creationTime(p), c(filename)))defmodificationTime(filename, k) = k(Result.flatMap(p -> FileTime.modificationTime(p), c(filename))) }////// Middleware that intercepts the `FileTime` effect, validating that all file paths/// match at least one of the given glob `patterns`. Rejects paths that do not match/// with a `PermissionDenied` error.///pubdefwithAllowGlob(patterns: Nel[String], f: Unit -> a \ ef): a \ (ef - FileTime) + FileTime =letc = p -> Fs.FsLayer.allowGlob(patterns, p);run { f() } withhandlerFileTime {defaccessTime(filename, k) = k(Result.flatMap(p -> FileTime.accessTime(p), c(filename)))defcreationTime(filename, k) = k(Result.flatMap(p -> FileTime.creationTime(p), c(filename)))defmodificationTime(filename, k) = k(Result.flatMap(p -> FileTime.modificationTime(p), c(filename))) }////// Middleware that intercepts the `FileTime` effect, validating that all file paths/// do not match any of the given glob `patterns`. Rejects paths that match a denied/// pattern with a `PermissionDenied` error.///pubdefwithDenyGlob(patterns: Nel[String], f: Unit -> a \ ef): a \ (ef - FileTime) + FileTime =letc = p -> Fs.FsLayer.denyGlob(patterns, p);run { f() } withhandlerFileTime {defaccessTime(filename, k) = k(Result.flatMap(p -> FileTime.accessTime(p), c(filename)))defcreationTime(filename, k) = k(Result.flatMap(p -> FileTime.creationTime(p), c(filename)))defmodificationTime(filename, k) = k(Result.flatMap(p -> FileTime.modificationTime(p), c(filename))) }////// Middleware that intercepts the `FileTime` effect, resolving all file paths/// to their real filesystem targets by following symlinks.////// Value ops (`accessTime`, `creationTime`, `modificationTime`): resolve path; propagate all errors.///pubdefwithFollowLinks(f: Unit -> a \ ef): a \ (ef - FileTime) + FileTime =run { f() } withhandlerFileTime {defaccessTime(filename, k) = k(Result.flatMap(p -> FileTime.accessTime(p), Fs.FsLayer.resolveRealPath(filename)))defcreationTime(filename, k) = k(Result.flatMap(p -> FileTime.creationTime(p), Fs.FsLayer.resolveRealPath(filename)))defmodificationTime(filename, k) = k(Result.flatMap(p -> FileTime.modificationTime(p), Fs.FsLayer.resolveRealPath(filename))) }}