# io

> Filesystem access — read/write/append files, directories, streaming for large files, and path helpers.

# io

The `io` module reads and writes files, manages directories, streams large files through integer handles, manipulates paths, and watches the filesystem for changes. It is organized into the `file`, `stream`, `path`, and `watch` submodules, with flat top-level shortcuts for the most common operations. Import it with `import io`.

## io.file

| Function | Signature | Description |
| --- | --- | --- |
| `read` | `io.file.read(path: string) → string` | read the whole file as a string |
| `write` | `io.file.write(path: string, content: string) → bool` | write content, truncating; true on success |
| `append` | `io.file.append(path: string, content: string) → bool` | append content, creating if needed |
| `exists` | `io.file.exists(path: string) → bool` | true if the path exists |
| `delete` | `io.file.delete(path: string) → bool` | remove the file; true on success |
| `copy` | `io.file.copy(src: string, dst: string) → bool` | copy `src` to `dst` |
| `rename` | `io.file.rename(src: string, dst: string) → bool` | rename/move `src` to `dst` |
| `size` | `io.file.size(path: string) → int` | file size in bytes; 0 on error |
| `isFile` | `io.file.isFile(path: string) → bool` | true if the path is a regular file |
| `isDir` | `io.file.isDir(path: string) → bool` | true if the path is a directory |
| `mkdir` | `io.file.mkdir(path: string) → bool` | create the directory and parents |
| `readDir` | `io.file.readDir(path: string) → string[]` | list entry names in the directory |

## io.stream

Stream functions operate on an integer **handle** returned by `io.stream.open` (or `-1` on failure). Always `close` the handle when done. Open modes are `"r"`, `"w"`, `"a"`, and `"rw"`.

| Function | Signature | Description |
| --- | --- | --- |
| `open` | `io.stream.open(path: string, mode?: string) → int` | open a file and return a handle (`-1` on error); default mode `"r"` |
| `read` | `io.stream.read(id: int, n: int) → string` | read up to `n` bytes |
| `readLine` | `io.stream.readLine(id: int) → string` | read one line including the trailing newline |
| `readAll` | `io.stream.readAll(id: int) → string` | read the rest of the stream |
| `write` | `io.stream.write(id: int, data: string) → bool` | write data to the stream |
| `seek` | `io.stream.seek(id: int, offset: int) → bool` | seek to `offset` from the start |
| `tell` | `io.stream.tell(id: int) → int` | current position; `-1` on error |
| `eof` | `io.stream.eof(id: int) → bool` | true if end of file reached |
| `close` | `io.stream.close(id: int) → bool` | close the handle |

## io.path

| Function | Signature | Description |
| --- | --- | --- |
| `abs` | `io.path.abs(path: string) → string` | absolute form of the path |
| `ext` | `io.path.ext(path: string) → string` | file extension, including the dot |
| `base` | `io.path.base(path: string) → string` | last path element |
| `dir` | `io.path.dir(path: string) → string` | directory portion of the path |
| `join` | `io.path.join(parts: string[]) → string` | join path elements |
| `clean` | `io.path.clean(path: string) → string` | normalize the path |
| `isAbs` | `io.path.isAbs(path: string) → bool` | true if the path is absolute |
| `rel` | `io.path.rel(basePath: string, targetPath: string) → string` | relative path from base to target |

## io.watch

Watch the filesystem for changes (built on fsnotify). `start` returns an integer
**handle** (`-1` on error) and runs your callback for each event with an object
`{path, op}`, where `op` is one of `"create"`, `"write"`, `"remove"`,
`"rename"`, or `"chmod"`. A watch covers a single path — walk a directory tree
and `add` each subdirectory for recursive watching.

| Function | Signature | Description |
| --- | --- | --- |
| `start` | `io.watch.start(path: string, fn) → int` | watch `path`; `fn({path, op})` fires per event. Returns a handle (`-1` on error) |
| `add` | `io.watch.add(handle: int, path: string) → bool` | add another path to an existing watcher |
| `stop` | `io.watch.stop(handle: int) → bool` | stop watching and free resources |

```goost
import io

let h = io.watch.start("./config", fn(ev) {
    println(ev.op + ": " + ev.path)
})
// ... later ...
io.watch.stop(h)
```

## Top-level shortcuts

| Function | Signature | Description |
| --- | --- | --- |
| `read` | `io.read(path: string) → string` | read the whole file |
| `write` | `io.write(path: string, content: string) → bool` | write content, truncating |
| `append` | `io.append(path: string, content: string) → bool` | append content |
| `exists` | `io.exists(path: string) → bool` | true if the path exists |
| `delete` | `io.delete(path: string) → bool` | remove the file |
| `mkdir` | `io.mkdir(path: string) → bool` | create the directory and parents |
| `readDir` | `io.readDir(path: string) → string[]` | list directory entries |
| `isFile` | `io.isFile(path: string) → bool` | true if a regular file |
| `isDir` | `io.isDir(path: string) → bool` | true if a directory |
| `size` | `io.size(path: string) → int` | file size in bytes |
| `copy` | `io.copy(src: string, dst: string) → bool` | copy a file |
| `rename` | `io.rename(src: string, dst: string) → bool` | rename/move a file |
| `abs` | `io.abs(path: string) → string` | absolute path |
| `ext` | `io.ext(path: string) → string` | file extension |
| `base` | `io.base(path: string) → string` | last path element |
| `dir` | `io.dir(path: string) → string` | directory portion |
| `join` | `io.join(parts: string[]) → string` | join path elements |

## Example

```goost
import io

// quick whole-file read/write
io.write("notes.txt", "hello\nworld\n")
print(io.read("notes.txt"))

// stream a large file line by line via a handle
let h = io.stream.open("notes.txt", "r")
while !io.stream.eof(h) {
    print(io.stream.readLine(h))
}
io.stream.close(h)
```