from-pg

Crates.iofrom-pg
lib.rsfrom-pg
version0.2.0
created_at2025-07-09 07:19:23.162549+00
updated_at2025-07-13 06:28:46.089136+00
descriptionTrait for converting a postgres row into a struct
homepagehttps://github.com/Benschar1/from-pg
repositoryhttps://github.com/Benschar1/from-pg
max_upload_size
id1744350
size44,441
(Benschar1)

documentation

https://docs.rs/from-pg

README

from-pg

Derive FromPg on a struct to extract it to generate a mapping between a struct and a tokio-postgres row.

Examples

use from_pg::FromPg;
use tokio_postgres::Row;

#[derive(FromPg)]
pub struct Contact {
    pub id: i64,
    pub name: String,
    pub phone_number: String,
}

let row = client.query_one("SELECT id, name, phone_number FROM contacts", &[]).unwrap();
let contact = Contact::from_pg_default(&row);

If you want to do a conversion after extracting a field using FromSql, you can use the #[frompg(from = T, func = f)] attribute. For instance, the following code first extracts permission using the FromSql implementation for &str, then runs it through the text_to_permission function:

#[derive(FromPg)]
pub struct User {
    pub id: i64,
    pub name: String,
    #[frompg(from = &str, func = text_to_permission)]
    pub permission: Permission,
}

pub fn text_to_permission(s: &str) -> Result<Permission, Box<dyn std::error::Error + Send + Sync>> {
    match s {
        "read" => Ok(Permission::Read),
        "write" => Ok(Permission::Write),
        "admin" => Ok(Permission::Admin),
        _ => Err(Box::<dyn std::error::Error + Send + Sync>::from(
            "invalid permission",
        )),
    }
}

Taking the User example above, the macro generates two new types: UserConfig and UserError.

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UserConfig {
    id: String,
    name: String,
    permission: String,
}

#[derive(Debug, Default)]
pub struct UserError {
    id: Option<
        Box<dyn std::error::Error + Sync + Send>,
    >,
    name: Option<
        Box<dyn std::error::Error + Sync + Send>,
    >,
    permission: Option<
        Box<dyn std::error::Error + Sync + Send>,
    >,
}

UserConfig lets you specify which column name to look for on a per-field basis. For instance, for the query SELECT user_id, username, perm FROM ..., you can configure it as follows:

let _ = User::from_pg(
    row,
    &UserConfig {
        id: "id".to_string(),
        name: "username".to_string(),
        permission: "perm".to_string(),
    },
)

The Config structs also have helper methods:

impl ::std::default::Default for UserConfig {
    fn default() -> Self {
        Self {
            id: String::from("id"),
            name: String::from("name"),
            permission: String::from("permission"),
        }
    }
}

impl UserConfig {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn id(&self) -> &str {
        self.id.as_ref()
    }

    pub fn set_id(self, name: String) -> Self {
        Self { id: name, ..self }
    }

    pub fn prefix_id(self, prefix: String) -> Self {
        let mut name = prefix.clone();
        name.push_str("id");
        Self { id: name, ..self }
    }

    // and so on for each field
}

The from_pg_default method just uses the default Config object.

Commit count: 0

cargo fmt