postrust_core/schema_cache/
routine.rs1use crate::api_request::QualifiedIdentifier;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Clone, Debug, Serialize, Deserialize)]
9pub struct Routine {
10 pub schema: String,
12 pub name: String,
14 pub description: Option<String>,
16 pub params: Vec<RoutineParam>,
18 pub return_type: RetType,
20 #[serde(default)]
24 pub returns_composite: bool,
25 pub volatility: FuncVolatility,
27 pub has_variadic: bool,
29 pub isolation_level: Option<String>,
31 pub settings: Vec<(String, String)>,
33 pub is_procedure: bool,
35 #[serde(default)]
44 pub media_type: Option<String>,
45 #[serde(default)]
51 pub media_base_type: Option<String>,
52 #[serde(default)]
60 pub output_columns: Vec<(String, String)>,
61}
62
63impl Routine {
64 pub fn produced_media_type(&self) -> Option<&str> {
66 match self.media_type.as_deref() {
67 Some("*/*") => Some("application/octet-stream"),
68 other => other,
69 }
70 }
71}
72
73impl Routine {
74 pub fn qualified_identifier(&self) -> QualifiedIdentifier {
76 QualifiedIdentifier::new(&self.schema, &self.name)
77 }
78
79 pub fn is_safe_for_get(&self) -> bool {
81 matches!(
82 self.volatility,
83 FuncVolatility::Immutable | FuncVolatility::Stable
84 )
85 }
86
87 pub fn required_params(&self) -> impl Iterator<Item = &RoutineParam> {
89 self.params.iter().filter(|p| p.required)
90 }
91
92 pub fn find_param(&self, name: &str) -> Option<&RoutineParam> {
94 self.params.iter().find(|p| p.name == name)
95 }
96}
97
98#[derive(Clone, Debug, Serialize, Deserialize)]
100pub struct RoutineParam {
101 pub name: String,
103 pub param_type: String,
105 pub type_max_length: String,
107 pub required: bool,
109 pub variadic: bool,
111}
112
113#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
115pub enum RetType {
116 Single(String),
118 SetOf(String),
120 Table(Vec<(String, String)>),
122 Void,
124}
125
126impl RetType {
127 pub fn is_set_returning(&self) -> bool {
129 matches!(self, Self::SetOf(_) | Self::Table(_))
130 }
131
132 pub fn type_name(&self) -> Option<&str> {
134 match self {
135 Self::Single(t) => Some(t),
136 Self::SetOf(t) => Some(t),
137 Self::Table(_) => None,
138 Self::Void => None,
139 }
140 }
141}
142
143#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
145pub enum FuncVolatility {
146 Immutable,
148 Stable,
150 Volatile,
152}
153
154impl FuncVolatility {
155 pub fn from_char(c: char) -> Self {
156 match c {
157 'i' => Self::Immutable,
158 's' => Self::Stable,
159 _ => Self::Volatile,
160 }
161 }
162}
163
164pub type RoutineMap = HashMap<QualifiedIdentifier, Vec<Routine>>;
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_routine_is_safe_for_get() {
173 let mut routine = Routine {
174 output_columns: Vec::new(),
175 media_type: None,
176 media_base_type: None,
177 schema: "public".into(),
178 name: "get_users".into(),
179 description: None,
180 params: vec![],
181 return_type: RetType::SetOf("users".into()),
182 returns_composite: true,
183 volatility: FuncVolatility::Stable,
184 has_variadic: false,
185 isolation_level: None,
186 settings: vec![],
187 is_procedure: false,
188 };
189
190 assert!(routine.is_safe_for_get());
191
192 routine.volatility = FuncVolatility::Volatile;
193 assert!(!routine.is_safe_for_get());
194 }
195
196 #[test]
197 fn test_ret_type_is_set_returning() {
198 assert!(!RetType::Single("text".into()).is_set_returning());
199 assert!(RetType::SetOf("users".into()).is_set_returning());
200 assert!(RetType::Table(vec![("id".into(), "int".into())]).is_set_returning());
201 assert!(!RetType::Void.is_set_returning());
202 }
203
204 #[test]
205 fn test_func_volatility_from_char() {
206 assert_eq!(FuncVolatility::from_char('i'), FuncVolatility::Immutable);
207 assert_eq!(FuncVolatility::from_char('s'), FuncVolatility::Stable);
208 assert_eq!(FuncVolatility::from_char('v'), FuncVolatility::Volatile);
209 assert_eq!(FuncVolatility::from_char('x'), FuncVolatility::Volatile);
210 }
211}