// This code is automatically generated by Senax and is always overwritten.

#![allow(non_snake_case)]

use anyhow::Result;
use senax_common::ShardId;
use std::path::Path;

#[rustfmt::skip]
#[allow(clippy::module_inception)]
pub mod repositories;
#[rustfmt::skip]
pub mod misc {
    macro_rules! fetch {
        ( $conn:ident, $query:ident, $method:ident ) => {
            if $conn.has_read_tx() {
                $query.$method($conn.get_read_tx().await?.as_mut()).await?
    @%- if !config.force_disable_cache %@
            } else if $conn.has_cache_tx() {
                $query.$method($conn.get_cache_tx().await?.as_mut()).await?
    @%- endif %@
            } else if $conn.has_tx() {
                $query.$method($conn.get_tx().await?.as_mut()).await?
            } else {
                $query.$method($conn.get_reader().await?.as_mut()).await?
            }
        };
    }
    pub(crate) use fetch;

    #[allow(unused_macros)]
    macro_rules! assign_sql_no_cache_update {
        ( $obj:ident, $vec:ident, $col:ident, $name:expr, $nullable:expr, $ph:expr ) => {
            if $obj._op.$col != Op::None && $obj._op.$col != Op::Skip {
                $vec.push($obj._op.$col.get_sql($name, $nullable, $ph));
            }
        };
    }
    #[allow(unused_imports)]
    pub(crate) use assign_sql_no_cache_update;

    #[allow(unused_macros)]
    macro_rules! assign_sql {
        ( $obj:ident, $vec:ident, $col:ident, $name:expr, $nullable:expr, $update_cache: ident, $ph:expr ) => {
            if $obj._op.$col != Op::None && $obj._op.$col != Op::Skip {
                $vec.push($obj._op.$col.get_sql($name, $nullable, $ph));
                $update_cache = true;
            }
        };
    }
    #[allow(unused_imports)]
    pub(crate) use assign_sql;

    pub trait ToBindableJson<T> {
    @%- if config.is_mysql() %@
        fn _to_bindable_json(&self) -> String;
    @%- else %@
        fn _to_bindable_json(&self) -> Option<serde_json::Value>;
    @% endif %@
    }

    impl<T> ToBindableJson<T> for T
    where
        T: serde::Serialize,
    {
    @%- if config.is_mysql() %@
        fn _to_bindable_json(&self) -> String {
            let s = serde_json::to_string(self).unwrap();
            assert!(s.len() <= @{ config.max_db_str_len() }@, "Incorrect JSON length.");
            s
        }
    @%- else %@
        fn _to_bindable_json(&self) -> Option<serde_json::Value> {
            Some(serde_json::to_value(self).unwrap())
        }
    @% endif %@
    }

    pub(crate) fn split_by_weight<T, F>(
        values: &[T],
        limit: usize,
        max_items: usize,
        weight: F,
    ) -> Vec<&[T]>
    where
        F: Fn(&T) -> usize,
    {
        assert!(limit > 0);
        assert!(max_items > 0);

        let mut result = Vec::new();

        let mut start = 0;
        let mut sum = 0;
        let mut count = 0;

        for (i, v) in values.iter().enumerate() {
            let w = weight(v);

            if w > limit {
                if count > 0 {
                    result.push(&values[start..i]);
                }
                result.push(&values[i..i + 1]);
                start = i + 1;
                sum = 0;
                count = 0;
                continue;
            }

            if count > 0 && (sum + w > limit || count >= max_items) {
                result.push(&values[start..i]);
                start = i;
                sum = 0;
                count = 0;
            }

            sum += w;
            count += 1;
        }

        if count > 0 {
            result.push(&values[start..]);
        }

        result
    }
}

pub async fn start(db_dir: &Path) -> Result<()> {
    @%- for (name, defs) in groups %@
    repositories::@{ name|snake|ident }@::start(Some(db_dir)).await?;
    @%- endfor %@
    Ok(())
}
pub async fn start_test() -> Result<()> {
    @%- for (name, defs) in groups %@
    repositories::@{ name|snake|ident }@::start(None).await?;
    @%- endfor %@
    Ok(())
}
pub async fn check(shard_id: ShardId) -> Vec<Vec<Result<()>>> {
    vec![
        @%- for (name, defs) in groups %@
        repositories::@{ name|snake|ident }@::check(shard_id).await,
        @%- endfor %@
    ]
}
@{-"\n"}@