pgx_sql_entity_graph/
control_file.rs

1/*
2Portions Copyright 2019-2021 ZomboDB, LLC.
3Portions Copyright 2021-2022 Technology Concepts & Design, Inc. <support@tcdi.com>
4
5All rights reserved.
6
7Use of this source code is governed by the MIT license that can be found in the LICENSE file.
8*/
9/*!
10
11`pgx_module_magic!()` related macro expansion for Rust to SQL translation
12
13> Like all of the [`sql_entity_graph`][crate::pgx_sql_entity_graph] APIs, this is considered **internal**
14to the `pgx` framework and very subject to change between versions. While you may use this, please do it with caution.
15
16*/
17use super::{SqlGraphEntity, SqlGraphIdentifier, ToSql};
18use core::convert::TryFrom;
19use std::collections::HashMap;
20
21/// The parsed contents of a `.control` file.
22///
23/// ```rust
24/// use pgx_sql_entity_graph::ControlFile;
25/// use std::convert::TryFrom;
26/// # fn main() -> eyre::Result<()> {
27/// let context = include_str!("../../pgx-examples/custom_types/custom_types.control");
28/// let _control_file = ControlFile::try_from(context)?;
29/// # Ok(())
30/// # }
31/// ```
32#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
33pub struct ControlFile {
34    pub comment: String,
35    pub default_version: String,
36    pub module_pathname: Option<String>,
37    pub relocatable: bool,
38    pub superuser: bool,
39    pub schema: Option<String>,
40}
41
42impl ControlFile {
43    /// Parse a `.control` file.
44    ///
45    /// ```rust
46    /// use pgx_sql_entity_graph::ControlFile;
47    /// # fn main() -> eyre::Result<()> {
48    /// let context = include_str!("../../pgx-examples/custom_types/custom_types.control");
49    /// let _control_file = ControlFile::from_str(context)?;
50    /// # Ok(())
51    /// # }
52    /// ```
53    pub fn from_str(input: &str) -> Result<Self, ControlFileError> {
54        let mut temp = HashMap::new();
55        for line in input.lines() {
56            let parts: Vec<&str> = line.split('=').collect();
57
58            if parts.len() != 2 {
59                continue;
60            }
61
62            let (k, v) = (parts.get(0).unwrap().trim(), parts.get(1).unwrap().trim());
63
64            let v = v.trim_start_matches('\'');
65            let v = v.trim_end_matches('\'');
66
67            temp.insert(k, v);
68        }
69        Ok(ControlFile {
70            comment: temp
71                .get("comment")
72                .ok_or(ControlFileError::MissingField { field: "comment" })?
73                .to_string(),
74            default_version: temp
75                .get("default_version")
76                .ok_or(ControlFileError::MissingField { field: "default_version" })?
77                .to_string(),
78            module_pathname: temp.get("module_pathname").map(|v| v.to_string()),
79            relocatable: temp
80                .get("relocatable")
81                .ok_or(ControlFileError::MissingField { field: "relocatable" })?
82                == &"true",
83            superuser: temp
84                .get("superuser")
85                .ok_or(ControlFileError::MissingField { field: "superuser" })?
86                == &"true",
87            schema: temp.get("schema").map(|v| v.to_string()),
88        })
89    }
90}
91
92impl From<ControlFile> for SqlGraphEntity {
93    fn from(val: ControlFile) -> Self {
94        SqlGraphEntity::ExtensionRoot(val)
95    }
96}
97
98/// An error met while parsing a `.control` file.
99#[derive(Debug, Clone)]
100pub enum ControlFileError {
101    MissingField { field: &'static str },
102}
103
104impl std::fmt::Display for ControlFileError {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        match self {
107            ControlFileError::MissingField { field } => {
108                write!(f, "Missing field in control file! Please add `{}`.", field)?;
109            }
110        };
111        Ok(())
112    }
113}
114
115impl std::error::Error for ControlFileError {}
116
117impl TryFrom<&str> for ControlFile {
118    type Error = ControlFileError;
119
120    fn try_from(input: &str) -> Result<Self, Self::Error> {
121        Self::from_str(input)
122    }
123}
124
125impl ToSql for ControlFile {
126    fn to_sql(&self, _context: &super::PgxSql) -> eyre::Result<String> {
127        let sql = format!(
128            "\
129            /* \n\
130            This file is auto generated by pgx.\n\
131            \n\
132            The ordering of items is not stable, it is driven by a dependency graph.\n\
133            */\
134        "
135        );
136        Ok(sql)
137    }
138}
139
140impl SqlGraphIdentifier for ControlFile {
141    fn dot_identifier(&self) -> String {
142        format!("extension root")
143    }
144    fn rust_identifier(&self) -> String {
145        format!("root")
146    }
147
148    fn file(&self) -> Option<&'static str> {
149        None
150    }
151
152    fn line(&self) -> Option<u32> {
153        None
154    }
155}