2022-03-28 04:27:07 +00:00
|
|
|
use crate::magick::ValidInputType;
|
|
|
|
use crate::serde_str::Serde;
|
2022-03-28 00:10:06 +00:00
|
|
|
use clap::ArgEnum;
|
2022-03-28 04:27:07 +00:00
|
|
|
use std::{fmt::Display, path::PathBuf, str::FromStr};
|
|
|
|
use tracing::Level;
|
2022-03-28 00:10:06 +00:00
|
|
|
|
|
|
|
#[derive(
|
|
|
|
Clone,
|
2022-03-28 04:27:07 +00:00
|
|
|
Copy,
|
2022-03-28 00:10:06 +00:00
|
|
|
Debug,
|
|
|
|
PartialEq,
|
|
|
|
Eq,
|
|
|
|
PartialOrd,
|
|
|
|
Ord,
|
|
|
|
Hash,
|
|
|
|
serde::Deserialize,
|
|
|
|
serde::Serialize,
|
|
|
|
ArgEnum,
|
|
|
|
)]
|
2022-03-28 04:27:07 +00:00
|
|
|
#[serde(rename_all = "snake_case")]
|
2022-03-28 00:10:06 +00:00
|
|
|
pub(crate) enum LogFormat {
|
|
|
|
Compact,
|
|
|
|
Json,
|
|
|
|
Normal,
|
|
|
|
Pretty,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(
|
|
|
|
Clone,
|
2022-03-28 04:27:07 +00:00
|
|
|
Copy,
|
2022-03-28 00:10:06 +00:00
|
|
|
Debug,
|
|
|
|
PartialEq,
|
|
|
|
Eq,
|
|
|
|
PartialOrd,
|
|
|
|
Ord,
|
|
|
|
Hash,
|
|
|
|
serde::Deserialize,
|
|
|
|
serde::Serialize,
|
|
|
|
ArgEnum,
|
|
|
|
)]
|
2022-03-28 04:27:07 +00:00
|
|
|
#[serde(rename_all = "snake_case")]
|
2022-03-28 00:10:06 +00:00
|
|
|
pub(crate) enum ImageFormat {
|
|
|
|
Jpeg,
|
|
|
|
Webp,
|
|
|
|
Png,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub(crate) struct Targets {
|
|
|
|
pub(crate) targets: tracing_subscriber::filter::Targets,
|
|
|
|
}
|
|
|
|
|
2022-03-28 04:27:07 +00:00
|
|
|
/// Configuration for filesystem media storage
|
|
|
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, clap::Parser)]
|
|
|
|
#[serde(rename_all = "snake_case")]
|
|
|
|
pub(crate) struct Filesystem {
|
|
|
|
/// Path to store media
|
|
|
|
#[clap(short, long)]
|
|
|
|
pub(crate) path: PathBuf,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Configuration for object media storage
|
|
|
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, clap::Parser)]
|
|
|
|
#[serde(rename_all = "snake_case")]
|
|
|
|
pub(crate) struct ObjectStorage {
|
|
|
|
/// The bucket in which to store media
|
|
|
|
#[clap(short, long)]
|
|
|
|
pub(crate) bucket_name: String,
|
|
|
|
|
|
|
|
/// The region the bucket is located in
|
|
|
|
#[clap(short, long)]
|
|
|
|
pub(crate) region: Serde<s3::Region>,
|
|
|
|
|
|
|
|
/// The Access Key for the user accessing the bucket
|
|
|
|
#[clap(short, long)]
|
|
|
|
pub(crate) access_key: String,
|
|
|
|
|
|
|
|
/// The secret key for the user accessing the bucket
|
|
|
|
#[clap(short, long)]
|
|
|
|
pub(crate) secret_key: String,
|
|
|
|
|
|
|
|
/// The session token for accessing the bucket
|
|
|
|
#[clap(long)]
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
pub(crate) session_token: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
|
|
|
#[serde(rename_all = "snake_case")]
|
|
|
|
#[serde(tag = "type")]
|
|
|
|
pub(crate) enum Store {
|
|
|
|
Filesystem(Filesystem),
|
|
|
|
|
|
|
|
ObjectStorage(ObjectStorage),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ImageFormat {
|
|
|
|
pub(crate) fn as_hint(self) -> Option<ValidInputType> {
|
|
|
|
Some(ValidInputType::from_format(self))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn as_magick_format(self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
Self::Jpeg => "JPEG",
|
|
|
|
Self::Png => "PNG",
|
|
|
|
Self::Webp => "WEBP",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Filesystem> for Store {
|
|
|
|
fn from(f: Filesystem) -> Self {
|
|
|
|
Self::Filesystem(f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<ObjectStorage> for Store {
|
|
|
|
fn from(o: ObjectStorage) -> Self {
|
|
|
|
Self::ObjectStorage(o)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-28 00:10:06 +00:00
|
|
|
impl FromStr for Targets {
|
|
|
|
type Err = <tracing_subscriber::filter::Targets as FromStr>::Err;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
Ok(Targets {
|
|
|
|
targets: s.parse()?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for Targets {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
let targets = self
|
|
|
|
.targets
|
|
|
|
.iter()
|
|
|
|
.map(|(path, level)| format!("{}={}", path, level))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(",");
|
|
|
|
|
2022-03-28 04:27:07 +00:00
|
|
|
let max_level = [
|
|
|
|
Level::TRACE,
|
|
|
|
Level::DEBUG,
|
|
|
|
Level::INFO,
|
|
|
|
Level::WARN,
|
|
|
|
Level::ERROR,
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
.fold(None, |found, level| {
|
|
|
|
if found.is_none()
|
|
|
|
&& self
|
|
|
|
.targets
|
|
|
|
.would_enable("not_a_real_target_so_nothing_can_conflict", level)
|
|
|
|
{
|
|
|
|
Some(level.to_string().to_lowercase())
|
|
|
|
} else {
|
|
|
|
found
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if let Some(level) = max_level {
|
|
|
|
if !targets.is_empty() {
|
|
|
|
write!(f, "{},{}", level, targets)
|
|
|
|
} else {
|
|
|
|
write!(f, "{}", level)
|
|
|
|
}
|
|
|
|
} else if !targets.is_empty() {
|
|
|
|
write!(f, "{}", targets)
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-03-28 00:10:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for ImageFormat {
|
|
|
|
type Err = String;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
2022-03-28 20:34:36 +00:00
|
|
|
match s.to_lowercase().as_str() {
|
|
|
|
"jpeg" | "jpg" => Ok(Self::Jpeg),
|
|
|
|
"png" => Ok(Self::Png),
|
|
|
|
"webp" => Ok(Self::Webp),
|
|
|
|
other => Err(format!("Invalid variant: {}", other)),
|
2022-03-28 00:10:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for LogFormat {
|
|
|
|
type Err = String;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
for variant in Self::value_variants() {
|
|
|
|
if variant.to_possible_value().unwrap().matches(s, false) {
|
|
|
|
return Ok(*variant);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(format!("Invalid variant: {}", s))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for ImageFormat {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
self.to_possible_value()
|
|
|
|
.expect("no values are skipped")
|
|
|
|
.get_name()
|
|
|
|
.fmt(f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for LogFormat {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
self.to_possible_value()
|
|
|
|
.expect("no values are skipped")
|
|
|
|
.get_name()
|
|
|
|
.fmt(f)
|
|
|
|
}
|
|
|
|
}
|
2022-03-28 04:27:07 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::{Serde, Targets};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn builds_info_targets() {
|
|
|
|
let t: Serde<Targets> = "info".parse().unwrap();
|
|
|
|
|
|
|
|
println!("{:?}", t);
|
|
|
|
|
|
|
|
assert_eq!(t.to_string(), "info");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn builds_specific_targets() {
|
|
|
|
let t: Serde<Targets> = "pict_rs=info".parse().unwrap();
|
|
|
|
|
|
|
|
assert_eq!(t.to_string(), "pict_rs=info");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn builds_warn_and_specific_targets() {
|
|
|
|
let t: Serde<Targets> = "warn,pict_rs=info".parse().unwrap();
|
|
|
|
|
|
|
|
assert_eq!(t.to_string(), "warn,pict_rs=info");
|
|
|
|
}
|
|
|
|
}
|