gaman-core 0.3.22

Offline core engine for Gaman migrations
Documentation
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::operations::Operation;
use crate::states::types::EntityKind;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Severity {
    Fatal,
    Warning,
    Suggestion,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ClarificationKind {
    RenameTable {
        old: String,
        candidates: Vec<String>,
    },
    RenameColumn {
        table: String,
        old: String,
        candidates: Vec<String>,
    },
    RenameEnumValue {
        enum_name: String,
        old: String,
        candidates: Vec<String>,
    },
    NotNullAdd {
        table: String,
        column: String,
        col_type: String,
    },
    NotNullChange {
        table: String,
        column: String,
    },
    TypeCast {
        table: String,
        column: String,
        from: String,
        to: String,
    },
    UnknownType {
        table: String,
        column: String,
        type_name: String,
        suggested: Vec<String>,
    },
    OpaqueEntity {
        kind: EntityKind,
        name: String,
    },
    UnmanagedTableOptions {
        table: String,
    },
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Clarification {
    pub id: String,
    pub severity: Severity,
    pub kind: ClarificationKind,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "value", rename_all = "snake_case")]
pub enum Answer {
    RenameTo(String),
    RenameNo,
    NotNullDefault(String),
    NotNullNullable,
    NotNullManual,
    TypeCast(String),
    TypeCastImplicit,
    UseType(String),
    KeepType,
    AcceptRisk,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Decision {
    pub clarification_id: String,
    pub answer: Answer,
}

#[derive(Debug, Clone)]
pub enum ClarifyResult {
    NeedsInput(Vec<Clarification>),
    Resolved(Vec<Operation>),
}

#[derive(Debug, Error, PartialEq)]
pub enum ClarifyError {
    #[error("decision references unknown clarification '{0}'")]
    UnknownDecision(String),
    #[error("decision for '{id}' has an answer incompatible with the clarification kind")]
    InvalidAnswer { id: String },
    #[error("decision for '{id}' chose '{chosen}' which is not a valid candidate")]
    InvalidCandidate { id: String, chosen: String },
    #[error("duplicate decision for clarification '{0}'")]
    DuplicateDecision(String),
    #[error("multiple rename decisions target '{target}' in {scope}")]
    ConflictingRenameTarget { scope: String, target: String },
    #[error("decision for '{id}' requires a non-empty value")]
    EmptyInput { id: String },
}

#[derive(Debug, Error)]
pub enum PromptError {
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),
    #[error("{0}")]
    #[allow(dead_code)]
    Other(String),
}

pub trait PromptEngine {
    fn prompt(&self, clarifications: &[Clarification]) -> Result<Vec<Decision>, PromptError>;
}