nautilus-orm-codegen 1.0.1

Code generator for Nautilus ORM schema files
Documentation
/// Internal Update builder for {{ model_name }} — use `{{ delegate_name }}::update(args)` instead.
pub(crate) struct {{ update_name }}<E: crate::Executor> {
    client: crate::Client<E>,
    filter: Option<nautilus_core::Expr>,
    require_filter: bool,
    /// Column-value assignments collected from `set_data`.
    assignments: Vec<(nautilus_core::ColumnMarker, nautilus_core::Value)>,
}

impl<E> {{ update_name }}<E>
where
    E: crate::Executor,
    for<'a> E::Row<'a>: Into<crate::Row>,
{
    pub(crate) fn new(client: crate::Client<E>) -> Self {
        {{ update_name }} {
            client,
            filter: None,
            require_filter: true,
            assignments: Vec::new(),
        }
    }

    /// Set WHERE filter.
    #[must_use]
    pub(crate) fn where_(mut self, expr: nautilus_core::Expr) -> Self {
        self.filter = Some(expr);
        self
    }

    /// Update all rows (removes the safety guard that requires a filter).
    #[must_use]
    pub(crate) fn all(mut self) -> Self {
        self.require_filter = false;
        self
    }

    /// Load field assignments from a structured update-input value.
    ///
    /// Only fields that are `Some(…)` in `data` are included in the SET
    /// clause; `None` fields are intentionally skipped (left unchanged).
    /// For nullable fields a `Some(None)` sets the column to NULL.
    #[must_use]
    pub(crate) fn set_data(mut self, data: {{ model_name }}UpdateInput) -> Self {
{%- for field in create_fields %}
        if let Some(outer) = data.{{ field.name }} {
{%- if field.is_optional %}
            let val = match outer {
                Some(v) => nautilus_core::Value::from(v),
                None => nautilus_core::Value::Null,
            };
{%- else %}
            let val = nautilus_core::Value::from(outer);
{%- endif %}
            self.assignments.push((
                nautilus_core::ColumnMarker::new("{{ table_name }}", "{{ field.db_name }}"),
                val,
            ));
        }
{%- endfor %}
        self
    }

    /// Execute the update, returning affected rows via `RETURNING`.
    ///
    /// Returns an empty `Vec` for databases that do not support `RETURNING`
    /// (e.g. MySQL).  Returns an empty `Vec` without touching the database
    /// when no assignments have been loaded via `set_data`.
    pub(crate) async fn exec(self) -> nautilus_core::Result<Vec<{{ model_name }}>> {
        use nautilus_core::*;
        use futures::StreamExt;

        if self.assignments.is_empty() {
            return Ok(vec![]);
        }

        if self.require_filter && self.filter.is_none() {
            return Err(Error::InvalidQuery(
                "Update requires a WHERE filter — call `.all()` to update every row.".to_string(),
            ));
        }

        let mut builder = Update::table("{{ table_name }}");
        for (col, val) in self.assignments {
            builder = builder.set(col, val);
        }
        if let Some(filter) = self.filter {
            builder = builder.filter(filter);
        }

        let supports_returning = self.client.dialect().supports_returning();
        if supports_returning {
            let returning_cols = vec![
{%- for field in all_scalar_fields %}
                ColumnMarker::new("{{ table_name }}", "{{ field.db_name }}"),
{%- endfor %}
            ];
            builder = builder.returning(returning_cols);
        }

        let update = builder.build()?;
        let sql = self.client.dialect().render_update(&update)?;

        let stream = self.client.executor().execute(&sql);
        futures::pin_mut!(stream);

        let mut results = Vec::new();
        while let Some(result) = stream.next().await {
            let row: crate::Row = result.map_err(|e| Error::Other(e.to_string()))?.into();
            results.push(decode_model_row_with_hints(row, all_scalar_value_hints())?);
        }

        Ok(results)
    }
}