sabiql 1.11.0

A fast, driver-less TUI for browsing and editing PostgreSQL databases
Documentation
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Instant;

use tokio::sync::mpsc;

use crate::app::cmd::cache::TtlCache;
use crate::app::cmd::runner::{EffectRunner, EffectRunnerBuilder};
use crate::app::ports::{
    ClipboardError, ClipboardWriter, ConfigWriter, ConnectionStore, DsnBuilder, ErDiagramExporter,
    ErExportResult, ErLogWriter, FolderOpenError, FolderOpener, MetadataProvider, QueryExecutor,
    QueryHistoryError, QueryHistoryStore, ServiceFileError, ServiceFileReader,
};
use crate::app::update::action::Action;
use crate::domain::connection::{ConnectionProfile, ServiceEntry};
use crate::domain::query_history::QueryHistoryEntry;
use crate::domain::{ConnectionId, DatabaseMetadata, ErTableInfo, QueryResult, QuerySource};

pub struct NoopConfigWriter;
impl ConfigWriter for NoopConfigWriter {
    fn get_cache_dir(&self, _project_name: &str) -> color_eyre::eyre::Result<PathBuf> {
        Ok(PathBuf::from("/tmp"))
    }
}

pub struct NoopErExporter;
impl ErDiagramExporter for NoopErExporter {
    fn generate_and_export(
        &self,
        _tables: &[ErTableInfo],
        _filename: &str,
        _cache_dir: &Path,
    ) -> ErExportResult<PathBuf> {
        Ok(PathBuf::from("/tmp/er.svg"))
    }
}

pub struct NoopErLogWriter;
impl ErLogWriter for NoopErLogWriter {
    fn write_er_failure_log(
        &self,
        _failed_tables: Vec<(String, String)>,
        _cache_dir: PathBuf,
    ) -> std::io::Result<()> {
        Ok(())
    }
}

pub struct NoopDsnBuilder;
impl DsnBuilder for NoopDsnBuilder {
    fn build_dsn(&self, _profile: &ConnectionProfile) -> String {
        String::new()
    }
}

pub struct NoopServiceFileReader;
impl ServiceFileReader for NoopServiceFileReader {
    fn read_services(&self) -> Result<(Vec<ServiceEntry>, PathBuf), ServiceFileError> {
        Ok((vec![], PathBuf::new()))
    }
}

pub struct NoopClipboardWriter;
impl ClipboardWriter for NoopClipboardWriter {
    fn copy_text(&self, _content: &str) -> Result<(), ClipboardError> {
        Ok(())
    }
}

pub struct NoopFolderOpener;
impl FolderOpener for NoopFolderOpener {
    fn open(&self, _path: &Path) -> Result<(), FolderOpenError> {
        Ok(())
    }
}

pub struct NoopQueryHistoryStore;
#[async_trait::async_trait]
impl QueryHistoryStore for NoopQueryHistoryStore {
    async fn append(
        &self,
        _project_name: &str,
        _connection_id: &ConnectionId,
        _entry: &QueryHistoryEntry,
    ) -> Result<(), QueryHistoryError> {
        Ok(())
    }

    async fn load(
        &self,
        _project_name: &str,
        _connection_id: &ConnectionId,
    ) -> Result<Vec<QueryHistoryEntry>, QueryHistoryError> {
        Ok(Vec::new())
    }
}

pub fn make_runner(
    metadata_provider: Arc<dyn MetadataProvider>,
    query_executor: Arc<dyn QueryExecutor>,
    connection_store: Arc<dyn ConnectionStore>,
    cache: TtlCache<String, Arc<DatabaseMetadata>>,
    action_tx: mpsc::Sender<Action>,
) -> EffectRunner {
    make_runner_builder(
        metadata_provider,
        query_executor,
        connection_store,
        cache,
        action_tx,
    )
    .build()
}

pub fn make_runner_builder(
    metadata_provider: Arc<dyn MetadataProvider>,
    query_executor: Arc<dyn QueryExecutor>,
    connection_store: Arc<dyn ConnectionStore>,
    cache: TtlCache<String, Arc<DatabaseMetadata>>,
    action_tx: mpsc::Sender<Action>,
) -> EffectRunnerBuilder {
    EffectRunner::builder()
        .metadata_provider(metadata_provider)
        .query_executor(query_executor)
        .dsn_builder(Arc::new(NoopDsnBuilder))
        .er_exporter(Arc::new(NoopErExporter))
        .config_writer(Arc::new(NoopConfigWriter))
        .er_log_writer(Arc::new(NoopErLogWriter))
        .connection_store(connection_store)
        .service_file_reader(Arc::new(NoopServiceFileReader))
        .clipboard(Arc::new(NoopClipboardWriter))
        .folder_opener(Arc::new(NoopFolderOpener))
        .query_history_store(Arc::new(NoopQueryHistoryStore))
        .metadata_cache(cache)
        .action_tx(action_tx)
}

pub fn sample_metadata() -> DatabaseMetadata {
    DatabaseMetadata {
        database_name: "testdb".to_string(),
        schemas: vec![],
        table_summaries: vec![],
        fetched_at: Instant::now(),
    }
}

pub fn sample_query_result() -> QueryResult {
    QueryResult {
        query: "SELECT 1".to_string(),
        columns: vec!["id".to_string()],
        rows: vec![vec!["1".to_string()]],
        row_count: 1,
        execution_time_ms: 5,
        executed_at: Instant::now(),
        source: QuerySource::Preview,
        error: None,
        command_tag: None,
    }
}