Skip to content

Commit

Permalink
Rename to receipt.toml
Browse files Browse the repository at this point in the history
  • Loading branch information
zanieb committed Jun 26, 2024
1 parent d80a72d commit 20034ae
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 115 deletions.
52 changes: 27 additions & 25 deletions crates/uv-tool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@ use tracing::debug;
use uv_fs::{LockedFile, Simplified};
use uv_toolchain::{Interpreter, PythonEnvironment};

pub use tools_toml::{Tool, ToolToml};
pub use receipt::ToolReceipt;
pub use tool::Tool;

use uv_state::{StateBucket, StateStore};
mod tools_toml;
mod receipt;
mod tool;

#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
IO(#[from] io::Error),
#[error("Failed to update `tool.toml` at {0}")]
TomlWrite(PathBuf, #[source] Box<toml::ser::Error>),
#[error("Failed to read `tool.toml` at {0}")]
TomlRead(PathBuf, #[source] Box<toml::de::Error>),
#[error("Failed to update `uv-receipt.toml` at {0}")]
ReceiptWrite(PathBuf, #[source] Box<toml::ser::Error>),
#[error("Failed to read `uv-receipt.toml` at {0}")]
ReceiptRead(PathBuf, #[source] Box<toml::de::Error>),
#[error(transparent)]
VirtualEnvError(#[from] uv_virtualenv::Error),
#[error("Failed to read package entry points {0}")]
Expand All @@ -32,8 +34,8 @@ pub enum Error {
DistInfoMissing(String, PathBuf),
#[error("Failed to find a directory for executables")]
NoExecutableDirectory,
#[error("Failed to find a metadata for tool `{0}` at {1}")]
MissingToolMetadata(String, PathBuf),
#[error("Failed to find a receipt for tool `{0}` at {1}")]
MissingToolReceipt(String, PathBuf),
}

/// A collection of uv-managed tools installed on the current system.
Expand Down Expand Up @@ -72,27 +74,27 @@ impl InstalledTools {
let mut tools = Vec::new();
for directory in uv_fs::directories(self.root()) {
let name = directory.file_name().unwrap().to_string_lossy().to_string();
let path = directory.join("tool.toml");
let path = directory.join("uv-receipt.toml");
let contents = match fs_err::read_to_string(&path) {
Ok(contents) => contents,
// TODO(zanieb): Consider warning on malformed tools instead
Err(err) if err.kind() == io::ErrorKind::NotFound => {
return Err(Error::MissingToolMetadata(name.clone(), path.clone()))
return Err(Error::MissingToolReceipt(name.clone(), path.clone()))
}
Err(err) => return Err(err.into()),
};
let tool_toml = ToolToml::from_string(contents)
.map_err(|err| Error::TomlRead(path, Box::new(err)))?;
tools.push((name, tool_toml.tool));
let tool_receipt = ToolReceipt::from_string(contents)
.map_err(|err| Error::ReceiptRead(path, Box::new(err)))?;
tools.push((name, tool_receipt.tool));
}
Ok(tools)
}

/// Get the tool metadata for the given tool
pub fn tool_metadata(&self, name: &str) -> Result<Option<Tool>, Error> {
let path = self.root.join(name).join("tool.toml");
match ToolToml::from_path(&path) {
Ok(tool_toml) => Ok(Some(tool_toml.tool)),
/// Get the receipt for the given tool.
pub fn get_tool_receipt(&self, name: &str) -> Result<Option<Tool>, Error> {
let path = self.root.join(name).join("uv-receipt.toml");
match ToolReceipt::from_path(&path) {
Ok(tool_receipt) => Ok(Some(tool_receipt.tool)),
Err(Error::IO(err)) if err.kind() == io::ErrorKind::NotFound => Ok(None),
Err(err) => Err(err),
}
Expand All @@ -115,22 +117,22 @@ impl InstalledTools {
)?)
}

/// Add metadata for a tool.
/// Add a receipt for a tool.
///
/// Existing metadata will be replaced.
pub fn add_tool_metadata(&self, name: &str, tool: Tool) -> Result<(), Error> {
/// Any existing receipt will be replaced.
pub fn add_tool_receipt(&self, name: &str, tool: Tool) -> Result<(), Error> {
let _lock = self.acquire_tool_lock(name);

let tool_toml = ToolToml::from(tool);
let path = self.root.join(name).join("tool.toml");
let tool_receipt = ToolReceipt::from(tool);
let path = self.root.join(name).join("uv-receipt.toml");

debug!(
"Adding metadata entry for tool `{name}` at {}",
path.user_display()
);

let doc = toml::to_string(&tool_toml)
.map_err(|err| Error::TomlWrite(path.clone(), Box::new(err)))?;
let doc = toml::to_string(&tool_receipt)
.map_err(|err| Error::ReceiptWrite(path.clone(), Box::new(err)))?;

// Save the modified `tools.toml`.
fs_err::write(&path, doc)?;
Expand Down
51 changes: 51 additions & 0 deletions crates/uv-tool/src/receipt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::path::Path;

use serde::{Deserialize, Serialize};

use crate::Tool;

/// A `uv-receipt.toml` file tracking the installation of a tool.
#[allow(dead_code)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolReceipt {
pub(crate) tool: Tool,

/// The raw unserialized document.
#[serde(skip)]
pub(crate) raw: String,
}

impl ToolReceipt {
/// Parse a [`ToolReceipt`] from a raw TOML string.
pub(crate) fn from_string(raw: String) -> Result<Self, toml::de::Error> {
let tool = toml::from_str(&raw)?;
Ok(ToolReceipt { raw, ..tool })
}

/// Read a [`ToolReceipt`] from the given path.
pub(crate) fn from_path(path: &Path) -> Result<ToolReceipt, crate::Error> {
match fs_err::read_to_string(path) {
Ok(contents) => Ok(ToolReceipt::from_string(contents)
.map_err(|err| crate::Error::ReceiptRead(path.to_owned(), Box::new(err)))?),
Err(err) => Err(err.into()),
}
}
}

// Ignore raw document in comparison.
impl PartialEq for ToolReceipt {
fn eq(&self, other: &Self) -> bool {
self.tool.eq(&other.tool)
}
}

impl Eq for ToolReceipt {}

impl From<Tool> for ToolReceipt {
fn from(tool: Tool) -> Self {
ToolReceipt {
tool,
raw: String::new(),
}
}
}
25 changes: 25 additions & 0 deletions crates/uv-tool/src/tool.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use pypi_types::VerbatimParsedUrl;
use serde::{Deserialize, Serialize};

/// A tool entry.
#[allow(dead_code)]
#[derive(Debug, Clone, Serialize, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Tool {
requirements: Vec<pep508_rs::Requirement<VerbatimParsedUrl>>,
python: Option<String>,
}

impl Tool {
/// Create a new `Tool`.
pub fn new(
requirements: Vec<pep508_rs::Requirement<VerbatimParsedUrl>>,
python: Option<String>,
) -> Self {
Self {
requirements,
python,
}
}
}
73 changes: 0 additions & 73 deletions crates/uv-tool/src/tools_toml.rs

This file was deleted.

4 changes: 2 additions & 2 deletions crates/uv/src/commands/tool/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub(crate) async fn install(
let installed_tools = InstalledTools::from_settings()?;

// TODO(zanieb): Automatically replace an existing tool if the request differs
if installed_tools.tool_metadata(&name)?.is_some() {
if installed_tools.get_tool_receipt(&name)?.is_some() {
if force {
debug!("Replacing existing tool due to `--force` flag.");
} else {
Expand Down Expand Up @@ -180,7 +180,7 @@ pub(crate) async fn install(
}

let installed_tools = installed_tools.init()?;
installed_tools.add_tool_metadata(&name, tool)?;
installed_tools.add_tool_receipt(&name, tool)?;

Ok(ExitStatus::Success)
}
30 changes: 15 additions & 15 deletions crates/uv/tests/tool_install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn tool_install() {
tool_dir.child("black").assert(predicate::path::is_dir());
tool_dir
.child("black")
.child("tool.toml")
.child("uv-receipt.toml")
.assert(predicate::path::exists());

let executable = bin_dir.child(format!("black{}", std::env::consts::EXE_SUFFIX));
Expand Down Expand Up @@ -72,8 +72,8 @@ fn tool_install() {
insta::with_settings!({
filters => context.filters(),
}, {
// We should have a tool entry
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("tool.toml")).unwrap(), @r###"
// We should have a tool receipt
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("uv-receipt.toml")).unwrap(), @r###"
[tool]
requirements = ["black"]
"###);
Expand Down Expand Up @@ -148,8 +148,8 @@ fn tool_install() {
insta::with_settings!({
filters => context.filters(),
}, {
// We should have a new tool entry
assert_snapshot!(fs_err::read_to_string(tool_dir.join("flask").join("tool.toml")).unwrap(), @r###"
// We should have a new tool receipt
assert_snapshot!(fs_err::read_to_string(tool_dir.join("flask").join("uv-receipt.toml")).unwrap(), @r###"
[tool]
requirements = ["flask"]
"###);
Expand Down Expand Up @@ -188,7 +188,7 @@ fn tool_install_twice() {
tool_dir.child("black").assert(predicate::path::is_dir());
tool_dir
.child("black")
.child("tool.toml")
.child("uv-receipt.toml")
.assert(predicate::path::exists());

let executable = bin_dir.child(format!("black{}", std::env::consts::EXE_SUFFIX));
Expand Down Expand Up @@ -216,8 +216,8 @@ fn tool_install_twice() {
insta::with_settings!({
filters => context.filters(),
}, {
// We should have a tool entry
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("tool.toml")).unwrap(), @r###"
// We should have a tool receipt
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("uv-receipt.toml")).unwrap(), @r###"
[tool]
requirements = ["black"]
"###);
Expand Down Expand Up @@ -245,8 +245,8 @@ fn tool_install_twice() {
insta::with_settings!({
filters => context.filters(),
}, {
// We should not have an additional tool entry
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("tool.toml")).unwrap(), @r###"
// We should not have an additional tool receipt
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("uv-receipt.toml")).unwrap(), @r###"
[tool]
requirements = ["black"]
"###);
Expand Down Expand Up @@ -297,7 +297,7 @@ fn tool_install_entry_point_exists() {
assert!(!tool_dir.child("black").exists());

// We should not write a tools entry
assert!(!tool_dir.join("black").join("tool.toml").exists());
assert!(!tool_dir.join("black").join("uv-receipt.toml").exists());

insta::with_settings!({
filters => context.filters(),
Expand Down Expand Up @@ -360,8 +360,8 @@ fn tool_install_entry_point_exists() {
insta::with_settings!({
filters => context.filters(),
}, {
// We write a tool entry
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("tool.toml")).unwrap(), @r###"
// We write a tool receipt
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("uv-receipt.toml")).unwrap(), @r###"
[tool]
requirements = ["black"]
"###);
Expand Down Expand Up @@ -392,8 +392,8 @@ fn tool_install_entry_point_exists() {
insta::with_settings!({
filters => context.filters(),
}, {
// We should have a tool entry
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("tool.toml")).unwrap(), @r###"
// We should have a tool receipt
assert_snapshot!(fs_err::read_to_string(tool_dir.join("black").join("uv-receipt.toml")).unwrap(), @r###"
[tool]
requirements = ["black"]
"###);
Expand Down

0 comments on commit 20034ae

Please sign in to comment.