es-entity 0.10.35

Event Sourcing Entity Framework
Documentation
//! Control and customize the query execution and its response.

/// Controls the sorting order when listing the entities from the database
///
/// `ListDirection` enum is used to specify order when listing entities from the database using [`EsRepo`][crate::EsRepo]
/// generated functions like `list_by`, `list_for` and `list_for_filters`. Has two variants: `Ascending` and `Descending`
///
/// # Examples
///
/// ```ignore
/// // List users by ID in ascending order (oldest first)
/// let paginated_users_asc = users.list_by_id(
///     PaginatedQueryArgs { first: 5, after: None },
///     ListDirection::Ascending, // or just Default::default()
/// ).await?
///
/// // List users by name in descending order (Z to A)
/// let paginated_users_desc = users.list_by_name(
///     PaginatedQueryArgs { first: 5, after: None },
///     ListDirection::Descending,
/// ).await?
/// ```
#[derive(Default, std::fmt::Debug, Clone, Copy)]
pub enum ListDirection {
    /// Sets the default direction variant to `Ascending`
    #[default]
    Ascending,
    Descending,
}

/// Structure to sort entities on a specific field when listing from database
///
/// Sort enum is used to specify the sorting order and the field to sort the entities by when listing them using `list_for_filters`
/// generated by [`EsRepo`][crate::EsRepo]. It is generated automatically for all fields which have `list_by` option. It encapsulates two fields,
/// first is `by` to specify the field name, second is `direction` which takes [`ListDirection`]
///
/// # Example
///
/// ```ignore
/// let result = users.list_for_filters(
///     UserFilters {
///         name: Some("Murphy".to_string()),
///     },
///     Sort {
///         // `UserSortBy::Id` and `UserSortBy::CreatedAt` are created by default,
///         // other columns need `list_by` to sort by
///         by: UserSortBy::Id,
///         direction: ListDirection::Descending,
///     },
///     PaginatedQueryArgs {
///         first: 10,
///         after: Default::default(),
///     },
/// )
/// .await?;
/// ```
#[derive(std::fmt::Debug, Clone, Copy)]
pub struct Sort<T> {
    // T parameter represents the field
    pub by: T,
    pub direction: ListDirection,
}

/// A cursor-based pagination structure for efficiently paginating through large datasets
///
/// The `PaginatedQueryArgs<T>` encapsulates a `first` field that specifies the count of entities to fetch per query, and an optional `after` field
/// that specifies the cursor to start from for the current query. The `<T>` parameter represents cursor type, which depends on the sorting field.
/// A field's cursor is generated by [`EsRepo`][crate::EsRepo] if it has `list_by` option.
///
/// # Examples
///
/// ```ignore
/// // Initial query - fetch first 10 users after an existing user id
/// let query_args = PaginatedQueryArgs {
///     first: 10,
///     after: Some(user_cursor::UserByIdCursor {
///         id: some_existing_user_id // assume this variable exists
///     }),
/// };
///
/// // Execute query using `query_args` argument of `PaginatedQueryArgs` type
/// let result = users.list_by_id(query_args, ListDirection::Ascending).await?;
///
/// // Continue pagination using the updated `next_query_args` of `PaginatedQueryArgs` type
/// if result.has_next_page {
///     let next_query_args = PaginatedQueryArgs {
///         first: 10,
///         after: result.end_cursor, // Use cursor from previous result to update `after`
///     };
///     let next_result = users.list_by_id(next_query_args, ListDirection::Ascending).await?;
/// }
/// ```
#[derive(Debug)]
pub struct PaginatedQueryArgs<T: std::fmt::Debug> {
    /// Specifies the number of entities to fetch per query
    pub first: usize,
    /// Specifies the cursor/marker to start from for current query
    pub after: Option<T>,
}

impl<T: std::fmt::Debug> Clone for PaginatedQueryArgs<T>
where
    T: Clone,
{
    fn clone(&self) -> Self {
        Self {
            first: self.first,
            after: self.after.clone(),
        }
    }
}

impl<T: std::fmt::Debug> Default for PaginatedQueryArgs<T> {
    /// Default value fetches first 100 entities
    fn default() -> Self {
        Self {
            first: 100,
            after: None,
        }
    }
}

/// Return type for paginated queries containing entities and pagination metadata
///
/// `PaginatedQueryRet` contains the fetched entities and utilities for continuing pagination.
/// Returned by the [`EsRepo`][crate::EsRepo] functions like `list_by`, `list_for` and `list_for_filters`.
/// Used with [`PaginatedQueryArgs`] to perform consistent and efficient pagination
///
/// # Examples
///
/// ```ignore
/// let query_args = PaginatedQueryArgs {
///     first: 10,
///     after: None,
/// };
///
/// // Execute query and get the `result` of type `PaginatedQueryRet`
/// let result = users.list_by_id(query_args, ListDirection::Ascending).await?;
///
/// // Continue pagination using the `next_query_args` argument updated using result
/// // Will continue only if 'has_next_page` returned from `result` is true
/// if result.has_next_page {
///     let next_query_args = PaginatedQueryArgs {
///         first: 10,
///         after: result.end_cursor, // update with 'end_cursor' of previous result
///     };
///     let next_result = users.list_by_id(next_query_args, ListDirection::Ascending).await?;
/// }
///
/// // Or use PaginatedQueryRet::into_next_query() convenience method
/// if let Some(next_query_args) = result.into_next_query() {
///     let next_result = users.list_by_id(next_query_args, ListDirection::Ascending).await?;
/// }
/// ```
pub struct PaginatedQueryRet<T, C> {
    /// [Vec] for the fetched `entities` by the paginated query
    pub entities: Vec<T>,
    /// [bool] for indicating if the list has been exhausted or more entities can be fetched
    pub has_next_page: bool,
    /// cursor on the last entity fetched to continue paginated queries.
    pub end_cursor: Option<C>,
}

impl<T, C> PaginatedQueryRet<T, C> {
    /// Convenience method to create next query args if more pages are available
    pub fn into_next_query(self) -> Option<PaginatedQueryArgs<C>>
    where
        C: std::fmt::Debug,
    {
        if self.has_next_page {
            Some(PaginatedQueryArgs {
                first: self.entities.len(),
                after: self.end_cursor,
            })
        } else {
            None
        }
    }
}