Skip to content

Commit

Permalink
Add is_writeable to the pipeline trait
Browse files Browse the repository at this point in the history
It has an implementation, and will be used to determine whether `sudo`
is likely to be needed to install an extension.

It needs to stringify file names, so move the `filename!` macro to the
root crate and make it public inside the create. Add tests for it,
though it requires annotation for the argument type. Sadly, test
coverage cannot tell it has been tested, and [runtime-macros] looks a
bit heavyweight for my taste.

  [runtime-macros]: https://crates.io/crates/runtime-macros
  • Loading branch information
theory committed Dec 12, 2024
1 parent 0284ae0 commit dce67c8
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ regex = "1.11.1"
semver = "1.0.23"
serde = "1.0.215"
serde_json = "1.0.133"
tempfile = "3.14.0"
thiserror = "2.0.4"
ureq = { version = "2.12.1", features = ["json"] }
url = "2.5.4"
Expand All @@ -33,6 +34,5 @@ zip = "2.2.1"
[dev-dependencies]
httpmock = "0.7.0"
sha2 = "0.10.8"
tempfile = "3.14.0"
temp-env = "0.3.6"
assertables = "9.4.0"
12 changes: 1 addition & 11 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,6 @@ use std::{
};
use url::Url;

/// Returns a string representation of the final segment of a Path.
macro_rules! filename {
( $x:expr ) => {{
$x.as_ref()
.file_name()
.unwrap_or(std::ffi::OsStr::new("UNKNOWN"))
.to_string_lossy()
}};
}

macro_rules! copy_err {
($x:expr, $y:expr, $z:expr) => {{
Err(BuildError::File(
Expand Down Expand Up @@ -128,7 +118,7 @@ impl Api {
/// Unpack download `file` in directory `into` and return the path to the
/// unpacked directory.
pub fn unpack<P: AsRef<Path>>(&self, into: P, file: P) -> Result<PathBuf, BuildError> {
info!(file:display = filename!(file); "unpacking");
info!(file:display = crate::filename!(&file); "unpacking");
let zip = File::open(file)?;
let mut archive = zip::ZipArchive::new(zip)?;
archive.extract(&into)?;
Expand Down
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,16 @@ impl<P: AsRef<Path>> Builder<P> {
}
}

/// Returns a string representation of the final segment of a Path.
macro_rules! filename {
( $x:expr ) => {{
AsRef::<Path>::as_ref($x)
.file_name()
.unwrap_or(std::ffi::OsStr::new("UNKNOWN"))
.to_string_lossy()
}};
}
pub(crate) use filename;

#[cfg(test)]
mod tests;
17 changes: 16 additions & 1 deletion src/pipeline/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Build Pipeline interface definition.
use crate::error::BuildError;
use std::{path::Path, process::Command};
use log::debug;
use std::{io::Write, path::Path, process::Command};

/// Defines the interface for build pipelines to configure, compile, and test
/// PGXN distributions.
Expand Down Expand Up @@ -30,6 +31,20 @@ pub(crate) trait Pipeline<P: AsRef<Path>> {
/// Returns the directory passed to [`new`].
fn dir(&self) -> &P;

/// Attempts to write a temporary file to `dir` and returns `true` on
/// success and `false` on failure. The temporary file will be deleted.
fn is_writeable<D: AsRef<Path>>(&self, dir: D) -> bool {
debug!(dir:display = crate::filename!(&dir); "testing write access");
match tempfile::Builder::new()
.prefix("pgxn-")
.suffix(".test")
.tempfile_in(dir)
{
Ok(f) => write!(&f, "ok").is_ok(),
Err(_) => false,
}
}

/// Run a command. Runs it with elevated privileges using `sudo` unless
/// it's on Windows.
fn run<S, I>(&self, cmd: &str, args: I, sudo: bool) -> Result<(), BuildError>
Expand Down
11 changes: 11 additions & 0 deletions src/pipeline/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,14 @@ fn run() -> Result<(), BuildError> {

Ok(())
}

#[test]
fn is_writeable() -> Result<(), BuildError> {
let tmp = tempdir()?;

let pipe = TestPipeline::new(&tmp, false);
assert!(pipe.is_writeable(&tmp));
assert!(!pipe.is_writeable(tmp.path().join(" nonesuch")));

Ok(())
}
10 changes: 9 additions & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::*;
use serde_json::{json, Value};
use std::{fs::File, io::Write, process::Command};
use std::{fs::File, io::Write, path::PathBuf, process::Command};
use tempfile::tempdir;

fn release_meta(pipeline: &str) -> Value {
Expand Down Expand Up @@ -199,3 +199,11 @@ pub fn compile_mock(name: &str, dest: &str) {
)
}
}

#[test]
fn filename() {
assert_eq!("string", filename!("string"));
assert_eq!("path", filename!(Path::new("path")));
let pb = PathBuf::from(r"C:\windows\system32.dll");
assert_eq!(r"C:\windows\system32.dll", filename!(&pb));
}

0 comments on commit dce67c8

Please sign in to comment.