use super::*;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum AvatarCompatibilityMode {
#[default]
Strict,
LegacyV1,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum RequestStyle {
Explicit(AvatarStyleOptions),
Automatic,
}
#[derive(Clone)]
pub struct AvatarRequest {
identity: AvatarIdentity,
spec: AvatarSpec,
style: RequestStyle,
compatibility: AvatarCompatibilityMode,
}
impl std::fmt::Debug for AvatarRequest {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AvatarRequest")
.field("identity", &"[REDACTED]")
.field("spec", &self.spec)
.field("style", &self.style)
.field("compatibility", &self.compatibility)
.finish()
}
}
impl AvatarRequest {
pub fn builder(identity: AvatarIdentity) -> AvatarRequestBuilder {
AvatarRequestBuilder::new(identity)
}
pub fn new(identity: AvatarIdentity, spec: AvatarSpec, style: AvatarStyleOptions) -> Self {
Self {
identity,
spec,
style: RequestStyle::Explicit(style),
compatibility: AvatarCompatibilityMode::Strict,
}
}
pub fn automatic(identity: AvatarIdentity, spec: AvatarSpec) -> Self {
Self {
identity,
spec,
style: RequestStyle::Automatic,
compatibility: AvatarCompatibilityMode::Strict,
}
}
pub const fn spec(&self) -> AvatarSpec {
self.spec
}
pub const fn compatibility_mode(&self) -> AvatarCompatibilityMode {
self.compatibility
}
pub const fn explicit_style(&self) -> Option<AvatarStyleOptions> {
match self.style {
RequestStyle::Explicit(style) => Some(style),
RequestStyle::Automatic => None,
}
}
pub fn prepare(self) -> Result<PreparedAvatar, AvatarRequestError> {
PreparedAvatar::from_request(self)
}
pub(crate) fn into_parts(
self,
) -> (
AvatarIdentity,
AvatarSpec,
Option<AvatarStyleOptions>,
AvatarCompatibilityMode,
) {
let style = match self.style {
RequestStyle::Explicit(style) => Some(style),
RequestStyle::Automatic => None,
};
(self.identity, self.spec, style, self.compatibility)
}
}
#[derive(Clone)]
pub struct AvatarRequestBuilder {
identity: AvatarIdentity,
width: u32,
height: u32,
seed: u64,
style: RequestStyle,
compatibility: AvatarCompatibilityMode,
}
impl std::fmt::Debug for AvatarRequestBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AvatarRequestBuilder")
.field("identity", &"[REDACTED]")
.field("width", &self.width)
.field("height", &self.height)
.field("seed", &self.seed)
.field("style", &self.style)
.field("compatibility", &self.compatibility)
.finish()
}
}
impl AvatarRequestBuilder {
pub fn new(identity: AvatarIdentity) -> Self {
let spec = AvatarSpec::default();
Self {
identity,
width: spec.width(),
height: spec.height(),
seed: spec.seed(),
style: RequestStyle::Explicit(AvatarStyleOptions::default()),
compatibility: AvatarCompatibilityMode::Strict,
}
}
pub fn spec(mut self, spec: AvatarSpec) -> Self {
self.width = spec.width();
self.height = spec.height();
self.seed = spec.seed();
self
}
pub fn size(mut self, width: u32, height: u32) -> Self {
self.width = width;
self.height = height;
self
}
pub fn style_variant(mut self, seed: u64) -> Self {
self.seed = seed;
self
}
pub fn seed(self, seed: u64) -> Self {
self.style_variant(seed)
}
pub fn automatic_style(mut self) -> Self {
self.style = RequestStyle::Automatic;
self
}
pub fn style(mut self, style: AvatarStyleOptions) -> Self {
self.style = RequestStyle::Explicit(style);
self
}
pub fn options(self, options: AvatarOptions) -> Self {
self.style(AvatarStyleOptions::from_options(options))
}
pub fn kind(self, kind: AvatarKind) -> Self {
self.with_style(|style| style.kind = kind)
}
pub fn background(self, background: AvatarBackground) -> Self {
self.with_style(|style| style.background = background)
}
pub fn accessory(self, accessory: AvatarAccessory) -> Self {
self.with_style(|style| style.accessory = accessory)
}
pub fn color(self, color: AvatarColor) -> Self {
self.with_style(|style| style.color = color)
}
pub fn expression(self, expression: AvatarExpression) -> Self {
self.with_style(|style| style.expression = expression)
}
pub fn shape(self, shape: AvatarShape) -> Self {
self.with_style(|style| style.shape = shape)
}
pub fn strict_style_validation(mut self) -> Self {
self.compatibility = AvatarCompatibilityMode::Strict;
self
}
pub fn legacy_v1_compatibility(mut self) -> Self {
self.compatibility = AvatarCompatibilityMode::LegacyV1;
self
}
pub fn build(self) -> Result<AvatarRequest, AvatarRequestError> {
let spec = AvatarSpec::new(self.width, self.height, self.seed)?;
Ok(AvatarRequest {
identity: self.identity,
spec,
style: self.style,
compatibility: self.compatibility,
})
}
pub fn prepare(self) -> Result<PreparedAvatar, AvatarRequestError> {
self.build()?.prepare()
}
fn with_style(mut self, update: impl FnOnce(&mut AvatarStyleOptions)) -> Self {
let mut style = match self.style {
RequestStyle::Explicit(style) => style,
RequestStyle::Automatic => AvatarStyleOptions::default(),
};
update(&mut style);
self.style = RequestStyle::Explicit(style);
self
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AvatarRequestError {
Spec(AvatarSpecError),
Style(AvatarStyleValidationError),
}
impl From<AvatarSpecError> for AvatarRequestError {
fn from(error: AvatarSpecError) -> Self {
Self::Spec(error)
}
}
impl From<AvatarStyleValidationError> for AvatarRequestError {
fn from(error: AvatarStyleValidationError) -> Self {
Self::Style(error)
}
}
impl std::fmt::Display for AvatarRequestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Spec(error) => error.fmt(f),
Self::Style(error) => error.fmt(f),
}
}
}
impl std::error::Error for AvatarRequestError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Spec(error) => Some(error),
Self::Style(error) => Some(error),
}
}
}