/**
 * Effect service for reading, writing, inspecting, and watching files.
 *
 * The `FileSystem` service is the portable boundary between Effect programs and
 * the host file system. Programs depend on the service from `effect/FileSystem`;
 * platform packages provide concrete layers at the edge. Operations return
 * `Effect`, `Stream`, or `Sink` values and report failures as `PlatformError`
 * instead of throwing.
 *
 * **Mental model**
 *
 * `FileSystem` is a capability, not a global singleton. Request it with
 * `yield* FileSystem.FileSystem` inside an effect, compose file work like any
 * other effect, and provide a platform or test implementation when the program
 * is run. Scoped operations such as `open`, `makeTempFileScoped`, and
 * `makeTempDirectoryScoped` bind resource cleanup to `Scope`.
 *
 * **Common tasks**
 *
 * - Create, copy, rename, remove, chmod, chown, link, and symlink paths.
 * - Read and write whole files as bytes or strings.
 * - Stream large files with `stream` and `sink`, using binary size helpers such
 *   as `KiB` and `MiB` for chunk sizes and offsets.
 * - Inspect metadata with `stat`, check accessibility with `access` or
 *   `exists`, and canonicalize paths with `realPath`.
 * - Watch files or directories with `watch` when the platform implementation
 *   supports it.
 *
 * **Example** (Write and clean up a temporary file)
 *
 * ```ts
 * import { Effect, FileSystem } from "effect"
 *
 * const program = Effect.gen(function*() {
 *   const fs = yield* FileSystem.FileSystem
 *
 *   const directory = yield* fs.makeTempDirectoryScoped()
 *   const path = `${directory}/message.txt`
 *
 *   yield* fs.writeFileString(path, "hello")
 *   return yield* fs.readFileString(path)
 * })
 * ```
 *
 * **Gotchas**
 *
 * Paths are interpreted by the provided implementation, so relative paths, case
 * sensitivity, permissions, links, and watch behavior are platform-dependent.
 * Size options are normalized to branded bigint byte counts; prefer the `Size`,
 * `KiB`, `MiB`, and related helpers for offsets, chunk sizes, and truncation
 * lengths. A program that uses this service still needs a concrete layer, such
 * as `NodeFileSystem.layer`, before it can access the real file system.
 *
 * @since 4.0.0
 */
import * as Arr from "./Array.js";
import * as Brand from "./Brand.js";
import * as Cause from "./Cause.js";
import * as Context from "./Context.js";
import * as Effect from "./Effect.js";
import { pipe } from "./Function.js";
import * as Layer from "./Layer.js";
import * as Option from "./Option.js";
import { badArgument, systemError } from "./PlatformError.js";
import { hasProperty } from "./Predicate.js";
import * as Sink from "./Sink.js";
import * as Stream from "./Stream.js";
const TypeId = "~effect/platform/FileSystem";
/**
 * Creates a `Size` from various numeric input types.
 *
 * **Details**
 *
 * Converts numbers, bigints, or existing Size values into a properly
 * branded Size type. This function handles the conversion and ensures
 * type safety for file size operations.
 *
 * **Example** (Converting size inputs)
 *
 * ```ts
 * import { Effect, FileSystem } from "effect"
 *
 * // From number
 * const size1 = FileSystem.Size(1024)
 * console.log(typeof size1) // "bigint"
 *
 * // From bigint
 * const size2 = FileSystem.Size(BigInt(2048))
 *
 * // From existing Size (identity)
 * const size3 = FileSystem.Size(size1)
 *
 * // Use in file operations
 * const readChunk = (path: string, chunkSize: number) =>
 *   Effect.gen(function*() {
 *     const fs = yield* FileSystem.FileSystem
 *     return fs.stream(path, {
 *       chunkSize: FileSystem.Size(chunkSize)
 *     })
 *   })
 * ```
 *
 * @category sizes
 * @since 4.0.0
 */
export const Size = bytes => typeof bytes === "bigint" ? bytes : BigInt(bytes);
/**
 * Creates a `Size` representing kilobytes (1024 bytes).
 *
 * **Details**
 *
 * Converts a number of kilobytes to the equivalent size in bytes.
 * Uses binary kilobytes (1024 bytes) rather than decimal (1000 bytes).
 *
 * **Example** (Creating kibibyte sizes)
 *
 * ```ts
 * import { Effect, FileSystem } from "effect"
 *
 * const program = Effect.gen(function*() {
 *   const fs = yield* FileSystem.FileSystem
 *
 *   // Create a 64 KiB buffer size for streaming
 *   const bufferSize = FileSystem.KiB(64)
 *
 *   const stream = fs.stream("large-file.txt", {
 *     chunkSize: bufferSize
 *   })
 *
 *   // Truncate file to 100 KiB
 *   yield* fs.truncate("data.txt", FileSystem.KiB(100))
 * })
 * ```
 *
 * @category sizes
 * @since 4.0.0
 */
