use crate::{ config::primitives::{LogFormat, Targets}, formats::{AnimationFormat, AudioCodec, ImageFormat, VideoCodec}, serde_str::Serde, }; use clap::{Parser, Subcommand}; use std::{net::SocketAddr, path::PathBuf}; use url::Url; use super::primitives::RetentionValue; impl Args { pub(super) fn into_output(self) -> Output { let Args { config_file, old_db_path, log_format, log_targets, console_address, console_buffer_capacity, opentelemetry_url, opentelemetry_service_name, opentelemetry_targets, save_to, command, } = self; let old_db = OldDb { path: old_db_path }; let tracing = Tracing { logging: Logging { format: log_format, targets: log_targets.map(Serde::new), }, console: Console { address: console_address, buffer_capacity: console_buffer_capacity, }, opentelemetry: OpenTelemetry { url: opentelemetry_url, service_name: opentelemetry_service_name, targets: opentelemetry_targets.map(Serde::new), }, }; match command { Command::Run(Run { address, api_key, worker_id, client_pool_size, client_timeout, metrics_prometheus_address, media_preprocess_steps, media_max_file_size, media_retention_variants, media_retention_proxy, media_image_max_width, media_image_max_height, media_image_max_area, media_image_max_file_size, media_image_format, media_image_quality_avif, media_image_quality_jpeg, media_image_quality_jxl, media_image_quality_png, media_image_quality_webp, media_animation_max_width, media_animation_max_height, media_animation_max_area, media_animation_max_file_size, media_animation_max_frame_count, media_animation_format, media_animation_quality_apng, media_animation_quality_avif, media_animation_quality_webp, media_video_enable, media_video_allow_audio, media_video_max_width, media_video_max_height, media_video_max_area, media_video_max_file_size, media_video_max_frame_count, media_video_codec, media_video_audio_codec, media_video_quality_max, media_video_quality_240, media_video_quality_360, media_video_quality_480, media_video_quality_720, media_video_quality_1080, media_video_quality_1440, media_video_quality_2160, media_filters, read_only, max_file_count, store, }) => { let server = Server { address, api_key, worker_id, read_only, max_file_count, }; let client = Client { pool_size: client_pool_size, timeout: client_timeout, }; let metrics = Metrics { prometheus_address: metrics_prometheus_address, }; let retention = Retention { variants: media_retention_variants, proxy: media_retention_proxy, }; let image_quality = ImageQuality { avif: media_image_quality_avif, jpeg: media_image_quality_jpeg, jxl: media_image_quality_jxl, png: media_image_quality_png, webp: media_image_quality_webp, }; let image = Image { max_file_size: media_image_max_file_size, max_width: media_image_max_width, max_height: media_image_max_height, max_area: media_image_max_area, format: media_image_format, quality: image_quality.set(), }; let animation_quality = AnimationQuality { apng: media_animation_quality_apng, avif: media_animation_quality_avif, webp: media_animation_quality_webp, }; let animation = Animation { max_file_size: media_animation_max_file_size, max_width: media_animation_max_width, max_height: media_animation_max_height, max_area: media_animation_max_area, max_frame_count: media_animation_max_frame_count, format: media_animation_format, quality: animation_quality.set(), }; let video_quality = VideoQuality { crf_240: media_video_quality_240, crf_360: media_video_quality_360, crf_480: media_video_quality_480, crf_720: media_video_quality_720, crf_1080: media_video_quality_1080, crf_1440: media_video_quality_1440, crf_2160: media_video_quality_2160, crf_max: media_video_quality_max, }; let video = Video { enable: media_video_enable, allow_audio: media_video_allow_audio, max_file_size: media_video_max_file_size, max_width: media_video_max_width, max_height: media_video_max_height, max_area: media_video_max_area, max_frame_count: media_video_max_frame_count, video_codec: media_video_codec, audio_codec: media_video_audio_codec, quality: video_quality.set(), }; let media = Media { max_file_size: media_max_file_size, preprocess_steps: media_preprocess_steps, filters: media_filters, retention: retention.set(), image: image.set(), animation: animation.set(), video: video.set(), }; let operation = Operation::Run; match store { Some(RunStore::Filesystem(RunFilesystem { system, repo })) => { let store = Some(Store::Filesystem(system)); Output { config_format: ConfigFormat { server, client, old_db, tracing, metrics, media, store, repo, }, operation, config_file, save_to, } } Some(RunStore::ObjectStorage(RunObjectStorage { storage, repo })) => { let store = Some(Store::ObjectStorage(storage)); Output { config_format: ConfigFormat { server, client, old_db, tracing, metrics, media, store, repo, }, operation, config_file, save_to, } } None => Output { config_format: ConfigFormat { server, client, old_db, tracing, metrics, media, store: None, repo: None, }, operation, config_file, save_to, }, } } Command::MigrateStore(MigrateStore { skip_missing_files, store, }) => { let server = Server::default(); let client = Client::default(); let media = Media::default(); let metrics = Metrics::default(); match store { MigrateStoreFrom::Filesystem(MigrateFilesystem { from, to }) => match to { MigrateStoreTo::Filesystem(MigrateFilesystemInner { to, repo }) => Output { config_format: ConfigFormat { server, client, old_db, tracing, metrics, media, store: None, repo, }, operation: Operation::MigrateStore { skip_missing_files, from: from.into(), to: to.into(), }, config_file, save_to, }, MigrateStoreTo::ObjectStorage(MigrateObjectStorageInner { to, repo }) => { Output { config_format: ConfigFormat { server, client, old_db, tracing, metrics, media, store: None, repo, }, operation: Operation::MigrateStore { skip_missing_files, from: from.into(), to: to.into(), }, config_file, save_to, } } }, MigrateStoreFrom::ObjectStorage(MigrateObjectStorage { from, to }) => { match to { MigrateStoreTo::Filesystem(MigrateFilesystemInner { to, repo }) => { Output { config_format: ConfigFormat { server, client, old_db, tracing, metrics, media, store: None, repo, }, operation: Operation::MigrateStore { skip_missing_files, from: from.into(), to: to.into(), }, config_file, save_to, } } MigrateStoreTo::ObjectStorage(MigrateObjectStorageInner { to, repo, }) => Output { config_format: ConfigFormat { server, client, old_db, tracing, metrics, media, store: None, repo, }, operation: Operation::MigrateStore { skip_missing_files, from: from.into(), to: to.into(), }, config_file, save_to, }, } } } } } } } pub(super) struct Output { pub(super) config_format: ConfigFormat, pub(super) operation: Operation, pub(super) save_to: Option, pub(super) config_file: Option, } #[allow(clippy::large_enum_variant)] #[derive(Clone)] pub(crate) enum Operation { Run, MigrateStore { skip_missing_files: bool, from: crate::config::primitives::Store, to: crate::config::primitives::Store, }, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] pub(super) struct ConfigFormat { server: Server, client: Client, old_db: OldDb, tracing: Tracing, metrics: Metrics, media: Media, #[serde(skip_serializing_if = "Option::is_none")] repo: Option, #[serde(skip_serializing_if = "Option::is_none")] store: Option, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] struct Server { #[serde(skip_serializing_if = "Option::is_none")] address: Option, #[serde(skip_serializing_if = "Option::is_none")] worker_id: Option, #[serde(skip_serializing_if = "Option::is_none")] api_key: Option, #[serde(skip_serializing_if = "std::ops::Not::not")] read_only: bool, #[serde(skip_serializing_if = "Option::is_none")] max_file_count: Option, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] struct Client { #[serde(skip_serializing_if = "Option::is_none")] pool_size: Option, #[serde(skip_serializing_if = "Option::is_none")] timeout: Option, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] struct Tracing { logging: Logging, console: Console, opentelemetry: OpenTelemetry, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] struct Logging { #[serde(skip_serializing_if = "Option::is_none")] format: Option, #[serde(skip_serializing_if = "Option::is_none")] targets: Option>, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] struct Console { #[serde(skip_serializing_if = "Option::is_none")] address: Option, #[serde(skip_serializing_if = "Option::is_none")] buffer_capacity: Option, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] struct OpenTelemetry { #[serde(skip_serializing_if = "Option::is_none")] url: Option, #[serde(skip_serializing_if = "Option::is_none")] service_name: Option, #[serde(skip_serializing_if = "Option::is_none")] targets: Option>, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] struct Metrics { #[serde(skip_serializing_if = "Option::is_none")] prometheus_address: Option, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] struct OldDb { #[serde(skip_serializing_if = "Option::is_none")] path: Option, } #[derive(Debug, Default, serde::Serialize)] #[serde(rename_all = "snake_case")] struct Media { #[serde(skip_serializing_if = "Option::is_none")] max_file_size: Option, #[serde(skip_serializing_if = "Option::is_none")] preprocess_steps: Option, #[serde(skip_serializing_if = "Option::is_none")] filters: Option>, #[serde(skip_serializing_if = "Option::is_none")] retention: Option, #[serde(skip_serializing_if = "Option::is_none")] image: Option, #[serde(skip_serializing_if = "Option::is_none")] animation: Option, #[serde(skip_serializing_if = "Option::is_none")] video: Option