use camino::Utf8PathBuf;
use std::{io::Read, str::FromStr};
mod command;
mod environment;
mod file;
mod inline;
mod shell;
pub(crate) use command::CommandInputSource;
pub(crate) use environment::EnvironmentInputSource;
pub(crate) use file::FileInputSource;
pub(crate) use inline::InlineInputSource;
pub(crate) use shell::ShellInputSource;
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
#[error(transparent)]
#[remain::sorted]
pub(crate) enum Error {
Command(#[from] command::Error),
Environment(#[from] environment::Error),
File(#[from] file::Error),
Inline(#[from] inline::Error),
Json(#[from] serde_json::Error),
Shell(#[from] shell::Error),
Stdin(#[from] std::io::Error),
Yaml(#[from] serde_yml::Error),
}
#[derive(Debug, Clone)]
#[remain::sorted]
pub(crate) enum InputSource {
Command(CommandInputSource),
Environment(EnvironmentInputSource),
File(FileInputSource),
Inline(InlineInputSource),
Shell(ShellInputSource),
Stdin,
}
impl InputSource {
pub(crate) fn cache(self) -> Result<Self, Error> {
let value = self.try_into()?;
Ok(InlineInputSource { value }.into())
}
pub(crate) fn is_stdin(&self) -> bool {
matches!(self, Self::Stdin)
}
}
impl FromStr for InputSource {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s == "-" {
return Ok(Self::Stdin);
}
let source = match s.strip_prefix('$') {
Some(s) => EnvironmentInputSource::from_str(s)?.into(),
None => match s.strip_prefix('@') {
Some(s) => FileInputSource::from_str(s)?.into(),
None => match s.strip_prefix('|') {
Some(s) => CommandInputSource::from_str(s)?.into(),
None => match s.strip_prefix('!') {
Some(s) => ShellInputSource::from_str(s)?.into(),
None => InlineInputSource::from_str(s)?.into(),
},
},
},
};
Ok(source)
}
}
impl TryFrom<InputSource> for String {
type Error = Error;
fn try_from(source: InputSource) -> Result<Self, Self::Error> {
let mut value = match source {
InputSource::Command(input) => input.try_into()?,
InputSource::Environment(input) => input.try_into()?,
InputSource::File(input) => input.try_into()?,
InputSource::Inline(input) => input.try_into()?,
InputSource::Shell(input) => input.try_into()?,
InputSource::Stdin => {
let mut buf = String::new();
std::io::stdin().read_to_string(&mut buf)?;
buf
}
};
Ok(value)
}
}
impl Default for InputSource {
fn default() -> Self {
InlineInputSource::default().into()
}
}
impl From<CommandInputSource> for InputSource {
fn from(input: CommandInputSource) -> Self {
Self::Command(input)
}
}
impl From<EnvironmentInputSource> for InputSource {
fn from(input: EnvironmentInputSource) -> Self {
Self::Environment(input)
}
}
impl From<FileInputSource> for InputSource {
fn from(input: FileInputSource) -> Self {
Self::File(input)
}
}
impl From<InlineInputSource> for InputSource {
fn from(input: InlineInputSource) -> Self {
Self::Inline(input)
}
}
impl From<ShellInputSource> for InputSource {
fn from(input: ShellInputSource) -> Self {
Self::Shell(input)
}
}