export const KiB = n => Size(n * 1024);
/**
 * Creates a `Size` representing mebibytes (1024² bytes).
 *
 * **Details**
 *
 * Converts a number of mebibytes to the equivalent size in bytes.
 * Uses binary mebibytes (1,048,576 bytes) rather than decimal megabytes.
 *
 * **Example** (Creating mebibyte sizes)
 *
 * ```ts
 * import { Effect, FileSystem } from "effect"
 *
 * const program = Effect.gen(function*() {
 *   const fs = yield* FileSystem.FileSystem
 *
 *   // Set a 10 MiB chunk size for large file operations
 *   const largeChunkSize = FileSystem.MiB(10)
 *
 *   const stream = fs.stream("video.mp4", {
 *     chunkSize: largeChunkSize
 *   })
 *
 *   // Check if file is larger than 100 MiB
 *   const stats = yield* fs.stat("archive.zip")
 *   const maxSize = FileSystem.MiB(100)
 *   if (stats.size > maxSize) {
 *     yield* Effect.log("File is very large!")
 *   }
 * })
 * ```
 *
 * @category sizes
 * @since 4.0.0
 */
export const MiB = n => Size(n * 1024 * 1024);
/**
 * Creates a `Size` representing gibibytes (1024³ bytes).
 *
 * **Details**
 *
 * Converts a number of gibibytes to the equivalent size in bytes.
 * Uses binary gibibytes (1,073,741,824 bytes) rather than decimal gigabytes.
 *
 * **Example** (Creating gibibyte sizes)
 *
 * ```ts
 * import { Effect, FileSystem } from "effect"
 *
 * const program = Effect.gen(function*() {
 *   const fs = yield* FileSystem.FileSystem
 *
 *   // Use GiB values as size thresholds
 *   const maxArchiveSize = FileSystem.GiB(1)
 *   console.log(maxArchiveSize.toString()) // "1073741824"
 *
 *   const tempFile = yield* fs.makeTempFile({ prefix: "archive-" })
 *   yield* fs.writeFileString(tempFile, "backup data")
 *
 *   const info = yield* fs.stat(tempFile)
 *   console.log(info.size < maxArchiveSize) // true
 *
 *   yield* fs.remove(tempFile)
 * })
 * ```
 *
 * @category sizes
 * @since 4.0.0
 */
export const GiB = n => Size(n * 1024 * 1024 * 1024);
/**
 * Creates a `Size` representing tebibytes (1024⁴ bytes).
 *
 * **Details**
 *
 * Converts a number of tebibytes to the equivalent size in bytes.
 * Uses binary tebibytes (1,099,511,627,776 bytes) rather than decimal terabytes.
 *
 * **Example** (Creating tebibyte sizes)
 *
 * ```ts
 * import { Console, Effect, FileSystem } from "effect"
 *
 * const program = Effect.gen(function*() {
 *   const fs = yield* FileSystem.FileSystem
 *
 *   // Check if we're dealing with very large files
 *   const stats = yield* fs.stat("database-backup.sql")
 *   const oneTiB = FileSystem.TiB(1)
 *
 *   if (stats.size > oneTiB) {
 *     yield* Console.log("This is a very large database backup!")
 *
 *     // Use larger chunk sizes for such files
 *     const stream = fs.stream("database-backup.sql", {
 *       chunkSize: FileSystem.MiB(100) // 100 MiB chunks
 *     })
 *   }
 * })
 * ```
 *
 * @category sizes
 * @since 4.0.0
 */
