Skip to main content

tablo_core/resource/
write.rs

1//! The derived record writes a resource's create and update default to.
2
3use toasty::{Executor, stmt::IntoInsert};
4use topcoat::{Result, context::Cx};
5
6use super::{Resource, require_mounted};
7use crate::{
8    form::{Posted, RecordForm},
9    tenancy::require_tenant,
10};
11
12/// The derived create: the form's builder, the request tenant stamped on the
13/// resource's [`Tenancy::column`](crate::Tenancy::column), executed through
14/// `ex`.
15///
16/// A resource whose tenant is inherited ([`Tenancy::via`](crate::Tenancy::via))
17/// has nothing to stamp.
18///
19/// # Errors
20///
21/// A tenantless request on a tenant-scoped resource (the handler answers 403 first),
22/// a misdeclared tenant column (the mount refuses it first), a resource the request's panel does
23/// not mount, or the driver's error.
24pub async fn write_create<R: Resource>(
25    cx: &Cx,
26    form: R::Form,
27    ex: &mut dyn Executor,
28) -> Result<R::Model> {
29    let resource = require_mounted::<R>(cx)?;
30    let mut insert = form.into_create().into_insert();
31    match resource.tenancy.column_field() {
32        Some(Ok(column)) => {
33            insert.set(
34                column.index,
35                toasty_core::stmt::Value::from(require_tenant(cx)?),
36            );
37        }
38        Some(Err(error)) => {
39            return Err(crate::error::declaration(
40                crate::DeclarationError::of::<R>(crate::Site::Tenancy, error).to_string(),
41            ));
42        }
43        None => {}
44    }
45    ex.exec(insert.into())
46        .await
47        .map_err(|error| -> topcoat::Error { error.into() })
48}
49
50/// The derived update: assign every named field and execute through `ex`.
51///
52/// A submission that names no field writes nothing. The instance update
53/// reloads `record`, so the returned row is the written one.
54///
55/// # Errors
56///
57/// The driver's error.
58pub async fn write_update<R: Resource>(
59    _cx: &Cx,
60    mut record: R::Model,
61    posted: Posted<R::Form>,
62    ex: &mut dyn Executor,
63) -> Result<R::Model> {
64    // Build the execution future before awaiting: the builder itself is not
65    // known to be `Send`, the future `exec_update` returns is.
66    {
67        let pending = posted
68            .into_update(&mut record)
69            .map(|update| <R::Form as RecordForm>::exec_update(update, &mut *ex));
70        if let Some(pending) = pending {
71            pending
72                .await
73                .map_err(|error| -> topcoat::Error { error.into() })?;
74        }
75    }
76    Ok(record)
77}