sf3 0.2.0

An implementation of the Simple File Format Family in Rust.
Documentation
# SF3

An implementation of SF3 (Simple File Format Family) in Rust. See the spec and reference implementations at <https://shirakumo.org/docs/sf3>.

> [!WARNING]
> This crate is an experimental work-in-progress still early in the design phase. Expect major breaking changes in the near future.
>
> Feedback is appreciated, either via an [issue](https://git.average.name/AverageHelper/sf3-rust/issues/new)
> or contacting me [directly](https://average.name/contact)!

## Features

This crate is a work-in-progress. The following functions have been implemented and tested:

- Read SF3 files from bytes in memory, a file on disc, or any `std::io::Read` reader
- Read metadata or headers alone, for easy parsing of large files
- Create new SF3 files of supported formats
- MIME types and preferred file extensions for all supported formats
- `no_std` support
- Formats
	- Archive
	- Audio
	- Log
	- Text
	- ...

## Roadmap

The following formats and functions have yet to be implemented:

- [ ] Image
- [ ] Model
- [ ] Physics-Model
- [ ] Table (serde serializer?)
- [ ] Vector Graphic
- [ ] Utilities for reading from important parts of SF3 files without needing to load the entire file into memory
- [ ] Utilities for writing parts of SF3 files in-place, without needing to load the entire file into memory

## Examples

See the [`examples`](examples) directory. Run an example using `cargo run --example <example>`

## Dependencies

- Stable Rust (2024 edition)
- [`chrono`](https://github.com/chronotope/chrono) for parsing timestamp values (see the `Archive` and `Log` file types)
- [`const-str`](https://github.com/Nugine/const-str) and [`const_panic`](https://github.com/rodrimati1992/const_panic/) for constructing sized strings from string literals
- [`crc32fast`](https://github.com/srijs/rust-crc32fast) for computing CRC32 checksums
- [`half`](https://github.com/VoidStarKat/half-rs) for handling 16-bit floats [until `f16` stabilizes](https://github.com/rust-lang/rust/issues/116909) (see the `Audio` file type)
- [`mediatype`](https://github.com/picoHz/mediatype) for parsing and serving MIME types
- [`timeout-readwrite`](https://github.com/jcreekmore/timeout-readwrite-rs) for ensuring File I/O doesn't hang when it should time out

## Installation

This crate is available on [crates.io](https://crates.io/crates/sf3) and [our own package registry](https://git.average.name/AverageHelper/-/packages/cargo/sf3/). Add it to your project like so:

```sh
# via crates.io
cargo add sf3

# or via git.avg.name
cargo add --index sparse+https://git.average.name/api/packages/AverageHelper/cargo/ sf3
```

> [!NOTE]
> [crates.io](https://crates.io/) does not allow packages to be published with dependencies on code published outside of [crates.io](https://crates.io/). Only use our package registry if you intend to self-publish. See [The Cargo Book](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-other-registries) for details.

## Usage

Parse from memory:

```rust
use sf3::file::Sf3File;
use sf3::Archive;

let archive_bytes: &[u8] = /* ... */;
let archive = Archive::parse_bytes(archive_bytes).unwrap();
assert_eq!(archive.payload.len(), 2);
```

Parse from file:

```rust
use sf3::file::Sf3File;
use sf3::Archive;

let file = std::fs::File::open("/path/to/example.ar.sf3")?;
let archive = Archive::parse_file(file).unwrap();
assert_eq!(archive.payload.len(), 2);
```

Parse from Reader or custom buffer:

```rust
use sf3::file::Sf3File;
use sf3::Archive;

let stream: impl std::io::Read = /* ... */;
let buf = std::io::BufReader::new(stream);
let archive = Archive::parse_io(Box::new(buf)).unwrap();
assert_eq!(archive.payload.len(), 2);
```

MIME type (`const` compatible):

```rust
use mediatype::MediaType;

// mime-types for specific formats
let archive_type: MediaType = sf3::Archive::MIME;
let audio_type: MediaType = sf3::Archive::MIME;

// mime-type for a general SF3 file (may change in future with IANA registration)
let sf3_type: MediaType = sf3::file::MIME;
```

## Feature flags

### `std`

This feature is enabled by default, and enables support for parsing files from `Read` types, and certain features in `chrono`, `const-str`, `crc32fast`, and `half` which require Rust's `std` crate. All functions work well enough without `std`, including parsing from byte slices and generating new byte representations of SF3 files, so if you're writing a `no_std` crate or targeting an environment where `std` isn't available but `alloc` and `core` are, then you may disable this feature without much consequence.

### `serde`

Disabled by default. Enables `serde` support for `chrono`, `half`, and `mediatype` types.

## Testing

Run code-coverage tests using the `test.sh` script.

Regular unit tests can be run using `cargo test`. Use the `--no-default-features` flag to test `no_std` support.

The `tests/` directory contains sample files to test against. These are copied directly from the [Shirakumo/sf3](https://shirakumo.org/projects/sf3/src/branch/master/samples) repository, and may be out of date.

## Contributing

Suggestions and code contributions are welcome! The codebase lives primarily at [git.average.name](https://git.average.name/AverageHelper/sf3-rust), where you may file issues or pull requests using a fresh account there or an existing account with Codeberg or the like.

A read-only code mirror of this project also exists at [Codeberg](https://codeberg.org/AverageHelper/sf3-rust).

## Acknowledgements

The SF3 spec was written by [Yukari Hafner](https://shinmera.com/) at [Shirakumo](https://shirakumo.org/), under the [zlib License](https://shirakumo.org/projects/sf3/src/branch/master/LICENSE).

The API was somewhat inspired by the [`yaml`](https://crates.io/crates/yaml) and [`toml`](https://crates.io/crates/toml) crates.