Example 8: Files, limits, errors

  • file format 6
  • Rust SDK 1.0 preview

Needs the Rust SDK

Write and open a file, open gzip input, cap the input size, and branch on error kinds.

This program also needs flate2 = "1.1" under [dependencies] in its Cargo.toml.

08_files_limits_errors.rsrust
// Files, gzip, a size limit, and handling errors by kind.
use std::io::Write as _;

use flate2::{write::GzEncoder, Compression};
use plxi_sdk::{write, write_to_path, ErrorKind, OpenOptions, Pack, Record};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let records = vec![
        Record::from_json_line(r#"{"k":"ent","id":"e1","t":"Person"}"#)?,
        Record::from_json_line(r#"{"k":"ent","id":"e2","t":"Person"}"#)?,
    ];
    let id = std::process::id();
    let dir = std::env::temp_dir().join(format!("plxi-example-{id}"));
    std::fs::create_dir_all(&dir)?;

    // Write to a file and open it by path.
    let path = dir.join("people.plxi");
    write_to_path(&path, &records, None)?;
    let pack = Pack::open(&path, OpenOptions::default())?;
    assert_eq!(pack.record_count(), 2);

    // A gzip-compressed pack opens the same way: input that starts
    // with the gzip magic bytes is inflated first.
    let plain = write(&records, None)?;
    let mut enc = GzEncoder::new(Vec::new(), Compression::default());
    enc.write_all(&plain)?;
    let gz = enc.finish()?;
    let from_gz = Pack::from_bytes(gz.clone(), OpenOptions::default())?;
    assert_eq!(from_gz.to_bytes(), plain.as_slice());

    // No size limit applies unless one is set. With a limit, both the
    // input and the inflated output are capped.
    let cap = plain.len() as u64 - 1;
    let capped = OpenOptions::default().with_max_input_bytes(cap);
    let err = Pack::from_bytes(gz, capped).unwrap_err();
    assert_eq!(err.kind(), ErrorKind::LimitExceeded);
    assert_eq!(err.details()["max_input_bytes"], cap);

    // Errors carry a stable kind, a message for people, and JSON
    // details.
    let missing = dir.join("nope.plxi");
    let missing = Pack::open(missing, OpenOptions::default()).unwrap_err();
    let what = match missing.kind() {
        ErrorKind::Io => "could not read the file",
        ErrorKind::ChecksumMismatch => "the file is damaged",
        _ => "something else",
    };
    assert_eq!(what, "could not read the file");
    let not_object = Record::from_json_line("[1,2]").unwrap_err();
    assert_eq!(not_object.kind(), ErrorKind::InvalidRecord);
    let cut_short = r#"{"k":"ent","id":"e1""#;
    let cut_short = Record::from_json_line(cut_short).unwrap_err();
    assert_eq!(cut_short.kind(), ErrorKind::Json);
    let key = ErrorKind::from_key("limit_exceeded");
    assert_eq!(key, Some(ErrorKind::LimitExceeded));
    println!("{}", err.to_json());

    std::fs::remove_dir_all(&dir)?;
    Ok(())
}
Output
{"kind":"limit_exceeded","message":"the inflated input is larger than the caller's limit of 460 bytes; inflating stopped after 461 bytes","details":{"inflated_at_least":461,"max_input_bytes":460}}

Ran with cargo run --example 08_files_limits_errors · exit status 0

Explained in: Errors and limits

Sections