[go: up one dir, main page]

Skip to main content

sql_docs/
source.rs

1//! Module for structuring the Sql input
2use crate::files::{SqlContent, SqlContentSet};
3use std::{
4    io,
5    path::{Path, PathBuf},
6};
7
8/// Holds the optional path and content of the `sql` to be parsed
9#[derive(Debug)]
10pub struct SqlSource {
11    path: Option<PathBuf>,
12    content: String,
13}
14
15impl SqlSource {
16    /// Loads a [`SqlSource`] from the given path.
17    ///
18    /// # Errors
19    /// - Returns an [`io::Error`] if the file cannot be read.
20    pub fn from_path(path: &Path) -> io::Result<Self> {
21        let source = SqlContent::from_path(path)?;
22        let content = source.content().to_owned();
23        Ok(Self { path: Some(path.to_owned()), content })
24    }
25
26    /// Creates an [`SqlSource`] from a a [`String`] and a [`Option<PathBuf>`]
27    #[must_use]
28    pub const fn from_str(content: String, path: Option<PathBuf>) -> Self {
29        Self { path, content }
30    }
31
32    /// Returns the filesystem path associated with this SQL file.
33    #[must_use]
34    pub fn path(&self) -> Option<&Path> {
35        self.path.as_deref()
36    }
37    /// Returns the [`PathBuf`] for the current path
38    #[must_use]
39    pub fn path_into_path_buf(&self) -> Option<PathBuf> {
40        self.path.clone()
41    }
42
43    /// Returns the raw SQL text contained in this file.
44    #[must_use]
45    pub fn content(&self) -> &str {
46        &self.content
47    }
48
49    /// Recursively discovers `.sql` files under `path`, applies the optional
50    /// deny list, and loads the contents of each file.
51    ///
52    /// # Parameters
53    ///
54    /// - `path`: Root directory to scan recursively for `.sql` files.
55    /// - `deny_list`: Optional list of path-like strings representing files
56    ///
57    /// # Errors
58    ///
59    /// Returns an [`io::Error`] if directory traversal fails or if any of the
60    /// discovered SQL files cannot be read.
61    pub fn sql_sources(path: &Path, deny_list: &[String]) -> io::Result<Vec<Self>> {
62        let sql_content_set = SqlContentSet::new(path, deny_list)?;
63
64        let files_contents = sql_content_set
65            .iter()
66            .map(|p| Self::from_path(p.path()))
67            .collect::<io::Result<Vec<_>>>()?;
68
69        Ok(files_contents)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use std::{env, fs, path::PathBuf};
76
77    use crate::source::SqlSource;
78
79    #[test]
80    fn test_sql_file_read() -> Result<(), Box<dyn std::error::Error>> {
81        let base = env::temp_dir().join("recursive_scan_test3");
82        let _ = fs::remove_dir_all(&base);
83        fs::create_dir_all(&base)?;
84        let sub = base.join("subdir");
85        fs::create_dir_all(&sub)?;
86        let file1 = base.join("one.sql");
87        let file2 = sub.join("two.sql");
88        let non_sql1 = base.join("ignore.txt");
89        let non_sql2 = sub.join("README.md");
90        fs::File::create(&file1)?;
91        fs::File::create(&file2)?;
92        fs::File::create(&non_sql1)?;
93        fs::File::create(&non_sql2)?;
94        let sql_statement = "CREATE TABLE users( id INTEGER PRIMARY KEY);";
95        fs::write(&file1, sql_statement)?;
96        fs::write(&file2, sql_statement)?;
97        let mut expected = vec![&file1, &file2];
98        expected.sort();
99        let mut found: Vec<&PathBuf> = Vec::new();
100        let sql_file_set = SqlSource::sql_sources(&base, &[])?;
101        for file in &sql_file_set {
102            assert_eq!(file.content, sql_statement);
103            if let Some(p) = &file.path {
104                found.push(p);
105            }
106        }
107        assert_eq!(found, expected);
108        let _ = fs::remove_dir_all(&base);
109        Ok(())
110    }
111
112    #[test]
113    fn test_sql_file_new_from_str_has_no_path_and_preserves_content() {
114        let sql = "SELECT * FROM users;";
115        let file = SqlSource::from_str(sql.to_owned(), None);
116        assert!(file.path().is_none());
117        assert!(file.path_into_path_buf().is_none());
118        assert_eq!(file.content(), sql);
119    }
120}