use serde::{ser::SerializeSeq, Serialize};
pub use super::super::options::{Container, Encoding};
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
#[non_exhaustive]
pub enum Model {
#[allow(missing_docs)]
FluxAlexisEn,
#[allow(missing_docs)]
FluxBreeEn,
#[allow(missing_docs)]
FluxBrittanyEn,
#[allow(missing_docs)]
FluxBrookeEn,
#[allow(missing_docs)]
FluxBruceEn,
#[allow(missing_docs)]
FluxCliffEn,
#[allow(missing_docs)]
FluxColeEn,
#[allow(missing_docs)]
FluxColinEn,
#[allow(missing_docs)]
FluxConorEn,
#[allow(missing_docs)]
FluxDonovanEn,
#[allow(missing_docs)]
FluxDrewEn,
#[allow(missing_docs)]
FluxEliseEn,
#[allow(missing_docs)]
FluxGemmaEn,
#[allow(missing_docs)]
FluxHaleyEn,
#[allow(missing_docs)]
FluxHannahEn,
#[allow(missing_docs)]
FluxHeatherEn,
#[allow(missing_docs)]
FluxJackEn,
#[allow(missing_docs)]
FluxKaiEn,
#[allow(missing_docs)]
FluxKelseyEn,
#[allow(missing_docs)]
FluxKitEn,
#[allow(missing_docs)]
FluxMaeveEn,
#[allow(missing_docs)]
FluxMarceloEn,
#[allow(missing_docs)]
FluxMarcusEn,
#[allow(missing_docs)]
FluxMeenaEn,
#[allow(missing_docs)]
FluxMeghanEn,
#[allow(missing_docs)]
FluxMilesEn,
#[allow(missing_docs)]
FluxNaveenEn,
#[allow(missing_docs)]
FluxPaigeEn,
#[allow(missing_docs)]
FluxPriyaEn,
#[allow(missing_docs)]
FluxRufusEn,
#[allow(missing_docs)]
FluxSeanEn,
#[allow(missing_docs)]
FluxSharonEn,
#[allow(missing_docs)]
FluxSiennaEn,
#[allow(missing_docs)]
FluxTannerEn,
#[allow(missing_docs)]
FluxWadeEn,
#[allow(missing_docs)]
FluxWesEn,
#[allow(missing_docs)]
CustomId(String),
}
impl AsRef<str> for Model {
fn as_ref(&self) -> &str {
match self {
Self::FluxAlexisEn => "flux-alexis-en",
Self::FluxBreeEn => "flux-bree-en",
Self::FluxBrittanyEn => "flux-brittany-en",
Self::FluxBrookeEn => "flux-brooke-en",
Self::FluxBruceEn => "flux-bruce-en",
Self::FluxCliffEn => "flux-cliff-en",
Self::FluxColeEn => "flux-cole-en",
Self::FluxColinEn => "flux-colin-en",
Self::FluxConorEn => "flux-conor-en",
Self::FluxDonovanEn => "flux-donovan-en",
Self::FluxDrewEn => "flux-drew-en",
Self::FluxEliseEn => "flux-elise-en",
Self::FluxGemmaEn => "flux-gemma-en",
Self::FluxHaleyEn => "flux-haley-en",
Self::FluxHannahEn => "flux-hannah-en",
Self::FluxHeatherEn => "flux-heather-en",
Self::FluxJackEn => "flux-jack-en",
Self::FluxKaiEn => "flux-kai-en",
Self::FluxKelseyEn => "flux-kelsey-en",
Self::FluxKitEn => "flux-kit-en",
Self::FluxMaeveEn => "flux-maeve-en",
Self::FluxMarceloEn => "flux-marcelo-en",
Self::FluxMarcusEn => "flux-marcus-en",
Self::FluxMeenaEn => "flux-meena-en",
Self::FluxMeghanEn => "flux-meghan-en",
Self::FluxMilesEn => "flux-miles-en",
Self::FluxNaveenEn => "flux-naveen-en",
Self::FluxPaigeEn => "flux-paige-en",
Self::FluxPriyaEn => "flux-priya-en",
Self::FluxRufusEn => "flux-rufus-en",
Self::FluxSeanEn => "flux-sean-en",
Self::FluxSharonEn => "flux-sharon-en",
Self::FluxSiennaEn => "flux-sienna-en",
Self::FluxTannerEn => "flux-tanner-en",
Self::FluxWadeEn => "flux-wade-en",
Self::FluxWesEn => "flux-wes-en",
Self::CustomId(id) => id,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
#[non_exhaustive]
pub enum CallbackMethod {
Post,
Put,
}
impl CallbackMethod {
pub(crate) fn as_str(&self) -> &str {
match self {
CallbackMethod::Post => "POST",
CallbackMethod::Put => "PUT",
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Options {
pub(super) model: Model,
pub(super) encoding: Option<Encoding>,
pub(super) sample_rate: Option<u32>,
pub(super) speed: Option<f64>,
pub(super) expressivity: Option<i32>,
pub(super) mip_opt_out: Option<bool>,
pub(super) tags: Vec<String>,
pub(super) container: Option<Container>,
pub(super) bit_rate: Option<u32>,
pub(super) callback: Option<String>,
pub(super) callback_method: Option<CallbackMethod>,
pub(super) priority_low: bool,
}
#[derive(Debug, PartialEq, Clone)]
pub struct OptionsBuilder(Options);
#[derive(Debug, PartialEq, Clone)]
pub(super) struct SerializableOptions<'a>(pub(super) &'a Options);
impl Options {
pub fn builder(model: Model) -> OptionsBuilder {
OptionsBuilder::new(model)
}
pub fn urlencoded(&self) -> Result<String, serde_urlencoded::ser::Error> {
serde_urlencoded::to_string(SerializableOptions(self))
}
pub(super) fn rest_only_options_set(&self) -> Option<&'static str> {
if self.container.is_some() {
Some("container")
} else if self.bit_rate.is_some() {
Some("bit_rate")
} else if self.callback.is_some() {
Some("callback")
} else if self.callback_method.is_some() {
Some("callback_method")
} else if self.priority_low {
Some("priority")
} else {
None
}
}
pub(super) fn rest_only_encoding_set(&self) -> Option<&'static str> {
match self.encoding {
Some(Encoding::Mp3) => Some("mp3"),
Some(Encoding::Opus) => Some("opus"),
Some(Encoding::Flac) => Some("flac"),
Some(Encoding::Aac) => Some("aac"),
_ => None,
}
}
}
impl OptionsBuilder {
pub fn new(model: Model) -> Self {
Self(Options {
model,
encoding: None,
sample_rate: None,
speed: None,
expressivity: None,
mip_opt_out: None,
tags: Vec::new(),
container: None,
bit_rate: None,
callback: None,
callback_method: None,
priority_low: false,
})
}
pub fn encoding(mut self, encoding: Encoding) -> Self {
self.0.encoding = Some(encoding);
self
}
pub fn sample_rate(mut self, sample_rate: u32) -> Self {
self.0.sample_rate = Some(sample_rate);
self
}
pub fn speed(mut self, speed: f64) -> Self {
self.0.speed = Some(speed);
self
}
pub fn expressivity(mut self, expressivity: i32) -> Self {
self.0.expressivity = Some(expressivity);
self
}
pub fn mip_opt_out(mut self, mip_opt_out: bool) -> Self {
self.0.mip_opt_out = Some(mip_opt_out);
self
}
pub fn tag<'a>(mut self, tags: impl IntoIterator<Item = &'a str>) -> Self {
self.0.tags.extend(tags.into_iter().map(String::from));
self
}
pub fn container(mut self, container: Container) -> Self {
self.0.container = Some(container);
self
}
pub fn bit_rate(mut self, bit_rate: u32) -> Self {
self.0.bit_rate = Some(bit_rate);
self
}
pub fn callback(mut self, callback: impl Into<String>) -> Self {
self.0.callback = Some(callback.into());
self
}
pub fn callback_method(mut self, callback_method: CallbackMethod) -> Self {
self.0.callback_method = Some(callback_method);
self
}
pub fn priority_low(mut self) -> Self {
self.0.priority_low = true;
self
}
pub fn build(self) -> Options {
self.0
}
}
impl Serialize for SerializableOptions<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
let Options {
model,
encoding,
sample_rate,
speed,
expressivity,
mip_opt_out,
tags,
container,
bit_rate,
callback,
callback_method,
priority_low,
} = self.0;
seq.serialize_element(&("model", model.as_ref()))?;
if let Some(encoding) = encoding {
seq.serialize_element(&("encoding", encoding.as_str()))?;
}
if let Some(sample_rate) = sample_rate {
seq.serialize_element(&("sample_rate", sample_rate))?;
}
if let Some(speed) = speed {
seq.serialize_element(&("speed", speed))?;
}
if let Some(expressivity) = expressivity {
seq.serialize_element(&("expressivity", expressivity))?;
}
if let Some(mip_opt_out) = mip_opt_out {
seq.serialize_element(&("mip_opt_out", mip_opt_out))?;
}
for tag in tags {
seq.serialize_element(&("tag", tag))?;
}
if let Some(container) = container {
seq.serialize_element(&("container", container.as_str()))?;
}
if let Some(bit_rate) = bit_rate {
seq.serialize_element(&("bit_rate", bit_rate))?;
}
if let Some(callback) = callback {
seq.serialize_element(&("callback", callback))?;
}
if let Some(callback_method) = callback_method {
seq.serialize_element(&("callback_method", callback_method.as_str()))?;
}
if *priority_low {
seq.serialize_element(&("priority", "low"))?;
}
seq.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn model_required_always_serialized() {
let options = Options::builder(Model::FluxHaleyEn).build();
assert_eq!(options.urlencoded().unwrap(), "model=flux-haley-en");
}
#[test]
fn all_options_serialized() {
let options = Options::builder(Model::CustomId("flux-custom-en".to_string()))
.encoding(Encoding::Mp3)
.sample_rate(48000)
.speed(1.05)
.expressivity(-1)
.mip_opt_out(true)
.tag(["prod", "client-xyz"])
.container(Container::None)
.bit_rate(48000)
.callback("https://example.com/hook")
.callback_method(CallbackMethod::Put)
.priority_low()
.build();
assert_eq!(
options.urlencoded().unwrap(),
"model=flux-custom-en&encoding=mp3&sample_rate=48000&speed=1.05&expressivity=-1&mip_opt_out=true&tag=prod&tag=client-xyz&container=none&bit_rate=48000&callback=https%3A%2F%2Fexample.com%2Fhook&callback_method=PUT&priority=low"
);
}
#[test]
fn rest_only_detection() {
let ws_ok = Options::builder(Model::FluxHaleyEn)
.encoding(Encoding::Linear16)
.sample_rate(24000)
.speed(0.95)
.expressivity(1)
.mip_opt_out(false)
.tag(["a"])
.build();
assert_eq!(ws_ok.rest_only_options_set(), None);
let with_container = Options::builder(Model::FluxHaleyEn)
.container(Container::Wav)
.build();
assert_eq!(with_container.rest_only_options_set(), Some("container"));
let with_callback = Options::builder(Model::FluxHaleyEn)
.callback("https://example.com")
.build();
assert_eq!(with_callback.rest_only_options_set(), Some("callback"));
let with_priority = Options::builder(Model::FluxHaleyEn).priority_low().build();
assert_eq!(with_priority.rest_only_options_set(), Some("priority"));
}
#[test]
fn rest_only_encoding_detection() {
for (encoding, name) in [
(Encoding::Mp3, "mp3"),
(Encoding::Opus, "opus"),
(Encoding::Flac, "flac"),
(Encoding::Aac, "aac"),
] {
let options = Options::builder(Model::FluxHaleyEn)
.encoding(encoding)
.build();
assert_eq!(options.rest_only_encoding_set(), Some(name));
}
for options in [
Options::builder(Model::FluxHaleyEn).build(),
Options::builder(Model::FluxHaleyEn)
.encoding(Encoding::Linear16)
.build(),
Options::builder(Model::FluxHaleyEn)
.encoding(Encoding::Mulaw)
.build(),
Options::builder(Model::FluxHaleyEn)
.encoding(Encoding::Alaw)
.build(),
Options::builder(Model::FluxHaleyEn)
.encoding(Encoding::CustomEncoding("linear32".to_string()))
.build(),
] {
assert_eq!(options.rest_only_encoding_set(), None);
}
}
}