use crate::{
ErrorCode,
db::{
response::{ProjectionRows, RowProjectionOutput},
session::DbSession,
},
diagnostic::RuntimeBoundaryCode,
error::{Error, ErrorOrigin},
traits::CanisterKind,
value::{InputValue, OutputValue},
};
use icydb_core as core;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MutationMode {
Insert,
Replace,
Update,
}
impl MutationMode {
const fn into_core(self) -> core::db::MutationMode {
match self {
Self::Insert => core::db::MutationMode::Insert,
Self::Replace => core::db::MutationMode::Replace,
Self::Update => core::db::MutationMode::Update,
}
}
}
#[derive(Default)]
pub struct StructuralPatch {
inner: core::db::AuthoredStructuralPatch,
}
impl StructuralPatch {
#[must_use]
pub const fn new() -> Self {
Self {
inner: core::db::AuthoredStructuralPatch::new(),
}
}
const fn from_core(inner: core::db::AuthoredStructuralPatch) -> Self {
Self { inner }
}
}
impl<C: CanisterKind> DbSession<C> {
fn projection_selection<E>(
selected_fields: Option<&[String]>,
) -> Result<(Vec<String>, Vec<usize>), Error>
where
E: crate::traits::EntityFor<C>,
{
match selected_fields {
None => Ok((
E::MODEL
.fields()
.iter()
.map(|field| field.name().to_string())
.collect(),
(0..E::MODEL.fields().len()).collect(),
)),
Some(fields) => {
let mut indices = Vec::with_capacity(fields.len());
for field in fields {
let index = E::MODEL
.fields()
.iter()
.position(|candidate| candidate.name() == field.as_str())
.ok_or_else(|| {
Error::from_runtime_boundary(
RuntimeBoundaryCode::RowProjectionFieldNotConfigured,
ErrorOrigin::Query,
)
})?;
indices.push(index);
}
Ok((fields.to_vec(), indices))
}
}
}
pub(crate) fn row_projection_output_from_entities<E>(
entity_name: String,
entities: Vec<E>,
selected_fields: Option<&[String]>,
mut project: impl FnMut(&E, &[usize]) -> Result<Vec<OutputValue>, Error>,
) -> Result<RowProjectionOutput, Error>
where
E: crate::traits::EntityFor<C>,
{
let (columns, indices) = Self::projection_selection::<E>(selected_fields)?;
let mut rows = Vec::with_capacity(entities.len());
for entity in entities {
rows.push(project(&entity, indices.as_slice())?);
}
let row_count = u32::try_from(rows.len()).unwrap_or(u32::MAX);
Ok(RowProjectionOutput::from_projection(
entity_name,
ProjectionRows::new(columns, rows, row_count),
))
}
fn returning_fields<I, S>(fields: I) -> Vec<String>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
fields
.into_iter()
.map(|field| field.as_ref().to_string())
.collect()
}
fn row_projection_output_from_entity<E>(
&self,
entity: E,
selected_fields: Option<&[String]>,
) -> Result<RowProjectionOutput, Error>
where
E: crate::traits::EntityFor<C>,
{
Self::row_projection_output_from_entities::<E>(
E::PATH.to_string(),
vec![entity],
selected_fields,
|entity, slots| {
self.inner
.project_entity_output_values(entity, slots)
.map_err(|_| {
Error::from_error_code(ErrorCode::RUNTIME_INTERNAL, ErrorOrigin::Query)
})
},
)
}
pub fn insert<E>(&self, entity: E) -> Result<E, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self.inner.insert(entity)?)
}
pub fn insert_returning_all<E>(&self, entity: E) -> Result<RowProjectionOutput, Error>
where
E: crate::traits::EntityFor<C>,
{
let entity = self.inner.insert(entity)?;
self.row_projection_output_from_entity::<E>(entity, None)
}
pub fn insert_returning<E, I, S>(
&self,
entity: E,
fields: I,
) -> Result<RowProjectionOutput, Error>
where
E: crate::traits::EntityFor<C>,
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let entity = self.inner.insert(entity)?;
let fields = Self::returning_fields(fields);
self.row_projection_output_from_entity::<E>(entity, Some(fields.as_slice()))
}
pub fn create<I>(&self, input: I) -> Result<I::Entity, Error>
where
I: crate::traits::CreateInputFor<C>,
I::Entity: crate::traits::EntityFor<C>,
{
Ok(self.inner.create(input)?)
}
pub fn create_returning_all<I>(&self, input: I) -> Result<RowProjectionOutput, Error>
where
I: crate::traits::CreateInputFor<C>,
I::Entity: crate::traits::EntityFor<C>,
{
let entity = self.inner.create(input)?;
self.row_projection_output_from_entity::<I::Entity>(entity, None)
}
pub fn create_returning<I, F, S>(
&self,
input: I,
fields: F,
) -> Result<RowProjectionOutput, Error>
where
I: crate::traits::CreateInputFor<C>,
I::Entity: crate::traits::EntityFor<C>,
F: IntoIterator<Item = S>,
S: AsRef<str>,
{
let entity = self.inner.create(input)?;
let fields = Self::returning_fields(fields);
self.row_projection_output_from_entity::<I::Entity>(entity, Some(fields.as_slice()))
}
pub fn insert_many_atomic<E>(
&self,
entities: impl IntoIterator<Item = E>,
) -> Result<Vec<E>, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self.inner.insert_many_atomic(entities)?.entities())
}
pub fn insert_many_non_atomic<E>(
&self,
entities: impl IntoIterator<Item = E>,
) -> Result<Vec<E>, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self.inner.insert_many_non_atomic(entities)?.entities())
}
pub fn replace<E>(&self, entity: E) -> Result<E, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self.inner.replace(entity)?)
}
pub fn replace_many_atomic<E>(
&self,
entities: impl IntoIterator<Item = E>,
) -> Result<Vec<E>, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self.inner.replace_many_atomic(entities)?.entities())
}
pub fn replace_many_non_atomic<E>(
&self,
entities: impl IntoIterator<Item = E>,
) -> Result<Vec<E>, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self.inner.replace_many_non_atomic(entities)?.entities())
}
pub fn update<E>(&self, entity: E) -> Result<E, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self.inner.update(entity)?)
}
pub fn update_returning_all<E>(&self, entity: E) -> Result<RowProjectionOutput, Error>
where
E: crate::traits::EntityFor<C>,
{
let entity = self.inner.update(entity)?;
self.row_projection_output_from_entity::<E>(entity, None)
}
pub fn update_returning<E, I, S>(
&self,
entity: E,
fields: I,
) -> Result<RowProjectionOutput, Error>
where
E: crate::traits::EntityFor<C>,
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let entity = self.inner.update(entity)?;
let fields = Self::returning_fields(fields);
self.row_projection_output_from_entity::<E>(entity, Some(fields.as_slice()))
}
pub fn mutate_structural<E>(
&self,
key: E::Key,
patch: StructuralPatch,
mode: MutationMode,
) -> Result<E, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self
.inner
.mutate_structural::<E>(key, patch.inner, mode.into_core())?)
}
pub fn structural_patch<E, I, S>(&self, fields: I) -> Result<StructuralPatch, Error>
where
E: crate::traits::EntityFor<C>,
I: IntoIterator<Item = (S, InputValue)>,
S: AsRef<str>,
{
let patch = self.inner.structural_patch::<E, _, _, _>(fields)?;
Ok(StructuralPatch::from_core(patch))
}
pub fn update_many_atomic<E>(
&self,
entities: impl IntoIterator<Item = E>,
) -> Result<Vec<E>, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self.inner.update_many_atomic(entities)?.entities())
}
pub fn update_many_non_atomic<E>(
&self,
entities: impl IntoIterator<Item = E>,
) -> Result<Vec<E>, Error>
where
E: crate::traits::EntityFor<C>,
{
Ok(self.inner.update_many_non_atomic(entities)?.entities())
}
}