pgx_utils/sql_entity_graph/
control_file.rs1use super::{SqlGraphEntity, SqlGraphIdentifier, ToSql};
18use core::convert::TryFrom;
19use std::collections::HashMap;
20use tracing_error::SpanTrace;
21
22#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
34pub struct ControlFile {
35 pub comment: String,
36 pub default_version: String,
37 pub module_pathname: Option<String>,
38 pub relocatable: bool,
39 pub superuser: bool,
40 pub schema: Option<String>,
41}
42
43impl ControlFile {
44 #[tracing::instrument(level = "error")]
55 pub fn from_str(input: &str) -> Result<Self, ControlFileError> {
56 let mut temp = HashMap::new();
57 for line in input.lines() {
58 let parts: Vec<&str> = line.split('=').collect();
59
60 if parts.len() != 2 {
61 continue;
62 }
63
64 let (k, v) = (parts.get(0).unwrap().trim(), parts.get(1).unwrap().trim());
65
66 let v = v.trim_start_matches('\'');
67 let v = v.trim_end_matches('\'');
68
69 temp.insert(k, v);
70 }
71 Ok(ControlFile {
72 comment: temp
73 .get("comment")
74 .ok_or(ControlFileError::MissingField {
75 field: "comment",
76 context: SpanTrace::capture(),
77 })?
78 .to_string(),
79 default_version: temp
80 .get("default_version")
81 .ok_or(ControlFileError::MissingField {
82 field: "default_version",
83 context: SpanTrace::capture(),
84 })?
85 .to_string(),
86 module_pathname: temp.get("module_pathname").map(|v| v.to_string()),
87 relocatable: temp.get("relocatable").ok_or(ControlFileError::MissingField {
88 field: "relocatable",
89 context: SpanTrace::capture(),
90 })? == &"true",
91 superuser: temp.get("superuser").ok_or(ControlFileError::MissingField {
92 field: "superuser",
93 context: SpanTrace::capture(),
94 })? == &"true",
95 schema: temp.get("schema").map(|v| v.to_string()),
96 })
97 }
98}
99
100impl From<ControlFile> for SqlGraphEntity {
101 fn from(val: ControlFile) -> Self {
102 SqlGraphEntity::ExtensionRoot(val)
103 }
104}
105
106#[derive(Debug, Clone)]
108pub enum ControlFileError {
109 MissingField { field: &'static str, context: SpanTrace },
110}
111
112impl std::fmt::Display for ControlFileError {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 match self {
115 ControlFileError::MissingField { field, context } => {
116 write!(f, "Missing field in control file! Please add `{}`.", field)?;
117 context.fmt(f)?;
118 }
119 };
120 Ok(())
121 }
122}
123
124impl std::error::Error for ControlFileError {}
125
126impl TryFrom<&str> for ControlFile {
127 type Error = ControlFileError;
128
129 fn try_from(input: &str) -> Result<Self, Self::Error> {
130 Self::from_str(input)
131 }
132}
133
134impl ToSql for ControlFile {
135 #[tracing::instrument(level = "debug", err, skip(self, _context))]
136 fn to_sql(&self, _context: &super::PgxSql) -> eyre::Result<String> {
137 let sql = format!(
138 "\
139 /* \n\
140 This file is auto generated by pgx.\n\
141 \n\
142 The ordering of items is not stable, it is driven by a dependency graph.\n\
143 */\
144 "
145 );
146 tracing::trace!(%sql);
147 Ok(sql)
148 }
149}
150
151impl SqlGraphIdentifier for ControlFile {
152 fn dot_identifier(&self) -> String {
153 format!("extension root")
154 }
155 fn rust_identifier(&self) -> String {
156 format!("root")
157 }
158
159 fn file(&self) -> Option<&'static str> {
160 None
161 }
162
163 fn line(&self) -> Option<u32> {
164 None
165 }
166}