use super::*; /// A trait used to validate atoms in the `uv` module to valid ones in this module. trait Validate: Sized { type Valid: Into; type Error: Into = eyre::Report; /// Consume into the strongly validated type. fn valiate(self) -> Result; /// Weakly validate that this instance itself is correct. fn self_validate(&self) -> Result<(), Self::Error>; } /// Non-validated inputs which get mapped to the validated ones in the parent module. mod uv { use crate::*; use super::Validate; #[derive(Debug, Clone)] pub struct ExecTaskAtom { prog: Option, args: Option>, //TODO: ... takes_tail: Option, } impl Validate for ExecTaskAtom { type Valid = super::ExecTaskAtom; fn valiate(self) -> Result { todo!() } fn self_validate(&self) -> Result<(), Self::Error> { todo!() } } pub enum Either { Exec(ExecTaskAtom), Shell(!), //TODO } //TODO: impl Validate for Either, or something /// Validate an atom from this module into `Inner`. pub(super) fn validate(inv: T) -> eyre::Result where T: Validate { Ok(inv.valiate().map_err(Into::into) .wrap_err(eyre!("Failed to validate atom")) .with_section(|| std::any::type_name::().header("Type name was")) .with_section(|| std::any::type_name::().header("Validating for"))?.into()) } } /// Options for a normal execution atom struct ExecTaskAtom { prog: String, args: Vec, //TODO: ... takes_tail: TailKind, } enum Inner { Exec(ExecTaskAtom), Shell(!), //TODO } impl From for Inner { #[inline] fn from(from: ExecTaskAtom) -> Self { Self::Exec(from) } } #[derive(Debug, Clone, Copy)] pub enum TailKind { /// No tail parsing Contained, /// Parse rest of arguments Rest(TailField), /// Parse concurrently from stdin Stdin(TailField), } impl Default for TailKind { #[inline] fn default() -> Self { Self::Contained } } #[derive(Debug, Clone, Copy)] pub enum TailField { //TODO: All possible tail fields for all exec atom types. Validating they are used for the correct atom type will be done in `uv`'s validate step. } /// The defaults specified for execution types pub struct TaskDefault(Box<(uv::ExecTaskAtom, ! /* TODO: ShellTaskAtom */)>); /// A task atom is task-specific execution directives. /// /// An atom can inherit from a `TaskDefault`. pub struct TaskAtom(Box); //TODO: how tf are we going to signal tail feeding? Do we just tag which field it is and let the program figure out how to handle it? I think that might be best, esp. for stdin for which reading happens concurrently. impl TaskAtom { fn inherit(default: &TaskDefault, from: uv::Either) -> eyre::Result { //TODO: Clone default.0., replacing the *set* (not `None`) fields set in `from` then validate it into `Self` todo!() } /// Does this atom take tail parsing? And if so, which field? pub fn tail(&self) -> &TailKind { match self.0.as_ref() { Inner::Exec(e) => &e.takes_tail, _ => todo!() } } }