more descriptive errors
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing

This commit is contained in:
Jake Hillion 2022-04-08 20:54:31 +01:00
parent bb28c74833
commit 245bbfdf9d
4 changed files with 42 additions and 46 deletions

View File

@ -15,18 +15,12 @@ pub enum Error {
#[error("json: {0}")] #[error("json: {0}")]
Json(#[from] serde_json::Error), Json(#[from] serde_json::Error),
#[error("bad pipe specification: a pipe must have exactly one reader and one writer: {0}")]
BadPipe(String),
#[error("bad specification type: only .json files are supported")] #[error("bad specification type: only .json files are supported")]
BadSpecType, BadSpecType,
#[error("bad pipe trigger: this entrypoint is not triggered by a pipe")] #[error("bad trigger argument: this entrypoint is not triggered by something with arguments")]
BadPipeTrigger, BadTriggerArgument,
#[error("too many pipes: a pipe must have one reader and one writer: {0}")]
TooManyPipes(String),
#[error("read only pipe: a pipe must have one reader and one writer: {0}")]
ReadOnlyPipe(String),
#[error("write only pipe: a pipe must have one reader and one writer: {0}")]
WriteOnlyPipe(String),
} }

View File

@ -79,45 +79,47 @@ pub fn run() -> Result<()> {
.spawn() .spawn()
} }
fn create_pipes(names: Vec<&str>) -> Result<HashMap<String, PipePair>> {
let mut pipes = HashMap::new();
for pipe in names {
info!("creating pipe pair `{}`", pipe);
pipes.insert(pipe.to_string(), PipePair::new(pipe)?);
}
Ok(pipes)
}
pub struct PipePair { pub struct PipePair {
name: String,
read: Option<File>, read: Option<File>,
write: Option<File>, write: Option<File>,
} }
impl PipePair { impl PipePair {
fn take_read(&mut self) -> File { fn new(name: &str) -> Result<PipePair> {
self.read
.take()
.expect("read pipes should only be used once")
}
fn take_write(&mut self) -> File {
self.write
.take()
.expect("write pipes should only be used once")
}
}
fn create_pipes(names: Vec<&str>) -> Result<HashMap<String, PipePair>> {
let mut pipes = HashMap::new();
for pipe in names {
info!("creating pipe pair `{}`", pipe);
let (read, write) = unistd::pipe2(OFlag::O_DIRECT).map_err(|e| Error::Nix { let (read, write) = unistd::pipe2(OFlag::O_DIRECT).map_err(|e| Error::Nix {
msg: "pipe2", msg: "pipe2",
src: e, src: e,
})?; })?;
// safe to create files given the successful return of pipe(2) // safe to create files given the successful return of pipe(2)
pipes.insert( Ok(PipePair {
pipe.to_string(), name: name.to_string(),
PipePair {
read: Some(unsafe { File::from_raw_fd(read) }), read: Some(unsafe { File::from_raw_fd(read) }),
write: Some(unsafe { File::from_raw_fd(write) }), write: Some(unsafe { File::from_raw_fd(write) }),
}, })
);
} }
Ok(pipes) fn take_read(&mut self) -> Result<File> {
self.read
.take()
.ok_or_else(|| Error::BadPipe(self.name.to_string()))
}
fn take_write(&mut self) -> Result<File> {
self.write
.take()
.ok_or_else(|| Error::BadPipe(self.name.to_string()))
}
} }

View File

@ -55,7 +55,7 @@ impl<'a> Spawner<'a> {
} }
Trigger::Pipe(s) => { Trigger::Pipe(s) => {
let pipe = self.pipes.get_mut(s).unwrap().take_read(); let pipe = self.pipes.get_mut(s).unwrap().take_read().unwrap();
let binary = PathBuf::from(self.binary).canonicalize()?; let binary = PathBuf::from(self.binary).canonicalize()?;
let mut builder = VoidBuilder::new(); let mut builder = VoidBuilder::new();
@ -126,11 +126,11 @@ impl<'a> Spawner<'a> {
Arg::Pipe(p) => out.push(match p { Arg::Pipe(p) => out.push(match p {
Pipe::Rx(s) => { Pipe::Rx(s) => {
let pipe = self.pipes.get_mut(s).unwrap().take_read(); let pipe = self.pipes.get_mut(s).unwrap().take_read().unwrap();
CString::new(pipe.as_raw_fd().to_string()).unwrap() CString::new(pipe.as_raw_fd().to_string()).unwrap()
} }
Pipe::Tx(s) => { Pipe::Tx(s) => {
let pipe = self.pipes.get_mut(s).unwrap().take_write(); let pipe = self.pipes.get_mut(s).unwrap().take_write().unwrap();
CString::new(pipe.as_raw_fd().to_string()).unwrap() CString::new(pipe.as_raw_fd().to_string()).unwrap()
} }
}), }),

View File

@ -122,25 +122,25 @@ impl Specification {
for pipe in read { for pipe in read {
if !read_set.insert(pipe) { if !read_set.insert(pipe) {
return Err(Error::TooManyPipes(pipe.to_string())); return Err(Error::BadPipe(pipe.to_string()));
} }
} }
let mut write_set = HashSet::with_capacity(write.len()); let mut write_set = HashSet::with_capacity(write.len());
for pipe in write { for pipe in write {
if !write_set.insert(pipe) { if !write_set.insert(pipe) {
return Err(Error::TooManyPipes(pipe.to_string())); return Err(Error::BadPipe(pipe.to_string()));
} }
} }
for pipe in read_set { for pipe in read_set {
if !write_set.remove(pipe) { if !write_set.remove(pipe) {
return Err(Error::ReadOnlyPipe(pipe.to_string())); return Err(Error::BadPipe(pipe.to_string()));
} }
} }
if let Some(pipe) = write_set.into_iter().next() { if let Some(pipe) = write_set.into_iter().next() {
return Err(Error::WriteOnlyPipe(pipe.to_string())); return Err(Error::BadPipe(pipe.to_string()));
} }
// validate pipe trigger arguments make sense // validate pipe trigger arguments make sense
@ -148,7 +148,7 @@ impl Specification {
if entrypoint.args.contains(&Arg::PipeTrigger) { if entrypoint.args.contains(&Arg::PipeTrigger) {
match entrypoint.trigger { match entrypoint.trigger {
Trigger::Pipe(_) => {} Trigger::Pipe(_) => {}
_ => return Err(Error::BadPipeTrigger), _ => return Err(Error::BadTriggerArgument),
} }
} }
} }