---
title: "Filesystem and URI construction"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Filesystem and URI construction}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

The core package contract is a byte-oriented filesystem handle with explicit service
selection.

```{r}
library(Ropendal)

# Local filesystem via constructor.
root <- file.path(tempdir(), "ropendal-abstract-fs")
unlink(root, recursive = TRUE)
dir.create(root, recursive = TRUE)
fs <- opendal("fs", root = root)

# The same contract via URI. Put the local root in the query string so the
# example is portable across POSIX and Windows path syntax.
uri <- paste0("fs:///?root=", utils::URLencode(root, reserved = TRUE))
uri_fs <- opendal_uri(uri)
isTRUE(identical(fs_info(uri_fs)$scheme, fs_info(fs)$scheme))
```

### Root-relative paths

All paths are normalized to the handle root before any operation, so clients can use
portable relative paths.

```{r}
fs_write(fs, "a/b/c.bin", as.raw(c(1, 2, 3)))
fs_read(fs, "a/b/c.bin")
fs_stat(fs, "a/b/c.bin")$path
```

### Range-heavy readers

Index-heavy formats can build a small range request object and pass it to the
same read verbs. This keeps Zarr-like chunk readers, FASTA indexes, and other
binary table lookups on the byte-first API.

```{r}
fs_write(fs, "chunks.bin", as.raw(1:8))
idx <- data.frame(
  path = "chunks.bin",
  offset = c(0, 4),
  size = c(4, 4),
  chunk = c("0.0", "0.1")
)
req <- byte_ranges(idx$path, idx$offset, size = idx$size, id = idx$chunk)
fs_read(fs, req)
```

### Zarr-like byte stores

A byte store is a prefix adapter over the same filesystem handle. It is not a
full array implementation; it provides store-relative keys for metadata and
chunks so higher-level packages can build Zarr-like readers on top.

```{r}
store <- byte_store(fs, "toy.zarr")
store_write(store, "zarr.json", charToRaw('{"zarr_format":3}'))
store_write(store, "c/0/0", as.raw(c(1, 2, 3, 4)))
store_list(store, recursive = TRUE)
as.raw(store_read(store, "c/0/0"))

# Add an explicit local full-object cache for repeated chunk reads.
cached_store <- store_cache(store, tempfile("ropendal-cache-"), validate = "none")
as.raw(collect_aio(store_read_aio(cached_store, "c/0/0")))
```

### Control knobs belong in handles

`runtime_config()`, `layer_concurrent_limit()`, and `layer_timeout()` are attached
at construction time so all operations share the same behavior.

```{r}
fs <- opendal(
  "fs",
  root = root,
  runtime = runtime_config(threads = 2),
  layers = list(
    layer_concurrent_limit(2),
    layer_timeout(request_timeout = 30, io_timeout = 30)
  )
)
```

## Error values are first-class

Failed object creation returns values instead of throwing, which keeps remote
control flow explicit.

```{r}
fs_write(fs, "a.bin", as.raw(1))
second <- fs_write(fs, "a.bin", as.raw(2))
is_error_value(second)
if (is_error_value(second)) error_kind(second) else NULL
```
