edinet_cli 0.0.2

A command-line interface for EDINET.
use anyhow::Context;
use sea_orm::{
    ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter, QueryOrder, QuerySelect,
};

use crate::store::asr_document_metadata::AsrDocumentMetadata;
use crate::store::entities::document_metadata;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SearchSort {
    SubmitDateDesc,
    SubmitDateAsc,
}

#[derive(Debug)]
pub struct SearchCondition {
    pub query: Option<String>,
    pub query_sec_code: Option<String>,
    pub edinet_code: Option<String>,
    pub sec_code: Option<String>,
    pub jcn: Option<String>,
    pub filer_name: Option<String>,
    pub submitted_date: Option<String>,
    pub submitted_from: Option<String>,
    pub submitted_to: Option<String>,
    pub submitted_year: Option<u16>,
    pub limit: Option<u64>,
    pub offset: u64,
    pub sort: SearchSort,
}

pub async fn search_asr_document_metadatas(
    db: &DatabaseConnection,
    condition: &SearchCondition,
) -> anyhow::Result<Vec<AsrDocumentMetadata>> {
    let mut filters = available_asr_condition()
        .add(document_metadata::Column::WithdrawalStatus.eq("0"))
        .add(document_metadata::Column::DisclosureStatus.eq("0"))
        .add(document_metadata::Column::LegalStatus.ne("0"))
        .add(document_metadata::Column::SubmitDateTime.is_not_null());

    if let Some(query) = &condition.query {
        let mut query_condition = Condition::any()
            .add(document_metadata::Column::FilerName.like(format!("%{query}%")))
            .add(document_metadata::Column::EdinetCode.eq(query))
            .add(document_metadata::Column::Jcn.eq(query));

        if let Some(query_sec_code) = &condition.query_sec_code {
            query_condition =
                query_condition.add(document_metadata::Column::SecCode.eq(query_sec_code));
        }

        filters = filters.add(query_condition);
    }
    if let Some(edinet_code) = &condition.edinet_code {
        filters = filters.add(document_metadata::Column::EdinetCode.eq(edinet_code));
    }
    if let Some(sec_code) = &condition.sec_code {
        filters = filters.add(document_metadata::Column::SecCode.eq(sec_code));
    }
    if let Some(jcn) = &condition.jcn {
        filters = filters.add(document_metadata::Column::Jcn.eq(jcn));
    }
    if let Some(filer_name) = &condition.filer_name {
        filters = filters.add(document_metadata::Column::FilerName.like(format!("%{filer_name}%")));
    }
    if let Some(submitted_date) = &condition.submitted_date {
        filters = filters
            .add(document_metadata::Column::SubmitDateTime.gte(format!("{submitted_date} 00:00")))
            .add(document_metadata::Column::SubmitDateTime.lte(format!("{submitted_date} 23:59")));
    }
    if let Some(submitted_from) = &condition.submitted_from {
        filters = filters
            .add(document_metadata::Column::SubmitDateTime.gte(format!("{submitted_from} 00:00")));
    }
    if let Some(submitted_to) = &condition.submitted_to {
        filters = filters
            .add(document_metadata::Column::SubmitDateTime.lte(format!("{submitted_to} 23:59")));
    }
    if let Some(submitted_year) = condition.submitted_year {
        let (start, end) = submission_year_bounds(submitted_year);
        filters = filters
            .add(document_metadata::Column::SubmitDateTime.gte(start))
            .add(document_metadata::Column::SubmitDateTime.lte(end));
    }

    let mut query = document_metadata::Entity::find()
        .filter(filters)
        .order_by_desc(document_metadata::Column::FileDate)
        .order_by_desc(document_metadata::Column::SeqNumber);

    if let Some(limit) = condition.limit {
        query = query.limit(limit).offset(condition.offset);
    }

    let rows = match condition.sort {
        SearchSort::SubmitDateDesc => {
            query
                .order_by_desc(document_metadata::Column::SubmitDateTime)
                .all(db)
                .await
        }
        SearchSort::SubmitDateAsc => {
            query
                .order_by_asc(document_metadata::Column::SubmitDateTime)
                .all(db)
                .await
        }
    }
    .context("failed to search ASR documents")?;

    Ok(rows.into_iter().map(AsrDocumentMetadata::from).collect())
}

fn submission_year_bounds(year: u16) -> (String, String) {
    (
        format!("{year:04}-01-01 00:00"),
        format!("{year:04}-12-31 23:59"),
    )
}

fn available_asr_condition() -> Condition {
    Condition::all()
        .add(document_metadata::Column::OrdinanceCode.eq("010"))
        .add(document_metadata::Column::DocTypeCode.eq("120"))
        .add(document_metadata::Column::CsvFlag.eq("1"))
}

#[cfg(test)]
mod tests {
    use super::submission_year_bounds;

    #[test]
    fn builds_full_submission_year_bounds() {
        assert_eq!(
            submission_year_bounds(2025),
            ("2025-01-01 00:00".to_owned(), "2025-12-31 23:59".to_owned(),)
        );
    }
}