use crate::Result;
use std::path::PathBuf;
use std::process::Command;
#[derive(Debug, clap::Args)]
pub struct PythonCheckAst {
#[clap(required = true)]
pub files: Vec<PathBuf>,
}
impl PythonCheckAst {
pub async fn run(&self) -> Result<()> {
let mut found_invalid = false;
for file_path in &self.files {
if !is_valid_python_syntax(file_path)? {
println!("{}", file_path.display());
found_invalid = true;
}
}
if found_invalid {
return Err(eyre::eyre!("Files with invalid Python syntax found"));
}
Ok(())
}
}
fn is_valid_python_syntax(path: &PathBuf) -> Result<bool> {
let output = Command::new("python3")
.arg("-m")
.arg("py_compile")
.arg(path)
.output();
match output {
Ok(result) => Ok(result.status.success()),
Err(_) => {
let output = Command::new("python")
.arg("-m")
.arg("py_compile")
.arg(path)
.output();
match output {
Ok(result) => Ok(result.status.success()),
Err(_) => {
Ok(true)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::NamedTempFile;
#[test]
fn test_valid_python() {
let file = NamedTempFile::new().unwrap();
fs::write(
file.path(),
r#"
def hello():
print("Hello, world!")
"#,
)
.unwrap();
if let Ok(valid) = is_valid_python_syntax(&file.path().to_path_buf()) {
assert!(valid);
}
}
#[test]
fn test_invalid_python() {
let file = NamedTempFile::new().unwrap();
fs::write(
file.path(),
r#"
def hello(:
print("Invalid syntax"
"#,
)
.unwrap();
if let Ok(valid) = is_valid_python_syntax(&file.path().to_path_buf()) {
assert!(!valid);
}
}
#[test]
fn test_empty_file() {
let file = NamedTempFile::new().unwrap();
fs::write(file.path(), "").unwrap();
if let Ok(valid) = is_valid_python_syntax(&file.path().to_path_buf()) {
assert!(valid);
}
}
}