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