postrust_core/plan/
mod.rs1mod call_plan;
7mod mutate_plan;
8mod read_plan;
9mod types;
10
11pub use call_plan::{CallParams, CallPlan};
12pub use mutate_plan::MutatePlan;
13pub use read_plan::{ReadPlan, ReadPlanTree};
14pub use types::*;
15
16use crate::api_request::{Action, ApiRequest, DbAction, QualifiedIdentifier};
17use crate::error::{Error, Result};
18use crate::schema_cache::SchemaCache;
19
20#[derive(Clone, Debug)]
22pub enum ActionPlan {
23 Db(DbActionPlan),
25 Info(InfoPlan),
27}
28
29#[derive(Clone, Debug)]
31pub enum DbActionPlan {
32 Read(ReadPlanTree),
34 MutateRead {
36 mutate: MutatePlan,
37 read: Option<ReadPlanTree>,
38 },
39 Call {
41 call: CallPlan,
42 read: Option<ReadPlanTree>,
43 },
44}
45
46#[derive(Clone, Debug)]
48pub enum InfoPlan {
49 RelationInfo(QualifiedIdentifier),
51 RoutineInfo(QualifiedIdentifier),
53 OpenApiSpec,
55}
56
57pub fn create_action_plan(request: &ApiRequest, schema_cache: &SchemaCache) -> Result<ActionPlan> {
59 match &request.action {
60 Action::Db(db_action) => {
61 if matches!(db_action, DbAction::SchemaRead { .. }) {
63 return Ok(ActionPlan::Info(InfoPlan::OpenApiSpec));
64 }
65 let plan = create_db_plan(request, db_action, schema_cache)?;
66 Ok(ActionPlan::Db(plan))
67 }
68 Action::RelationInfo(qi) => Ok(ActionPlan::Info(InfoPlan::RelationInfo(qi.clone()))),
69 Action::RoutineInfo { qi, .. } => Ok(ActionPlan::Info(InfoPlan::RoutineInfo(qi.clone()))),
70 Action::SchemaInfo => Ok(ActionPlan::Info(InfoPlan::OpenApiSpec)),
71 }
72}
73
74fn create_db_plan(
76 request: &ApiRequest,
77 action: &DbAction,
78 schema_cache: &SchemaCache,
79) -> Result<DbActionPlan> {
80 match action {
81 DbAction::RelationRead { qi, .. } => {
82 let table = schema_cache.require_table(qi)?;
83 let read_plan = ReadPlan::from_request(request, table, schema_cache)?;
84 Ok(DbActionPlan::Read(ReadPlanTree::leaf(read_plan)))
85 }
86
87 DbAction::RelationMut { qi, mutation } => {
88 let table = schema_cache.require_table(qi)?;
89 let mutate_plan = MutatePlan::from_request(request, table, mutation)?;
90
91 let read_plan = if request.preferences.representation.needs_body() {
92 let rp = ReadPlan::for_mutation(request, table, schema_cache)?;
93 Some(ReadPlanTree::leaf(rp))
94 } else {
95 None
96 };
97
98 Ok(DbActionPlan::MutateRead {
99 mutate: mutate_plan,
100 read: read_plan,
101 })
102 }
103
104 DbAction::Routine {
105 qi,
106 invoke_method: _,
107 } => {
108 let routines = schema_cache
109 .get_routines(qi)
110 .ok_or_else(|| Error::FunctionNotFound(qi.to_string()))?;
111
112 let routine = routines
113 .first()
114 .ok_or_else(|| Error::FunctionNotFound(qi.to_string()))?;
115
116 let call_plan = CallPlan::from_request(request, routine)?;
117
118 Ok(DbActionPlan::Call {
119 call: call_plan,
120 read: None,
121 })
122 }
123
124 DbAction::SchemaRead { .. } => {
125 unreachable!("SchemaRead should be handled in create_action_plan")
127 }
128 }
129}
130
131impl crate::api_request::PreferRepresentation {
132 pub fn needs_body(&self) -> bool {
134 matches!(self, Self::Full)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_info_plan() {
144 let qi = QualifiedIdentifier::new("public", "users");
145 let plan = ActionPlan::Info(InfoPlan::RelationInfo(qi.clone()));
146
147 match plan {
148 ActionPlan::Info(InfoPlan::RelationInfo(q)) => {
149 assert_eq!(q.name, "users");
150 }
151 _ => panic!("Expected RelationInfo"),
152 }
153 }
154}