sabiql 1.10.0

A fast, driver-less TUI for browsing and editing PostgreSQL databases
Documentation
use std::path::Path;

#[derive(Debug, thiserror::Error)]
pub enum GraphvizError {
    #[error(
        "Graphviz (dot) not found. Please install Graphviz (e.g., brew install graphviz on macOS)"
    )]
    NotInstalled,
    #[error("Graphviz failed (exit code {0:?}). Check DOT syntax.")]
    CommandFailed(Option<i32>),
    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),
}

#[derive(Debug, thiserror::Error)]
pub enum ViewerError {
    #[error("Failed to open viewer: {0}")]
    LaunchFailed(#[source] std::io::Error),
}

pub trait GraphvizRunner: Send + Sync {
    fn convert_dot_to_svg(&self, dot_path: &Path, svg_path: &Path) -> Result<(), GraphvizError>;
}

pub trait ViewerLauncher: Send + Sync {
    fn open_file(&self, path: &Path) -> Result<(), ViewerError>;
}

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

    mod graphviz_error {
        use super::*;

        #[test]
        fn not_installed_contains_installation_hint() {
            let error = GraphvizError::NotInstalled;

            let message = format!("{error}");

            assert!(message.contains("brew install graphviz"));
        }

        #[test]
        fn command_failed_contains_exit_code() {
            let error = GraphvizError::CommandFailed(Some(1));

            let message = format!("{error}");

            assert!(message.contains("exit code"));
        }
    }

    mod viewer_error {
        use super::*;

        #[test]
        fn launch_failed_contains_cause() {
            let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "command not found");
            let error = ViewerError::LaunchFailed(io_error);

            let message = format!("{error}");

            assert!(message.contains("command not found"));
        }
    }
}