qubit-fs 0.2.1

Provider-neutral synchronous and asynchronous filesystem abstraction for Rust
Documentation

Qubit FS

Rust CI Coverage Crates.io Rust License 中文文档

qubit-fs is a provider-neutral, synchronous and asynchronous filesystem abstraction for Rust 1.94 or later. It supplies application-facing concrete facades—FileSystem and AsyncFileSystem—instead of choosing a storage backend or an async runtime for you.

Installation

[dependencies]
qubit-fs = "0.2"

Synchronous APIs are enabled by default. Enable the asynchronous facade explicitly when it is needed:

qubit-fs = { version = "0.2", features = ["async"] }

Quick Start

A report job can keep its reads and writes on FileSystem while provider setup chooses the storage authority. This executable demo creates an isolated local root, writes a report, reads it with a 1 KiB limit, and prints report ready. The temporary directory is removed when the demo ends.

[dependencies]
qubit-fs = "0.2"
qubit-fs-local = "0.9"
tempfile = "3"
use qubit_fs::Path;
use qubit_fs::copy::CopyExecutionRoute;
use qubit_fs::copy::CopyOptions;
use qubit_fs::directory::ListScope;
use qubit_fs::read::ReadOptions;
use qubit_fs::read::PrefixReadTermination;
use qubit_fs::write::WriteOptions;
use qubit_fs_local::LocalFileSystems;
use qubit_fs_local::LocalResourcePolicy;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let directory = tempfile::tempdir()?;
    let policy = LocalResourcePolicy::standard();
    let filesystem = LocalFileSystems::rooted(directory.path(), policy)?;
    let path = Path::parse("/report.txt")?;
    filesystem.write_all(&path, b"report ready", WriteOptions::default())?;
    let bytes = filesystem.read_all(&path, ReadOptions::default(), 1024)?;
    assert_eq!(b"report ready", bytes.as_slice());
    let prefix = filesystem.read_prefix(&path, ReadOptions::default(), 6)?;
    assert_eq!(prefix.bytes(), b"report");
    assert_eq!(prefix.termination(), PrefixReadTermination::LimitReached);
    let target = Path::parse("/report-copy.txt")?;
    let assessment = filesystem.assess_copy(&path, &target, &CopyOptions::default())?;
    assert_eq!(assessment.route(), CopyExecutionRoute::ProviderThenStream);
    assert_eq!(assessment.fallback_rejection(), None);
    let scope = ListScope::Path(Path::root());
    let mut entries = filesystem.list(&scope, Default::default())?;
    assert_eq!(entries.next_entry()?.expect("published report").path, path);
    assert!(entries.next_entry()?.is_none());
    println!("{}", String::from_utf8(bytes)?);
    Ok(())
}

For a report assembled in several writes, create a temporary file under the same logical parent, write through its returned path, and call persist only when generation is complete. The final report name then stays free of incomplete output. If publication fails, use the retained publication fact, source qualification, and publication_target() to decide between retry, cleanup, and read-only reconciliation.

Why This Project Exists

Application code often needs the same read, write, copy, and listing workflow across local disks, object stores, and hosted filesystems, but provider SDKs expose different path models, error shapes, and recovery behavior. Scattering provider details through business logic makes retries, cleanup, and cancellation hard to reason about after a partial failure.

qubit-fs keeps applications on concrete FileSystem and AsyncFileSystem facades while providers implement extension contracts under qubit_fs::spi. Provider discovery, configuration, and credential handling stay in qubit-fs-registry, so the core crate does not embed a backend or async runtime.

What It Provides—and What It Does Not

The stable application surface is the public facade API, typed paths and URIs, explicit listing scopes, bounded reads, and recovery objects that retain publication facts across failed writes, copies, and cancellations. Detailed workflows, error tables, and operational limits are in the user guide; provider integration is documented separately.

The facade makes these semantics explicit:

  • Path is a logical name inside one configured filesystem. Uri is the secret-free canonical addressing form, while ConnectionUri is configuration ingress: it may accept credentials but redacts them in Display and Debug.
  • Default URI parsing uses the fixed standard redaction policy; pass an explicit policy to Uri::parse_with_policy or ConnectionUri::parse_with_policy when application-specific query names must be protected.
  • Copy, rename, writes, and temporary-resource publication (including keep) preserve typed recovery facts. Temporary persistence reports target publication separately from source qualification, and publication_target() retains an earlier confirmed target across rejected retries, cleanup failures, and asynchronous cancellation. Inspect both facts before retrying, cleaning up, or reconciling a visible target.
  • exists returns false only when stat reports NotFound; permission, authentication, timeout, and I/O failures remain errors.
  • DirectoryStream reads entries incrementally. Consume it in a bounded loop instead of assuming that a directory is a preloaded collection.
  • Listing uses explicit ListScope::Path or flat ListScope::Namespace; prefix reads add a byte range only when RangeRead is Guaranteed.

The core crate has no built-in backend and does not select an async runtime. It does not implement cross-filesystem move, guarantee every provider capability, or turn object keys into hierarchical paths without provider-specific rules. Platform roots and authority boundaries come from the configured provider.

Learn More

Testing

# Run tests with the default feature set
cargo test

# Run tests with all declared features
cargo test --all-features

# Project CI checks
./ci-check.sh

# Check code coverage
./coverage.sh

License

Copyright (c) 2025 - 2026. Haixing Hu. All rights reserved.

Licensed under the Apache License, Version 2.0. See LICENSE for the full license text.

Contributing

Contributions are welcome. Please follow the Rust API guidelines, keep public API documentation and tests current, and run ./align-ci.sh to format code and ./ci-check.sh to satisfy CI requirements before submitting a pull request.

Author

Haixing Hu - Qubit Co. Ltd.

Repository: https://github.com/qubit-ltd/rs-fs