export const TiB = n => Size(n * 1024 * 1024 * 1024 * 1024);
const bigint1024 = /*#__PURE__*/BigInt(1024);
const bigintPiB = bigint1024 * bigint1024 * bigint1024 * bigint1024 * bigint1024;
/**
 * Creates a `Size` representing pebibytes (1024⁵ bytes).
 *
 * **Details**
 *
 * Converts a number of pebibytes to the equivalent size in bytes.
 * Uses binary pebibytes (1,125,899,906,842,624 bytes) rather than decimal petabytes.
 * This function uses BigInt arithmetic to handle the very large numbers involved.
 *
 * **Example** (Creating pebibyte sizes)
 *
 * ```ts
 * import { Console, Effect, FileSystem } from "effect"
 *
 * const program = Effect.gen(function*() {
 *   const fs = yield* FileSystem.FileSystem
 *
 *   // For extremely large data processing scenarios
 *   const massiveDataset = FileSystem.PiB(2) // 2 PiB
 *
 *   // This would typically be used in enterprise/cloud scenarios
 *   yield* Console.log(`Processing ${massiveDataset} bytes of data`)
 *
 *   // Such large files would require specialized streaming
 *   const stream = fs.stream("massive-dataset.bin", {
 *     chunkSize: FileSystem.GiB(1), // 1 GiB chunks
 *     offset: FileSystem.TiB(100) // Start from 100 TiB offset
 *   })
 * })
 * ```
 *
 * @category sizes
 * @since 4.0.0
 */
export const PiB = n => Size(BigInt(n) * bigintPiB);
/**
 * Service tag for platform file-system operations.
 *
 * **When to use**
 *
 * Use to access or provide operations for files, directories, permissions,
 * streams, and sinks through the Effect context.
 *
 * **Details**
 *
 * This key is used to provide and access the FileSystem service in the Effect context.
 *
 * **Example** (Accessing and providing FileSystem)
 *
 * ```ts
 * import { Effect, FileSystem } from "effect"
 *
 * // Access the FileSystem service
 * const program = Effect.gen(function*() {
 *   const fs = yield* FileSystem.FileSystem
 *
 *   const exists = yield* fs.exists("./data.txt")
 *   if (exists) {
 *     const content = yield* fs.readFileString("./data.txt")
 *     yield* Effect.log("File content:", content)
 *   }
 * })
 *
 * // Provide a custom FileSystem implementation
 * declare const platformImpl: Omit<
 *   FileSystem.FileSystem,
 *   "exists" | "readFileString" | "stream" | "sink" | "writeFileString"
 * >
 * const customFs = FileSystem.make(platformImpl)
 *
 * const withCustomFs = Effect.provideService(
 *   program,
 *   FileSystem.FileSystem,
 *   customFs
 * )
 * ```
 *
 * @category services
 * @since 4.0.0
 */
export const FileSystem = /*#__PURE__*/Context.Service("effect/platform/FileSystem");
/**
 * Creates a FileSystem implementation from a partial implementation.
 *
 * **When to use**
 *
 * Use to build a concrete `FileSystem` service from platform-specific core
 * operations while deriving the convenience methods that can be implemented
 * from them.
 *
 * **Details**
 *
 * This function takes a partial FileSystem implementation and automatically provides
 * default implementations for `exists`, `readFileString`, `stream`, `sink`, and
 * `writeFileString` methods based on the provided core methods.
 *
 * @see {@link makeNoop} for a testing stub that accepts method overrides without requiring a complete implementation
 * @see {@link layerNoop} for providing a no-op `FileSystem` as a `Layer` in tests
 *
 * @category constructors
 * @since 4.0.0
 */
