use anyhow::{bail, Result};
use crate::project_info::{ProjectInfo, ProjectManager};
const FASTAPI_BASE_DEPENDENCIES: &[&str] = &[
"asyncpg",
"camel-converter[pydantic]",
"fastapi",
"granian[pname,reload]",
"httptools",
"loguru",
"orjson",
"pwdlib[argon2]",
"pydantic[email]",
"pydantic-settings",
"pyjwt",
"python-multipart",
"uvloop; sys_platform != 'win32'",
"valkey",
];
const FASTAPI_BASE_DEV_DEPENDENCIES: &[&str] = &["httpx", "pytest-xdist"];
pub fn install_fastapi_dependencies(project_info: &ProjectInfo) -> Result<()> {
match project_info.project_manager {
ProjectManager::Uv => uv_fastapi_dependency_installer(project_info)?,
ProjectManager::Poetry => poetry_fastapi_dependency_installer(project_info)?,
ProjectManager::Setuptools => setuptools_fastapi_dependency_installer(project_info)?,
ProjectManager::Maturin => maturin_fastapi_dependency_installer(project_info)?,
};
Ok(())
}
fn uv_fastapi_dependency_installer(project_info: &ProjectInfo) -> Result<()> {
let dependencies = FASTAPI_BASE_DEPENDENCIES.to_vec();
let mut args = vec!["add"];
args.extend(dependencies);
let output = std::process::Command::new("uv")
.args(args)
.current_dir(project_info.base_dir())
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("Failed to install FastAPI dependencies: {stderr}");
}
let dev_dependencies = FASTAPI_BASE_DEV_DEPENDENCIES.to_vec();
let mut dev_args = vec!["add", "--group=dev"];
dev_args.extend(dev_dependencies);
let output = std::process::Command::new("uv")
.args(dev_args)
.current_dir(project_info.base_dir())
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("Failed to install FastAPI dependencies: {stderr}");
}
Ok(())
}
fn poetry_fastapi_dependency_installer(project_info: &ProjectInfo) -> Result<()> {
let dependencies = FASTAPI_BASE_DEPENDENCIES.to_vec();
let mut args = vec!["add"];
args.extend(dependencies);
let output = std::process::Command::new("poetry")
.args(args)
.current_dir(project_info.base_dir())
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("Failed to install FastAPI dependencies: {stderr}");
}
let dev_dependencies = FASTAPI_BASE_DEV_DEPENDENCIES.to_vec();
let mut dev_args = vec!["add", "--group=dev"];
dev_args.extend(dev_dependencies);
let output = std::process::Command::new("poetry")
.args(dev_args)
.current_dir(project_info.base_dir())
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("Failed to install FastAPI dependencies: {stderr}");
}
Ok(())
}
fn setuptools_fastapi_dependency_installer(project_info: &ProjectInfo) -> Result<()> {
let venv_path = project_info.base_dir().join(".venv");
if !venv_path.exists() {
let venv_output = std::process::Command::new("python")
.args(["-m", "venv", ".venv"])
.current_dir(project_info.base_dir())
.output()?;
if !venv_output.status.success() {
let stderr = String::from_utf8_lossy(&venv_output.stderr);
bail!("Failed to create virtual environment: {stderr}");
}
}
let dependencies = FASTAPI_BASE_DEPENDENCIES.to_vec();
let dev_dependencies = FASTAPI_BASE_DEV_DEPENDENCIES.to_vec();
let mut args = vec!["-m", "pip", "install"];
args.extend(dependencies);
args.extend(dev_dependencies);
let output = std::process::Command::new(".venv/bin/python")
.args(args)
.current_dir(project_info.base_dir())
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
bail!("Failed to install FastAPI dependencies: {stderr}");
}
let freeze = std::process::Command::new(".venv/bin/python")
.args(["-m", "pip", "freeze"])
.current_dir(project_info.base_dir())
.output()?;
if !freeze.status.success() {
let stderr = String::from_utf8_lossy(&freeze.stderr);
bail!("Failed to get pip freeze output: {stderr}");
}
let requirements_path = project_info.base_dir().join("requirements.txt");
std::fs::write(requirements_path, freeze.stdout)?;
Ok(())
}
fn maturin_fastapi_dependency_installer(project_info: &ProjectInfo) -> Result<()> {
use crate::project_info::Pyo3PythonManager;
if let Some(pyo3_python_manager) = &project_info.pyo3_python_manager {
match pyo3_python_manager {
Pyo3PythonManager::Uv => uv_fastapi_dependency_installer(project_info),
Pyo3PythonManager::Setuptools => setuptools_fastapi_dependency_installer(project_info),
}
} else {
bail!("No Python project manager provided for PyO3 project");
}
}