use async_trait::async_trait;
use clap::{ArgMatches, Command, Id};
pub use crud_api_derive::*;
use crud_pretty_struct::PrettyPrint;
#[cfg(any(feature = "json", feature = "toml", feature = "yaml", feature = "csv"))]
use crud_tidy_viewer::{display_table, TableConfig};
use formats::OutputFormat;
#[doc(hidden)]
pub use formats::{
clap_match_input_from_file, clap_match_output_format, clap_match_template, clap_output_format_decl,
};
use miette::{IntoDiagnostic, Result};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{fmt::Debug, marker::PhantomData};
extern crate crud_api_derive;
#[doc(hidden)]
pub mod cli;
#[doc(hidden)]
pub mod completions;
#[doc(hidden)]
pub mod error;
mod formats;
#[doc(hidden)]
pub mod http;
#[doc(hidden)]
pub mod settings;
#[doc(hidden)]
pub struct ApiInputOptions {
pub conflicts_with_all: Vec<Id>,
}
#[doc(hidden)]
pub trait ApiInput {
fn clap(app: Command, options: Option<ApiInputOptions>) -> Command;
fn from_clap_matches(matches: &ArgMatches) -> Result<Self>
where
Self: Sized;
}
#[doc(hidden)]
#[async_trait]
pub trait Query {
async fn query<P, T, R, Q>(
&self,
payload: Option<P>,
argument: Option<Q>,
t: Option<PhantomData<T>>,
) -> Result<R>
where
P: Send + Serialize + Debug,
T: TryInto<R, Error = String> + DeserializeOwned + Send,
R: Send + DeserializeOwned + Debug + Default,
Q: Send + Serialize + Debug;
async fn stream<P, Q>(
&self,
payload: Option<P>,
argument: Option<Q>,
filename: Option<String>,
) -> Result<()>
where
P: Send + Serialize + Debug,
Q: Send + Serialize + Debug;
}
#[doc(hidden)]
pub trait Api {
fn to_table_header(&self) -> Vec<String>;
fn to_table(&self) -> Result<Vec<String>>;
fn to_output(&self) -> Result<String>;
#[cfg(any(feature = "json", feature = "toml", feature = "yaml", feature = "csv"))]
fn output(&self, format: Option<OutputFormat>) -> Result<()>
where
Self: Serialize + Debug,
{
let out = match format {
Some(format) => match format {
#[cfg(feature = "json")]
OutputFormat::Json => Some(serde_json::to_string_pretty(self).into_diagnostic()?),
#[cfg(feature = "toml")]
OutputFormat::Toml => Some(toml::to_string(self).into_diagnostic()?),
#[cfg(feature = "yaml")]
OutputFormat::Yaml => Some(serde_yaml::to_string(self).into_diagnostic()?),
#[cfg(feature = "csv")]
OutputFormat::Csv => {
let mut wtr = csv::Writer::from_writer(std::io::stdout());
wtr.serialize(self).into_diagnostic()?;
wtr.flush().into_diagnostic()?;
None
}
#[cfg(feature = "csv")]
OutputFormat::Tsv => {
let mut wtr = csv::WriterBuilder::new()
.delimiter(b'\t')
.quote_style(csv::QuoteStyle::NonNumeric)
.from_writer(std::io::stdout());
wtr.serialize(self).into_diagnostic()?;
wtr.flush().into_diagnostic()?;
None
}
},
None => Some(self.to_output()?),
};
if let Some(out) = out {
print!("{out}");
}
Ok(())
}
#[cfg(all(
not(feature = "json"),
not(feature = "toml"),
not(feature = "yaml"),
not(feature = "csv")
))]
fn output(&self, _format: Option<OutputFormat>) -> Result<()>
where
Self: Serialize + Debug,
{
Ok(())
}
#[cfg(any(feature = "json", feature = "toml", feature = "yaml", feature = "csv"))]
fn output_multiple(results: &Vec<Self>, format: Option<OutputFormat>) -> Result<()>
where
Self: Sized + Serialize + Debug,
{
let out = match format {
Some(format) => match format {
#[cfg(feature = "json")]
OutputFormat::Json => Some(serde_json::to_string_pretty(results).into_diagnostic()?),
#[cfg(feature = "toml")]
OutputFormat::Toml => Some(toml::to_string(results).into_diagnostic()?),
#[cfg(feature = "yaml")]
OutputFormat::Yaml => Some(serde_yaml::to_string(results).into_diagnostic()?),
#[cfg(feature = "csv")]
OutputFormat::Csv => {
let mut wtr = csv::Writer::from_writer(std::io::stdout());
for result in results {
wtr.serialize(result).into_diagnostic()?;
}
wtr.flush().into_diagnostic()?;
None
}
#[cfg(feature = "csv")]
OutputFormat::Tsv => {
let mut wtr = csv::WriterBuilder::new()
.delimiter(b'\t')
.quote_style(csv::QuoteStyle::NonNumeric)
.from_writer(std::io::stdout());
for result in results {
wtr.serialize(result).into_diagnostic()?;
}
wtr.flush().into_diagnostic()?;
None
}
},
None => {
if !results.is_empty() {
let mut table = vec![results.iter().next().unwrap().to_table_header()];
table.append(
&mut results
.iter()
.map(|row| row.to_table().expect("Formating data table"))
.collect(),
); display_table(&table, TableConfig::default());
}
None
}
};
if let Some(out) = out {
println!("{out}");
}
Ok(())
}
#[cfg(all(
not(feature = "json"),
not(feature = "toml"),
not(feature = "yaml"),
not(feature = "csv")
))]
fn output_multiple(_results: &Vec<Self>, _format: Option<OutputFormat>) -> Result<()>
where
Self: Sized + Serialize + Debug,
{
Ok(())
}
}
#[derive(Debug, Default, Deserialize, Serialize, PrettyPrint)]
pub struct EmptyResponse {}
impl Api for EmptyResponse {
fn to_table_header(&self) -> Vec<String> {
vec![]
}
fn to_table(&self) -> Result<Vec<String>> {
Ok(vec![])
}
fn to_output(&self) -> Result<String> {
Ok(String::new())
}
}
#[derive(Deserialize)]
pub struct DummyTryFrom;
impl TryFrom<DummyTryFrom> for EmptyResponse {
type Error = String;
fn try_from(_value: DummyTryFrom) -> std::result::Result<Self, Self::Error> {
Err(String::new())
}
}
impl<T> TryFrom<DummyTryFrom> for Vec<T> {
type Error = String;
fn try_from(_value: DummyTryFrom) -> std::result::Result<Self, Self::Error> {
Err(String::new())
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}