Skip to main content

sql_fun_core/extensions/
extension_collection.rs

1use std::{
2    fmt::{Display, Write},
3    path::{Path, PathBuf},
4    str::FromStr,
5};
6
7use combine::{
8    EasyParser, ParseError, Parser, Stream, eof,
9    parser::char::{char, spaces},
10    sep_end_by,
11};
12
13use crate::{ExtensionConfigError, ExtensionNameVersionPair, IVec};
14
15/// collection for postgres extensions
16#[derive(Debug, serde::Deserialize, Clone, Default)]
17#[serde(transparent)]
18pub struct PostgresExtensionsCollection(IVec<ExtensionNameVersionPair>);
19
20impl Display for PostgresExtensionsCollection {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        for (index, item) in self.0.iter().enumerate() {
23            if index == self.0.len() - 1 {
24                write!(f, "{item}")?;
25            } else {
26                write!(f, "{item},")?;
27            }
28        }
29        Ok(())
30    }
31}
32
33impl PostgresExtensionsCollection {
34    ///
35    /// get extension SQL file paths
36    ///
37    /// guranties returned paths exists true
38    ///
39    /// # Errors
40    ///
41    /// Returns [`ExtensionConfigError`] when any referenced SQL file cannot be
42    /// resolved.
43    pub fn sql_files<P: AsRef<Path>>(
44        &mut self,
45        base_path: P,
46    ) -> Result<IVec<PathBuf>, ExtensionConfigError> {
47        let mut result = Vec::new();
48        for item in &mut self.0 {
49            result.push(item.resolve_version(&base_path)?);
50        }
51        Ok(IVec::from(result))
52    }
53
54    /// resolve versions for extensions
55    ///
56    /// # Errors
57    ///
58    /// Returns [`ExtensionConfigError`] when extension versions cannot be
59    /// determined.
60    pub fn resolve_versions<P: AsRef<Path>>(
61        &mut self,
62        base_path: P,
63    ) -> Result<(), ExtensionConfigError> {
64        for item in &mut self.0 {
65            item.resolve_version(&base_path)?;
66        }
67        Ok(())
68    }
69
70    fn list_of_pair_parser<Input>()
71    -> impl Parser<Input, Output = Vec<Result<ExtensionNameVersionPair, ExtensionConfigError>>>
72    where
73        Input: Stream<Token = char>,
74        Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
75    {
76        spaces()
77            .with(sep_end_by(
78                ExtensionNameVersionPair::parser().skip(spaces()),
79                char(',').skip(spaces()),
80            ))
81            .skip(spaces())
82    }
83
84    fn list_of_pair_eof_parser<Input>()
85    -> impl Parser<Input, Output = Vec<Result<ExtensionNameVersionPair, ExtensionConfigError>>>
86    where
87        Input: Stream<Token = char>,
88        Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
89    {
90        Self::list_of_pair_parser().skip(eof())
91    }
92
93    fn parse(input: &str) -> Result<Vec<ExtensionNameVersionPair>, String> {
94        let parsed = Self::list_of_pair_eof_parser()
95            .easy_parse(input)
96            .map_err(|e| e.to_string())?;
97        let mut results = Vec::new();
98        let mut errors = String::new();
99        for item in parsed.0 {
100            match item {
101                Ok(v) => results.push(v),
102                Err(e) => writeln!(&mut errors, "{e}").expect("writing to string"),
103            }
104        }
105        if errors.is_empty() {
106            Ok(results)
107        } else {
108            Err(errors)
109        }
110    }
111}
112
113impl FromStr for PostgresExtensionsCollection {
114    type Err = (String, String);
115
116    fn from_str(s: &str) -> Result<Self, Self::Err> {
117        let parse_result = Self::parse(s);
118        match parse_result {
119            Ok(result) => Ok(Self(IVec::from(result))),
120            Err(msg) => Err((msg, s.to_string())),
121        }
122    }
123}