Files
uv/crates/uv-python/src/version_files.rs
T

93 lines
3.4 KiB
Rust
Raw Normal View History

use fs_err as fs;
use tracing::debug;
2024-07-03 08:44:29 -04:00
use crate::PythonRequest;
/// The file name for Python version pins.
pub static PYTHON_VERSION_FILENAME: &str = ".python-version";
/// The file name for multiple Python version declarations.
pub static PYTHON_VERSIONS_FILENAME: &str = ".python-versions";
2024-07-03 08:44:29 -04:00
/// Read [`PythonRequest`]s from a version file, if present.
///
/// Prefers `.python-versions` then `.python-version`.
/// If only one Python version is desired, use [`request_from_version_files`] which prefers the `.python-version` file.
pub async fn requests_from_version_file() -> Result<Option<Vec<PythonRequest>>, std::io::Error> {
if let Some(versions) = read_versions_file().await? {
Ok(Some(
versions
.into_iter()
2024-07-03 08:44:29 -04:00
.map(|version| PythonRequest::parse(&version))
.collect(),
))
} else if let Some(version) = read_version_file().await? {
2024-07-03 08:44:29 -04:00
Ok(Some(vec![PythonRequest::parse(&version)]))
} else {
Ok(None)
}
}
2024-07-03 08:44:29 -04:00
/// Read a [`PythonRequest`] from a version file, if present.
///
/// Prefers `.python-version` then the first entry of `.python-versions`.
/// If multiple Python versions are desired, use [`requests_from_version_files`] instead.
pub async fn request_from_version_file() -> Result<Option<PythonRequest>, std::io::Error> {
if let Some(version) = read_version_file().await? {
2024-07-03 08:44:29 -04:00
Ok(Some(PythonRequest::parse(&version)))
} else if let Some(versions) = read_versions_file().await? {
Ok(versions
.into_iter()
.next()
.inspect(|_| debug!("Using the first version from `{PYTHON_VERSIONS_FILENAME}`"))
2024-07-03 08:44:29 -04:00
.map(|version| PythonRequest::parse(&version)))
} else {
Ok(None)
}
}
2024-07-19 13:19:28 -04:00
/// Write a version to a .`python-version` file.
pub async fn write_version_file(version: &str) -> Result<(), std::io::Error> {
debug!("Writing Python version `{version}` to `{PYTHON_VERSION_FILENAME}`");
fs::tokio::write(PYTHON_VERSION_FILENAME, format!("{version}\n")).await
}
async fn read_versions_file() -> Result<Option<Vec<String>>, std::io::Error> {
match fs::tokio::read_to_string(PYTHON_VERSIONS_FILENAME).await {
Ok(content) => {
debug!("Reading requests from `{PYTHON_VERSIONS_FILENAME}`");
Ok(Some(
content
.lines()
.filter(|line| {
// Skip comments and empty lines.
let trimmed = line.trim();
!(trimmed.is_empty() || trimmed.starts_with('#'))
})
.map(ToString::to_string)
.collect(),
))
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(err) => Err(err),
}
}
async fn read_version_file() -> Result<Option<String>, std::io::Error> {
match fs::tokio::read_to_string(PYTHON_VERSION_FILENAME).await {
Ok(content) => {
debug!("Reading requests from `{PYTHON_VERSION_FILENAME}`");
Ok(content
.lines()
.find(|line| {
// Skip comments and empty lines.
let trimmed = line.trim();
!(trimmed.is_empty() || trimmed.starts_with('#'))
})
.map(ToString::to_string))
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(err) => Err(err),
}
}