fack-core 0.4.0

Foundational no_std APIs for the fack workspace
Documentation

fack

CI

Declarative Rust error derivation with no_std support and zero allocation in generated runtime code.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("file not found {path}")]
struct FileError {
    path: String,
}

Installation

[dependencies]
fack = "0.4.0"

Import the prelude to bring both the derive macro and core::error::Error into scope.

use fack::prelude::*;

Formatting

Use a format string to implement Display.

Named fields can be captured directly.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("invalid input {input:?}")]
struct InputError {
    input: String,
}

Tuple fields use positional captures.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("invalid value {0}")]
struct ValueError(i32);

Explicit Rust expressions are accepted as format arguments.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("normalized {value}", value = input.trim())]
struct NormalizedError {
    input: String,
}

Positional expressions are also supported.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("absolute value {}", value.abs())]
struct AbsoluteError {
    value: i32,
}

Dynamic width and precision may reference fields through normal Rust format syntax.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("value {value:>width$}")]
struct PaddedError {
    value: u32,
    width: usize,
}

Custom display

Use display(path) when formatting belongs in a formatter function.

use fack::prelude::*;

fn render(error: &RenderedError, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    let RenderedError { code } = error;
    write!(f, "rendered {code}")
}

#[derive(Error, Debug)]
#[error(display(render))]
struct RenderedError {
    code: u8,
}

Error sources

Use source(field) to expose an ordinary error source.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("request failed for {url}")]
#[error(source(io))]
struct RequestError {
    io: std::io::Error,
    url: String,
}

Direct, boxed, optional, and optional boxed sources are supported.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("optional failure")]
#[error(source(source))]
struct OptionalError {
    source: Option<Box<std::io::Error>>,
}

Transparent errors

Use transparent(field) to forward Display and source chaining through one non-optional error field.

Additional fields may retain context.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error(transparent(inner))]
struct Wrapper {
    context: u8,
    inner: std::io::Error,
}

Conversion with From

Use from on a declaration with exactly one field. It generates From<T> and uses that field as the ordinary source.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("parse failed")]
#[error(from)]
struct ParseError(std::num::ParseIntError);

The same form works on enum variants.

use fack::prelude::*;

#[derive(Error, Debug)]
enum AppError {
    #[error("io error")]
    #[error(from)]
    Io(std::io::Error),

    #[error("parse error")]
    #[error(from)]
    Parse(std::num::ParseIntError),
}

Enum errors

Each variant declares its own display, source, transparency, or conversion behavior.

use fack::prelude::*;

#[derive(Error, Debug)]
enum LoadError {
    #[error("missing file {0}")]
    Missing(String),

    #[error("failed to read {path}")]
    #[error(source(source))]
    Read {
        path: String,
        source: std::io::Error,
    },

    #[error(transparent(source))]
    Io {
        source: std::io::Error,
        attempts: u8,
    },
}

Generation options

Without an inline declaration, generated methods receive no explicit inline attribute.

#[error(inline)] and #[error(inline(neutral))] emit ordinary #[inline]. The always and never strategies emit the corresponding Rust attributes.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error(inline(never))]
#[error("rare error")]
struct RareError;

Generated paths use ::core by default. Use import(path) to select another root.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error(import(::std))]
#[error("standard error")]
struct StdError;

Enum-wide generation options belong on the enum.

use fack::prelude::*;

#[derive(Error, Debug)]
#[error(inline)]
#[error(import(::std))]
enum ConfiguredError {
    #[error("first error")]
    First,

    #[error("second error")]
    Second,
}

no_std

Generated implementations use core by default, so the facade can be used from no_std crates without switching import roots.

#![no_std]

use fack::prelude::*;

#[derive(Error, Debug)]
#[error("device error {code}")]
struct DeviceError {
    code: u8,
}

Associated crates

  • fack provides the user-facing facade
  • fack-core reserves the foundational no_std API boundary
  • fack-macro provides the derive macro
  • fack-codegen provides the standalone Syn 3 code generation API

License

GPL-3.0

Copyright (C) 2025 W. Frakchi

This program is free software. You can redistribute it and modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License or any later version.

See LICENSE.md for the full text.