export const make = impl => FileSystem.of({
  ...impl,
  [TypeId]: TypeId,
  exists: path => pipe(impl.access(path), Effect.as(true), Effect.catchTag("PlatformError", e => e.reason._tag === "NotFound" ? Effect.succeed(false) : Effect.fail(e))),
  readFileString: (path, encoding) => Effect.flatMap(impl.readFile(path), _ => Effect.try({
    try: () => new TextDecoder(encoding).decode(_),
    catch: cause => badArgument({
      module: "FileSystem",
      method: "readFileString",
      description: "invalid encoding",
      cause
    })
  })),
  stream: Effect.fnUntraced(function* (path, options) {
    const file = yield* impl.open(path, {
      flag: "r"
    });
    if (options?.offset) {
      yield* file.seek(options.offset, "start");
    }
    const bytesToRead = options?.bytesToRead !== undefined ? Size(options.bytesToRead) : undefined;
    let totalBytesRead = BigInt(0);
    const chunkSize = Size(options?.chunkSize ?? 64 * 1024);
    const readChunk = file.readAlloc(chunkSize);
    return Stream.fromPull(Effect.succeed(Effect.flatMap(Effect.suspend(() => {
      if (bytesToRead !== undefined && bytesToRead <= totalBytesRead) {
        return Cause.done();
      }
      return bytesToRead !== undefined && bytesToRead - totalBytesRead < chunkSize ? file.readAlloc(bytesToRead - totalBytesRead) : readChunk;
    }), Option.match({
      onNone: () => Cause.done(),
      onSome: buf => {
        totalBytesRead += BigInt(buf.length);
        return Effect.succeed(Arr.of(buf));
      }
    }))));
  }, Stream.unwrap),
  sink: (path, options) => pipe(impl.open(path, {
    flag: "w",
    ...options
  }), Effect.map(file => Sink.forEach(_ => file.writeAll(_))), Sink.unwrap),
  writeFileString: (path, data, options) => Effect.flatMap(Effect.try({
    try: () => new TextEncoder().encode(data),
    catch: cause => badArgument({
      module: "FileSystem",
      method: "writeFileString",
      description: "could not encode string",
      cause
    })
  }), _ => impl.writeFile(path, _, options))
});
const notFound = (method, path) => systemError({
  module: "FileSystem",
  method,
  _tag: "NotFound",
  description: "No such file or directory",
  pathOrDescriptor: path
});
/**
 * Creates a stub `FileSystem` implementation for tests.
 *
 * **Details**
 *
 * By default, `exists` returns `false`, `remove` succeeds, many file operations
 * fail with `PlatformError` `NotFound`, and temporary-directory/file operations
 * die as not implemented. Pass method overrides to provide the behavior needed
 * by a specific test without touching the real file system.
 *
 * **Example** (Creating a no-op FileSystem)
 *
 * ```ts
 * import { Effect, FileSystem, PlatformError } from "effect"
 *
 * // Create a test filesystem that only allows reading specific files
 * const testFs = FileSystem.makeNoop({
 *   readFileString: (path) => {
 *     if (path === "test-config.json") {
 *       return Effect.succeed("{\"test\": true}")
 *     }
 *     return Effect.fail(
 *       PlatformError.systemError({
 *         _tag: "NotFound",
 *         module: "FileSystem",
 *         method: "readFileString",
 *         description: "File not found",
 *         pathOrDescriptor: path
 *       })
 *     )
 *   },
 *   exists: (path) => Effect.succeed(path === "test-config.json")
 * })
 *
 * // Use in tests
 * const program = Effect.gen(function*() {
 *   const content = yield* testFs.readFileString("test-config.json")
 *   // Will succeed with mocked content
 * })
 *
 * // Test with the no-op filesystem
 * const testProgram = Effect.provideService(
 *   program,
 *   FileSystem.FileSystem,
 *   testFs
 * )
 * ```
 *
 * @category constructors
 * @since 4.0.0
 */
export const makeNoop = fileSystem => FileSystem.of({
  [TypeId]: TypeId,
  access(path) {
    return Effect.fail(notFound("access", path));
  },
  chmod(path) {
    return Effect.fail(notFound("chmod", path));
  },
  chown(path) {
    return Effect.fail(notFound("chown", path));
  },
  copy(path) {
    return Effect.fail(notFound("copy", path));
  },
  copyFile(path) {
    return Effect.fail(notFound("copyFile", path));
  },
  exists() {
    return Effect.succeed(false);
  },
  link(path) {
    return Effect.fail(notFound("link", path));
  },
  makeDirectory() {
    return Effect.die("not implemented");
  },
  makeTempDirectory() {
    return Effect.die("not implemented");
  },
  makeTempDirectoryScoped() {
    return Effect.die("not implemented");
  },
  makeTempFile() {
    return Effect.die("not implemented");
  },
  makeTempFileScoped() {
    return Effect.die("not implemented");
  },
  open(path) {
    return Effect.fail(notFound("open", path));
  },
  readDirectory(path) {
    return Effect.fail(notFound("readDirectory", path));
  },
  readFile(path) {
    return Effect.fail(notFound("readFile", path));
  },
  readFileString(path) {
    return Effect.fail(notFound("readFileString", path));
  },
  readLink(path) {
    return Effect.fail(notFound("readLink", path));
  },
  realPath(path) {
    return Effect.fail(notFound("realPath", path));
  },
  remove() {
    return Effect.void;
  },
  rename(oldPath) {
    return Effect.fail(notFound("rename", oldPath));
  },
  sink(path) {
    return Sink.fail(notFound("sink", path));
  },
  stat(path) {
    return Effect.fail(notFound("stat", path));
  },
  stream(path) {
    return Stream.fail(notFound("stream", path));
  },
  symlink(fromPath) {
    return Effect.fail(notFound("symlink", fromPath));
  },
  truncate(path) {
    return Effect.fail(notFound("truncate", path));
  },
  utimes(path) {
    return Effect.fail(notFound("utimes", path));
  },
  watch(path) {
    return Stream.fail(notFound("watch", path));
  },
  writeFile(path) {
    return Effect.fail(notFound("writeFile", path));
  },
  writeFileString(path) {
    return Effect.fail(notFound("writeFileString", path));
  },
  ...fileSystem
});
/**
 * Creates a Layer that provides a no-op FileSystem implementation for testing.
 *
 * **Details**
 *
 * This is a convenience function that wraps `makeNoop` in a Layer, making it easy
 * to provide the test filesystem to your Effect programs.
 *
 * **Example** (Providing a no-op FileSystem layer)
 *
 * ```ts
 * import { Effect, FileSystem } from "effect"
 *
 * // Create a test layer with specific behaviors
 * const testLayer = FileSystem.layerNoop({
 *   readFileString: (path) => Effect.succeed("mocked content"),
 *   exists: () => Effect.succeed(true)
 * })
 *
 * const program = Effect.gen(function*() {
 *   const fs = yield* FileSystem.FileSystem
 *   const content = yield* fs.readFileString("any-file.txt")
 *   return content
 * })
 *
 * // Provide the test layer
 * const testProgram = Effect.provide(program, testLayer)
 * ```
 *
 * @category layers
 * @since 4.0.0
 */
