#![deny(clippy::all)]
#![deny(missing_docs)]
#![warn(clippy::pedantic)]
cfg_if::cfg_if! {
if #[cfg(unix)] {
mod clircle_unix;
use clircle_unix as imp;
} else if #[cfg(windows)] {
mod clircle_windows;
use clircle_windows as imp;
} else {
compile_error!("Neither cfg(unix) nor cfg(windows) was true, aborting.");
}
}
#[cfg(feature = "serde")]
use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::fs::File;
use std::io;
pub trait Clircle: Eq + TryFrom<Stdio> + TryFrom<File> {
fn into_inner(self) -> Option<File>;
fn surely_conflicts_with(&self, _other: &Self) -> bool {
false
}
#[must_use]
fn stdin() -> Option<Self> {
Self::try_from(Stdio::Stdin).ok()
}
#[must_use]
fn stdout() -> Option<Self> {
Self::try_from(Stdio::Stdout).ok()
}
#[must_use]
fn stderr() -> Option<Self> {
Self::try_from(Stdio::Stderr).ok()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[allow(missing_docs)]
pub enum Stdio {
Stdin,
Stdout,
Stderr,
}
pub fn output_among_inputs<'o, T>(outputs: &'o [T], inputs: &[T]) -> Option<&'o T>
where
T: Clircle,
{
outputs.iter().find(|output| inputs.contains(output))
}
pub fn stdout_among_inputs<T>(inputs: &[T]) -> bool
where
T: Clircle,
{
T::stdout().map_or(false, |stdout| inputs.contains(&stdout))
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Identifier(imp::Identifier);
impl Clircle for Identifier {
#[must_use]
fn into_inner(self) -> Option<File> {
self.0.into_inner()
}
fn surely_conflicts_with(&self, other: &Self) -> bool {
self.0.surely_conflicts_with(&other.0)
}
}
impl TryFrom<Stdio> for Identifier {
type Error = io::Error;
fn try_from(stdio: Stdio) -> Result<Self, Self::Error> {
imp::Identifier::try_from(stdio).map(Self)
}
}
impl TryFrom<File> for Identifier {
type Error = io::Error;
fn try_from(file: File) -> Result<Self, Self::Error> {
imp::Identifier::try_from(file).map(Self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
use std::hash::Hash;
fn contains_duplicates<T>(items: Vec<T>) -> bool
where
T: Eq + Hash,
{
let mut set = HashSet::new();
items.into_iter().any(|item| !set.insert(item))
}
#[test]
fn test_basic_comparisons() -> Result<(), &'static str> {
let dir = tempfile::tempdir().expect("Couldn't create tempdir.");
let dir_path = dir.path().to_path_buf();
let filenames = ["a", "b", "c", "d"];
let paths: Vec<_> = filenames
.iter()
.map(|filename| dir_path.join(filename))
.collect();
let identifiers = paths
.iter()
.map(File::create)
.map(Result::unwrap)
.map(Identifier::try_from)
.map(Result::unwrap)
.collect::<Vec<_>>();
if contains_duplicates(identifiers) {
return Err("Duplicate identifier found for set of unique paths.");
}
Ok(())
}
}