Skip to main content

Getting Started

ivo for Rust expects you to define your data model with structs that implement IvoInputStruct (required for input structs) and IvoStruct. This is done via their respective derive macros.

Installation

cargo add ivo

Defining structs

use chrono::{DateTime, Utc};
use ivo::{IvoInputStruct, IvoStruct};

#[derive(Clone, PartialEq, IvoInputStruct)]
struct UserInput {
email: Option<String>,
phone_number: Option<String>,
username: String,
}

type Timestamp = DateTime<Utc>;

#[derive(Clone, PartialEq, IvoStruct)]
struct User {
id: String,
created_at: Timestamp,
email: Option<String>,
phone_number: Option<String>,
updated_at: Option<Timestamp>,
username: String,
username_last_updated_at: Option<Timestamp>,
}

IvoStruct

Deriving IvoStruct on User generates a PartialUser struct, plus helper methods:

impl IvoStruct for User {
fn append_updates(&mut self, updates: &Self::Partial);
fn clone_with_updates(&self, updates: &Self::Partial) -> Self;
}

impl From<User> for PartialUser {
fn from(value: User) -> PartialUser;
}

PartialUser gets a constructor, set_*/with_* builder methods and unset_* methods per field, plus into_option() and is_empty():

struct PartialUser {
id: Option<String>,
created_at: Option<Timestamp>,
email: Option<String>,
phone_number: Option<Option<String>>,
updated_at: Option<Option<Timestamp>>,
username: Option<String>,
username_last_updated_at: Option<Option<Timestamp>>,
}

The #[ivo(...)] attribute customizes generated partial structs and their fields, e.g. to derive Serialize/Deserialize or forward #[serde(...)] attributes onto generated fields - see the Rust README for the full example.

IvoInputStruct

Deriving IvoInputStruct on UserInput automatically implements IvoStruct and additionally generates a UserInputErrors struct, used to return errors from post-validators and grouped required resolvers.

Defining a schema

Fields on a schema fall into one of six categories - see each for rules and a runnable example:

Schema options

Custom context options

Context options let you thread extra data (dependency injection, caching, i18n, ...) through an operation. See the demo.

Custom ErrorSanitizer

The default payload returned for unsuccessful operations has the signature:

type DefaultFieldErrorMetadata = ();

struct FieldError<Metadata: Clone = DefaultFieldErrorMetadata> {
pub reason: String,
pub metadata: Option<Metadata>,
}

type IvoErrorPayload<Metadata: Clone> = HashMap<String, FieldError<Metadata>>;

To customize this payload, provide an implementation of the IvoErrorSanitizer trait - see this example.

API reference

The prose docs above cover the high-level concepts. For the exhaustive generated API reference (types, functions, derive macros), see:

  • docs.rs/crate/ivo — hosted rustdoc for the published crate.
  • crates.io/crates/ivo — crate registry page (versions, dependencies, README).
  • Local rustdoc — run cargo doc --no-deps --open from the rs/ directory to browse the same generated reference locally.