Skip to main content

athena/
utils.rs

1////! Utility functions for path handling and output formatting
2//!
3//! This module provides common utility functions used across the application:
4//! - Path manipulation and canonicalization
5//! - Directory checking
6//! - Pretty printing SQL output using `bat`
7
8use anyhow::{Context, Result};
9use std::{
10    fs::canonicalize,
11    path::{Path, PathBuf},
12};
13
14/// Get current working dir
15pub fn get_current_working_dir(working_dir: Option<PathBuf>) -> Result<PathBuf> {
16    let current_dir = std::env::current_dir()?;
17    let dir = working_dir.unwrap_or(current_dir);
18
19    canonicalize(&dir).with_context(|| format!("could not get full path: {:?}", dir))
20}
21
22/// Get abs path and convert to string
23pub fn get_full_path_str(path: &Path) -> Result<String> {
24    let path = canonicalize(path).with_context(|| format!("couldn't get full path {:?}", &path))?;
25
26    path.to_str()
27        .map(|t| t.trim_end_matches('/').to_string())
28        .with_context(|| "could not convert to string".to_string())
29}
30
31pub fn pretty_print(input: &[u8]) {
32    if let Err(e) = bat::PrettyPrinter::new()
33        .header(true)
34        .grid(true)
35        .line_numbers(true)
36        .language("sql")
37        .input_from_bytes(input)
38        .print()
39    {
40        // Fallback to simple output if pretty printing fails
41        eprintln!("Warning: pretty printing failed: {}", e);
42        if let Ok(s) = std::str::from_utf8(input) {
43            print!("{}", s);
44        }
45    }
46}
47
48/// Check if a path is a directory
49pub fn is_dir(path: &Path) -> bool {
50    path.is_dir()
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use predicates::prelude::*;
57    use std::env::current_dir;
58    use std::fs::create_dir_all;
59    use std::path::Path;
60    use tempfile::tempdir;
61
62    // No default working dir
63    #[test]
64    fn test_get_current_working_dir_without_param() {
65        assert_eq!(
66            get_current_working_dir(None).unwrap(),
67            current_dir().unwrap()
68        );
69    }
70
71    // With default working dir
72    #[test]
73    fn test_get_current_working_dir_with_param() {
74        let dir = tempdir().expect("could not create temp dir");
75        let path_1 = dir.path();
76        let path_2 = dir.path();
77        let actual = get_current_working_dir(Some(path_1.to_path_buf())).unwrap();
78        let predicate_fn = predicate::str::contains(format!("{}", path_2.display()));
79        assert!(predicate_fn.eval(actual.to_str().unwrap()));
80    }
81
82    #[test]
83    fn test_get_full_path_str() {
84        let dir = tempdir().unwrap();
85        let dir_str = dir.path().to_str().unwrap();
86
87        // Create tempdir <temp>/dir for tests
88        let path = dir.path().join("dir");
89        create_dir_all(&path).expect("could not create dir");
90
91        let test_path = dir.path().join("dir");
92        let predicate_fn = predicate::str::contains(format!("{}/dir", dir_str));
93        assert!(predicate_fn.eval(&get_full_path_str(&test_path).unwrap()));
94
95        let test_path = dir.path().join("dir/");
96        let predicate_fn = predicate::str::contains(format!("{}/dir", dir_str));
97        assert!(predicate_fn.eval(&get_full_path_str(&test_path).unwrap()));
98
99        let test_path = dir.path().join("dir///");
100        let predicate_fn = predicate::str::contains(format!("{}/dir", dir_str));
101        assert!(predicate_fn.eval(&get_full_path_str(&test_path).unwrap()));
102
103        // Folder is not found
104        let test_path = dir.path().join("/not/exists/dir///");
105        assert!(get_full_path_str(&test_path).is_err());
106
107        // Could not work with a file
108        let path = Path::new("/tmp/dir/a.sql");
109        assert!(get_full_path_str(path).is_err());
110
111        dir.close().expect("could not close the tempdir");
112    }
113}