1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4use crate::operations::Operation;
5use crate::states::types::EntityKind;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum Severity {
10 Fatal,
11 Warning,
12 Suggestion,
13}
14
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16#[serde(tag = "type", rename_all = "snake_case")]
17pub enum ClarificationKind {
18 RenameTable {
19 old: String,
20 candidates: Vec<String>,
21 },
22 RenameColumn {
23 table: String,
24 old: String,
25 candidates: Vec<String>,
26 },
27 RenameEnumValue {
28 enum_name: String,
29 old: String,
30 candidates: Vec<String>,
31 },
32 NotNullAdd {
33 table: String,
34 column: String,
35 col_type: String,
36 },
37 NotNullChange {
38 table: String,
39 column: String,
40 },
41 TypeCast {
42 table: String,
43 column: String,
44 from: String,
45 to: String,
46 },
47 UnknownType {
48 table: String,
49 column: String,
50 type_name: String,
51 suggested: Vec<String>,
52 },
53 OpaqueEntity {
54 kind: EntityKind,
55 name: String,
56 },
57 UnmanagedTableOptions {
58 table: String,
59 },
60}
61
62#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
63pub struct Clarification {
64 pub id: String,
65 pub severity: Severity,
66 pub kind: ClarificationKind,
67}
68
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70#[serde(tag = "type", content = "value", rename_all = "snake_case")]
71pub enum Answer {
72 RenameTo(String),
73 RenameNo,
74 NotNullDefault(String),
75 NotNullNullable,
76 NotNullManual,
77 TypeCast(String),
78 TypeCastImplicit,
79 UseType(String),
80 KeepType,
81 AcceptRisk,
82}
83
84#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
85pub struct Decision {
86 pub clarification_id: String,
87 pub answer: Answer,
88}
89
90#[derive(Debug, Clone)]
91pub enum ClarifyResult {
92 NeedsInput(Vec<Clarification>),
93 Resolved(Vec<Operation>),
94}
95
96#[derive(Debug, Error, PartialEq)]
97pub enum ClarifyError {
98 #[error("decision references unknown clarification '{0}'")]
99 UnknownDecision(String),
100 #[error("decision for '{id}' has an answer incompatible with the clarification kind")]
101 InvalidAnswer { id: String },
102 #[error("decision for '{id}' chose '{chosen}' which is not a valid candidate")]
103 InvalidCandidate { id: String, chosen: String },
104 #[error("duplicate decision for clarification '{0}'")]
105 DuplicateDecision(String),
106 #[error("multiple rename decisions target '{target}' in {scope}")]
107 ConflictingRenameTarget { scope: String, target: String },
108 #[error("decision for '{id}' requires a non-empty value")]
109 EmptyInput { id: String },
110}
111
112#[derive(Debug, Error)]
113pub enum PromptError {
114 #[error("I/O error: {0}")]
115 Io(#[from] std::io::Error),
116 #[error("{0}")]
117 #[allow(dead_code)]
118 Other(String),
119}
120
121pub trait PromptEngine {
122 fn prompt(&self, clarifications: &[Clarification]) -> Result<Vec<Decision>, PromptError>;
123}