export const layerNoop = fileSystem => Layer.succeed(FileSystem)(makeNoop(fileSystem));
/**
 * Runtime type identifier attached to `FileSystem.File` handles and used by
 * `isFile` to recognize them.
 *
 * **Details**
 *
 * This marker is part of the runtime representation of file handles. Prefer
 * `isFile` when narrowing unknown values.
 *
 * @see {@link File} for the open file handle shape that carries this marker
 * @see {@link isFile} for the public guard that checks this marker
 *
 * @category type IDs
 * @since 4.0.0
 */
export const FileTypeId = "~effect/platform/FileSystem/File";
/**
 * Returns `true` if a value is a `File` handle by checking for the
 * `FileTypeId` marker.
 *
 * **When to use**
 *
 * Use when accepting an unknown value and you need to narrow it to a `File`
 * before calling file-handle operations.
 *
 * **Details**
 *
 * This is a structural marker check. It does not validate the marker value or
 * the shape of the file handle.
 *
 * @see {@link File} for the file-handle interface narrowed by this guard
 * @see {@link FileTypeId} for the runtime marker checked by this guard
 *
 * @category file
 * @since 4.0.0
 */
export const isFile = u => hasProperty(u, FileTypeId);
/**
 * Creates a `File.Descriptor` from a number.
 *
 * **When to use**
 *
 * Use to brand an operating-system file descriptor number when implementing a
 * `FileSystem` that returns custom `File` handles.
 *
 * **Details**
 *
 * `File.Descriptor` is a branded integer handle used by operating systems to
 * identify open files.
 *
 * **Gotchas**
 *
 * This constructor is nominal and does not check that the number is an integer
 * or that it refers to an open file descriptor.
 *
 * @see {@link File.Descriptor} for the branded descriptor type produced by this constructor
 * @see {@link File} for file handles that expose a descriptor through `fd`
 *
 * @category constructors
 * @since 4.0.0
 */
export const FileDescriptor = /*#__PURE__*/Brand.nominal();
/**
 * Service key for file system watch backend implementations.
 *
 * **Details**
 *
 * This service provides the low-level file watching capabilities that can be
 * implemented differently on various platforms (e.g., inotify on Linux,
 * FSEvents on macOS, etc.).
 *
 * **Example** (Providing a custom watch backend)
 *
 * ```ts
 * import { Effect, FileSystem, Option, Stream } from "effect"
 *
 * // Custom watch backend implementation
 * const customWatchBackend = {
 *   register: (path: string, stat: FileSystem.File.Info) => {
 *     // Implementation would depend on platform
 *     return Option.some(Stream.empty) // Placeholder implementation
 *   }
 * }
 *
 * // Provide custom watch backend
 * const program = Effect.gen(function*() {
 *   const fs = yield* FileSystem.FileSystem
 *
 *   // File watching will use the custom backend
 *   const watcher = fs.watch("./directory")
 * })
 *
 * const withCustomBackend = Effect.provideService(
 *   program,
 *   FileSystem.WatchBackend,
 *   customWatchBackend
 * )
 * ```
 *
 * @category file watcher
 * @since 4.0.0
 */
export class WatchBackend extends /*#__PURE__*/Context.Service()("effect/platform/FileSystem/WatchBackend") {}
//# sourceMappingURL=FileSystem.js.map