keelson-gen 0.1.1

keelson's code generator: introspect a live schema, emit readable model .rs files against keelson-models.
Documentation
// @generated by keelson-gen. DO NOT EDIT.
// Regenerate from the schema instead; hand-written code (hooks included)
// belongs outside this directory.

/// The model marker `threads::table()` hangs off.
#[derive(Debug, Clone, Copy)]
pub struct Threads;
/// One row of `threads`.
#[derive(Debug, Clone, PartialEq)]
pub struct Thread {
    pub id: i32,
    pub title: String,
    pub first_message_id: Option<i32>,
    /// Relations, filled by `preload`/`then_load` mods; empty otherwise.
    pub rel: Rel,
}
/// `threads`' relations.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Rel {
    /// Belongs-to `messages`, via `threads.first_message_id`.
    pub first_message: Option<Box<super::messages::Message>>,
    /// Has-many `messages`, via `messages.thread_id`.
    pub messages: Vec<super::messages::Message>,
}
impl keelson_exec::FromRow for Thread {
    fn from_row(row: &mut keelson_exec::Row) -> Result<Self, keelson_exec::ExecError> {
        Ok(Thread {
            id: row.take("id")?,
            title: row.take("title")?,
            first_message_id: row.take("first_message_id")?,
            rel: Rel::default(),
        })
    }
}
/// The three-state setter: unset fields stay out of the statement.
#[derive(Debug, Clone, Default)]
pub struct Setter {
    pub id: keelson_models::Set<i32>,
    pub title: keelson_models::Set<String>,
    pub first_message_id: keelson_models::Set<i32>,
}
/// The entry point: `threads::table().query(…)` / `.insert(…)` / ….
pub fn table() -> Threads {
    Threads
}
pub fn id() -> keelson_models::Column<i32> {
    keelson_models::Column::new("threads", "id")
}
pub fn title() -> keelson_models::Column<String> {
    keelson_models::Column::new("threads", "title")
}
pub fn first_message_id() -> keelson_models::Column<i32> {
    keelson_models::Column::new("threads", "first_message_id")
}
#[allow(clippy::type_complexity)]
fn all_columns() -> (
    keelson_models::Column<i32>,
    keelson_models::Column<String>,
    keelson_models::Column<i32>,
) {
    (id(), title(), first_message_id())
}
impl keelson_models::View for Threads {
    type Row = Thread;
    type Select = keelson_mysql::SelectQuery;
    fn base_select() -> Self::Select {
        keelson_mysql::select((
            keelson_mysql::select::columns(all_columns()),
            keelson_mysql::select::from(keelson_mysql::quote("threads")),
        ))
    }
}
impl keelson_models::Table for Threads {
    type Pk = i32;
    type Setter = Setter;
    type Insert = keelson_mysql::InsertQuery;
    type Update = keelson_mysql::UpdateQuery;
    type Delete = keelson_mysql::DeleteQuery;
    fn insert_query(s: Setter) -> Self::Insert {
        let mut cols: Vec<&'static str> = Vec::new();
        let mut vals: Vec<keelson_core::expr::Expr> = Vec::new();
        s.id.push_into("id", &mut cols, &mut vals);
        s.title.push_into("title", &mut cols, &mut vals);
        s.first_message_id.push_into("first_message_id", &mut cols, &mut vals);
        let mut q = keelson_mysql::insert(
            keelson_mysql::insert::into(keelson_mysql::quote("threads")).columns(cols),
        );
        if !vals.is_empty() {
            q.apply(keelson_mysql::insert::values(vals));
        }
        q
    }
    fn update_query() -> Self::Update {
        keelson_mysql::update(
            keelson_mysql::update::table(keelson_mysql::quote("threads")),
        )
    }
    fn apply_setter(s: Setter, q: &mut Self::Update) {
        if let Some(v) = s.id.into_expr() {
            q.apply(keelson_mysql::update::set_col("id").to(v));
        }
        if let Some(v) = s.title.into_expr() {
            q.apply(keelson_mysql::update::set_col("title").to(v));
        }
        if let Some(v) = s.first_message_id.into_expr() {
            q.apply(keelson_mysql::update::set_col("first_message_id").to(v));
        }
    }
    fn delete_query() -> Self::Delete {
        keelson_mysql::delete(
            keelson_mysql::delete::from(keelson_mysql::quote("threads")),
        )
    }
    fn pk(row: &Thread) -> Self::Pk {
        row.id
    }
}
impl Threads {
    /// A `SELECT` over this model — the dialect-generic path.
    pub fn query(
        self,
        mods: impl keelson_core::Mod<keelson_models::ModelSelect<Threads>>,
    ) -> keelson_models::ModelSelect<Threads> {
        keelson_models::ModelTable::<Threads>::new().query(mods)
    }
    /// An `INSERT` of the setter's set fields, read back by key.
    pub fn insert(self, setter: Setter) -> Insert {
        Insert { setter, mods: Vec::new() }
    }
    /// An `UPDATE` of the setter's set fields — `exec` only.
    pub fn update(
        self,
        setter: Setter,
        mods: impl keelson_core::Mod<keelson_models::ModelUpdate<Threads>>,
    ) -> Update {
        Update(keelson_models::ModelTable::<Threads>::new().update(setter, mods))
    }
    /// A `DELETE` — `exec` only.
    pub fn delete(
        self,
        mods: impl keelson_core::Mod<keelson_models::ModelDelete<Threads>>,
    ) -> Delete {
        Delete(keelson_models::ModelTable::<Threads>::new().delete(mods))
    }
}
/// A pending `INSERT` on `threads`: the setter, held unbuilt so `before_insert` can still rewrite it, plus the deferred Layer 1 mods.
pub struct Insert {
    setter: Setter,
    #[allow(clippy::type_complexity)]
    mods: Vec<Box<dyn FnOnce(&mut keelson_mysql::InsertQuery) + Send>>,
}
impl std::fmt::Debug for Insert {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Insert")
            .field("setter", &self.setter)
            .field("mods", &self.mods.len())
            .finish()
    }
}
impl Insert {
    /// Defer Layer 1 mods onto the eventual statement.
    #[must_use]
    pub fn with(
        mut self,
        mods: impl keelson_core::Mod<keelson_mysql::InsertQuery> + Send + 'static,
    ) -> Self {
        self.mods.push(Box::new(move |q| mods.apply(q)));
        self
    }
    /// Insert, then read the row back by key. **Not** `RETURNING`: the two statements are not atomic — see the module's dialect notes.
    pub async fn one(
        self,
        db: &dyn keelson_exec::Executor,
    ) -> Result<Thread, keelson_exec::ExecError> {
        use keelson_exec::Execute as _;
        let Insert { mut setter, mods } = self;
        <Threads as keelson_models::Table>::before_insert(db, &mut setter).await?;
        let k0 = match &setter.id {
            keelson_models::Set::Value(v) => Some(*v),
            _ => None,
        };
        let mut q = <Threads as keelson_models::Table>::insert_query(setter);
        for m in mods {
            m(&mut q);
        }
        let done = q.execute(db).await?;
        let k0: i32 = k0
            .or_else(|| {
                done
                    .last_insert_id
                    .and_then(|id| {
                        <i32 as std::convert::TryFrom<i64>>::try_from(id).ok()
                    })
            })
            .ok_or_else(|| keelson_exec::ExecError::other(
                "threads: the INSERT set no `id` and MySQL reported no last_insert_id, so the inserted row cannot be read back",
            ))?;
        let row: Thread = by_pk(k0).fetch_one(db).await?;
        <Threads as keelson_models::Table>::after_insert(db, std::slice::from_ref(&row))
            .await?;
        Ok(row)
    }
    /// Insert for the side effect. `after_insert` still runs, with an empty row slice.
    pub async fn exec(
        self,
        db: &dyn keelson_exec::Executor,
    ) -> Result<keelson_exec::ExecResult, keelson_exec::ExecError> {
        use keelson_exec::Execute as _;
        let Insert { mut setter, mods } = self;
        <Threads as keelson_models::Table>::before_insert(db, &mut setter).await?;
        let mut q = <Threads as keelson_models::Table>::insert_query(setter);
        for m in mods {
            m(&mut q);
        }
        let done = q.execute(db).await?;
        <Threads as keelson_models::Table>::after_insert(db, &[]).await?;
        Ok(done)
    }
}
/// The keyed read-back: `threads`'s own columns, filtered by primary key. This is what stands in for `RETURNING` — a second statement, emitted as a function so its SQL is judged like any other.
pub fn by_pk(k0: i32) -> keelson_mysql::SelectQuery {
    keelson_mysql::select((
        keelson_mysql::select::columns(all_columns()),
        keelson_mysql::select::from(keelson_mysql::quote("threads")),
        id().eq(k0),
    ))
}
/// A pending `UPDATE`. `exec` only: with no `RETURNING` there is nothing for an `all` verb to decode.
#[derive(Debug)]
pub struct Update(keelson_models::ModelUpdate<Threads>);
impl Update {
    /// Apply mods written against the concrete statement.
    pub fn apply(&mut self, mods: impl keelson_core::Mod<keelson_mysql::UpdateQuery>) {
        self.0.apply(mods);
    }
    /// Update for the side effect; answers how many rows changed.
    pub async fn exec(
        self,
        db: &dyn keelson_exec::Executor,
    ) -> Result<keelson_exec::ExecResult, keelson_exec::ExecError> {
        self.0.exec(db).await
    }
}
/// A pending `DELETE`. `exec` only, for the same reason as `Update`.
#[derive(Debug)]
pub struct Delete(keelson_models::ModelDelete<Threads>);
impl Delete {
    /// Apply mods written against the concrete statement.
    pub fn apply(&mut self, mods: impl keelson_core::Mod<keelson_mysql::DeleteQuery>) {
        self.0.apply(mods);
    }
    /// Delete for the side effect; answers how many rows went.
    pub async fn exec(
        self,
        db: &dyn keelson_exec::Executor,
    ) -> Result<keelson_exec::ExecResult, keelson_exec::ExecError> {
        self.0.exec(db).await
    }
}
/// Preload mods: the relation joins into the *same* query — no second statement, and deliberately no `.then(…)`. A level below a join has no distinct child set to key on, so a nested path is spelled with `then_load` (see `keelson_models::ThenLoad`).
pub mod preload {
    /// Same-query `LEFT JOIN` preload of the to-one `first_message`.
    pub fn first_message() -> impl keelson_core::Mod<
        keelson_models::ModelSelect<super::Threads>,
    > {
        keelson_core::mod_fn(|q: &mut keelson_models::ModelSelect<super::Threads>| {
            use keelson_mysql::Chain as _;
            use keelson_mysql::Mod as _;
            (
                keelson_mysql::select::left_join(keelson_mysql::quote("messages"))
                    .on(
                        keelson_mysql::quote(("messages", "id"))
                            .eq(keelson_mysql::quote(("threads", "first_message_id"))),
                    ),
                keelson_mysql::select::preload_columns((
                    keelson_mysql::quote(("messages", "id")).as_("first_message.id"),
                    keelson_mysql::quote(("messages", "thread_id"))
                        .as_("first_message.thread_id"),
                    keelson_mysql::quote(("messages", "body")).as_("first_message.body"),
                )),
            )
                .apply(q);
            q.add_mapper_mod(
                keelson_models::mapper_mod(|row, parent: &mut super::Thread| {
                    parent.rel.first_message = first_message_from_preload(row)?
                        .map(Box::new);
                    Ok(())
                }),
            );
        })
    }
    /// Decode the prefixed columns; the joined key column decides a `LEFT JOIN` miss.
    pub fn first_message_from_preload(
        row: &mut keelson_exec::Row,
    ) -> Result<Option<super::super::messages::Message>, keelson_exec::ExecError> {
        if matches!(
            row.value("first_message.id"), None | Some(keelson_mysql::Value::Null)
        ) {
            return Ok(None);
        }
        Ok(
            Some(super::super::messages::Message {
                id: row.take("first_message.id")?,
                thread_id: row.take("first_message.thread_id")?,
                body: row.take("first_message.body")?,
                rel: super::super::messages::Rel::default(),
            }),
        )
    }
}
/// Then-load mods: one keyed, batched query per level of a load path — `then_load::a().then(b::then_load::c())` is two levels, two queries, checked by the compiler.
pub mod then_load {
    /// Load each row's `first_message` (to-one), one keyed query per batch of keys — `.then(…)` hangs the next level of the path off this one.
    pub fn first_message() -> keelson_models::ThenLoad<
        super::Threads,
        super::super::messages::Messages,
        i32,
    > {
        keelson_models::ThenLoad::new(
            |rows: &[super::Thread]| {
                rows.iter().filter_map(|r| r.first_message_id).collect()
            },
            |keys, q| keelson_core::Mod::apply(
                super::super::messages::id().in_(keys),
                q,
            ),
            |rows: &mut [super::Thread], related| {
                keelson_models::attach_to_one(
                    rows,
                    related,
                    |r| r.first_message_id,
                    |c| Some(c.id),
                    |r, c| {
                        r.rel.first_message = c.map(Box::new);
                    },
                );
            },
        )
    }
    /// Load each row's `messages` (to-many), one keyed query per batch of keys — `.then(…)` hangs the next level of the path off this one.
    pub fn messages() -> keelson_models::ThenLoad<
        super::Threads,
        super::super::messages::Messages,
        i32,
    > {
        keelson_models::ThenLoad::new(
            |rows: &[super::Thread]| rows.iter().map(|r| r.id).collect(),
            |keys, q| keelson_core::Mod::apply(
                super::super::messages::thread_id().in_(keys),
                q,
            ),
            |rows: &mut [super::Thread], related| {
                keelson_models::attach_to_many(
                    rows,
                    related,
                    |r| r.id,
                    |c| c.thread_id,
                    |r, cs| {
                        r.rel.messages = cs;
                    },
                );
            },
        )
    }
}