1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
mod from;
mod new;
mod planned;
mod resolve;

use {
	crate::{executor::types::ObjectName, Error, Result, Value},
	serde::Serialize,
	sqlparser::ast::{DataType, Expr},
	std::fmt::Debug,
	thiserror::Error as ThisError,
};
pub use {
	from::TryIntoMethod,
	new::MetaRecipe,
	planned::PlannedRecipe,
	resolve::{Resolve, SimplifyBy},
};

#[derive(ThisError, Serialize, Debug, PartialEq)]
pub enum RecipeError {
	#[error("recipe missing components")]
	MissingComponents,

	#[error("{0} is either invalid or unimplemented")]
	InvalidQuery(String),
	#[error("{0} is invalid or unimplemented")]
	InvalidExpression(Expr),
	#[error("a function is either invalid or unimplemented")]
	InvalidFunction,

	#[error("column '{0:?}' could not be found")]
	MissingColumn(ObjectName),
	#[error("column '{0:?}' could mean various different columns, please be more specific with (table).(column)")]
	AmbiguousColumn(ObjectName),

	#[error("{0} is either invalid or unimplemented")]
	UnimplementedQuery(String),
	#[error("{0} is either invalid or unimplemented")]
	UnimplementedMethod(String),
	#[error("{0} is unimplemented")]
	UnimplementedExpression(Expr),
	#[error("something is unimplemented")]
	Unimplemented,

	#[error("other failure occurred: {0}")]
	Failed(String),

	#[error("this should be impossible, please report")]
	UnreachableAggregatationFailed,
	#[error("this should be impossible, please report")]
	UnreachableAggregateFailed,
	#[error("this should be impossible, please report, failure: {0}")]
	UnreachableNotMethod(String),
	#[error("this should be impossible, please report, failure: {0}")]
	UnreachableNotAggregate(String),
	#[error("this should be impossible, please report")]
	UnreachableNoRow,
	#[error("this should be impossible, please report")]
	Unreachable,
}

#[derive(Debug, Clone, PartialEq)]
pub enum Recipe {
	Ingredient(Ingredient),
	Method(Box<Method>),
}

impl Default for Recipe {
	fn default() -> Self {
		Self::NULL
	}
}

#[derive(Debug, Clone, PartialEq)]
pub enum Ingredient {
	Value(Value),
	Column(usize),
	Aggregate(usize),
}

#[derive(Debug, Clone, PartialEq)]
pub enum Method {
	Value(Value), // Only occurs backwards for eval! Should never be returned outside of a recursive simplification!
	Aggregate(AggregateOperator, Recipe), // Only occurs inside Ingredient::Aggregate. Perhaps this should not be a Method.

	UnaryOperation(UnaryOperator, Recipe),
	BinaryOperation(BinaryOperator, Recipe, Recipe),
	Function(FunctionOperator, Vec<Recipe>),

	Cast(DataType, Recipe),

	Case {
		operand: Option<Recipe>,
		cases: Vec<(Recipe, Recipe)>,
		else_result: Option<Recipe>,
	},
}

// Cannot derive Debug for references. Perhaps these shouldn't consume their operators. TODO.
pub type UnaryOperator = fn(Value) -> Result<Value>;
pub type BinaryOperator = fn(Value, Value) -> Result<Value>;
pub type FunctionOperator = fn(Vec<Value>) -> Result<Value>;
pub type AggregateOperator = fn(Value, Value) -> Result<Value>;

pub trait RecipeUtilities
where
	Self: Sized,
{
	fn as_solution(&self) -> Option<Value>;

	fn confirm_or_err(self, error: Error) -> Result<Value> {
		self.as_solution().ok_or(error)
	}

	fn confirm(self) -> Result<Value> {
		self.confirm_or_err(RecipeError::MissingComponents.into())
	}

	fn simplify_by_basic(self) -> Result<Self>;
}
impl RecipeUtilities for Recipe {
	fn as_solution(&self) -> Option<Value> {
		if let Recipe::Ingredient(Ingredient::Value(value)) = self {
			Some(value.clone())
		} else {
			None
		}
	}
	fn simplify_by_basic(self) -> Result<Self> {
		self.simplify(SimplifyBy::Basic)
	}
}
impl RecipeUtilities for MetaRecipe {
	fn as_solution(&self) -> Option<Value> {
		self.recipe.as_solution()
	}
	fn simplify_by_basic(mut self) -> Result<Self> {
		self.recipe = self.recipe.simplify(SimplifyBy::Basic)?;
		Ok(self)
	}
}
impl RecipeUtilities for PlannedRecipe {
	fn as_solution(&self) -> Option<Value> {
		self.recipe.as_solution()
	}
	fn simplify_by_basic(mut self) -> Result<Self> {
		self.recipe = self.recipe.simplify(SimplifyBy::Basic)?;
		Ok(self)
	}
}

impl Recipe {
	pub const NULL: Recipe = Recipe::Ingredient(Ingredient::Value(Value::Null));
	pub const TRUE: Recipe = Recipe::Ingredient(Ingredient::Value(Value::Bool(true)));
	pub const SINGLE_COLUMN: Recipe = Recipe::Ingredient(Ingredient::Column(0));
}