use qubit_datatype::{
BlankStringPolicy, BooleanConversionOptions, CollectionConversionOptions,
DataConversionOptions, DurationConversionOptions, DurationUnit, EmptyItemPolicy,
StringConversionOptions,
};
use serde::de::Error as DeError;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ConfigReadOptions {
conversion: DataConversionOptions,
env_variable_substitution_enabled: bool,
}
impl Serialize for ConfigReadOptions {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
ConfigReadOptionsSerde::from(self).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for ConfigReadOptions {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
ConfigReadOptionsSerde::deserialize(deserializer)?
.try_into()
.map_err(D::Error::custom)
}
}
impl ConfigReadOptions {
#[must_use]
pub fn env_friendly() -> Self {
Self {
conversion: DataConversionOptions::env_friendly(),
env_variable_substitution_enabled: false,
}
}
#[inline]
pub fn conversion_options(&self) -> &DataConversionOptions {
&self.conversion
}
#[inline]
pub fn is_env_variable_substitution_enabled(&self) -> bool {
self.env_variable_substitution_enabled
}
#[must_use]
pub fn with_env_variable_substitution_enabled(mut self, enabled: bool) -> Self {
self.env_variable_substitution_enabled = enabled;
self
}
#[must_use]
pub fn with_blank_string_policy(mut self, policy: BlankStringPolicy) -> Self {
self.conversion = self.conversion.with_blank_string_policy(policy);
self
}
#[must_use]
pub fn with_empty_item_policy(mut self, policy: EmptyItemPolicy) -> Self {
self.conversion = self.conversion.with_empty_item_policy(policy);
self
}
#[must_use]
pub fn with_string_options(mut self, string: StringConversionOptions) -> Self {
self.conversion = self.conversion.with_string_options(string);
self
}
#[must_use]
pub fn with_boolean_options(mut self, boolean: BooleanConversionOptions) -> Self {
self.conversion = self.conversion.with_boolean_options(boolean);
self
}
#[must_use]
pub fn with_collection_options(mut self, collection: CollectionConversionOptions) -> Self {
self.conversion = self.conversion.with_collection_options(collection);
self
}
#[must_use]
pub fn with_duration_options(mut self, duration: DurationConversionOptions) -> Self {
self.conversion = self.conversion.with_duration_options(duration);
self
}
}
impl AsRef<DataConversionOptions> for ConfigReadOptions {
#[inline]
fn as_ref(&self) -> &DataConversionOptions {
&self.conversion
}
}
impl From<DataConversionOptions> for ConfigReadOptions {
#[inline]
fn from(conversion: DataConversionOptions) -> Self {
Self {
conversion,
env_variable_substitution_enabled: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct ConfigReadOptionsSerde {
#[serde(default)]
conversion: DataConversionOptionsSerde,
#[serde(default)]
env_variable_substitution_enabled: bool,
}
impl From<&ConfigReadOptions> for ConfigReadOptionsSerde {
fn from(options: &ConfigReadOptions) -> Self {
Self {
conversion: DataConversionOptionsSerde::from(&options.conversion),
env_variable_substitution_enabled: options.env_variable_substitution_enabled,
}
}
}
impl TryFrom<ConfigReadOptionsSerde> for ConfigReadOptions {
type Error = String;
fn try_from(value: ConfigReadOptionsSerde) -> Result<Self, Self::Error> {
Ok(Self {
conversion: value.conversion.try_into()?,
env_variable_substitution_enabled: value.env_variable_substitution_enabled,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct DataConversionOptionsSerde {
#[serde(default)]
string: StringConversionOptionsSerde,
#[serde(default)]
boolean: BooleanConversionOptionsSerde,
#[serde(default)]
collection: CollectionConversionOptionsSerde,
#[serde(default)]
duration: DurationConversionOptionsSerde,
}
impl Default for DataConversionOptionsSerde {
fn default() -> Self {
Self::from(&DataConversionOptions::default())
}
}
impl From<&DataConversionOptions> for DataConversionOptionsSerde {
fn from(options: &DataConversionOptions) -> Self {
Self {
string: StringConversionOptionsSerde::from(&options.string),
boolean: BooleanConversionOptionsSerde::from(&options.boolean),
collection: CollectionConversionOptionsSerde::from(&options.collection),
duration: DurationConversionOptionsSerde::from(&options.duration),
}
}
}
impl TryFrom<DataConversionOptionsSerde> for DataConversionOptions {
type Error = String;
fn try_from(value: DataConversionOptionsSerde) -> Result<Self, Self::Error> {
Ok(Self {
string: value.string.into(),
boolean: value.boolean.try_into()?,
collection: value.collection.into(),
duration: value.duration.into(),
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct StringConversionOptionsSerde {
#[serde(default)]
trim: bool,
#[serde(default)]
blank_string_policy: BlankStringPolicySerde,
}
impl Default for StringConversionOptionsSerde {
fn default() -> Self {
Self::from(&StringConversionOptions::default())
}
}
impl From<&StringConversionOptions> for StringConversionOptionsSerde {
fn from(options: &StringConversionOptions) -> Self {
Self {
trim: options.trim,
blank_string_policy: options.blank_string_policy.into(),
}
}
}
impl From<StringConversionOptionsSerde> for StringConversionOptions {
fn from(value: StringConversionOptionsSerde) -> Self {
Self {
trim: value.trim,
blank_string_policy: value.blank_string_policy.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct BooleanConversionOptionsSerde {
#[serde(default = "default_true_literals")]
true_literals: Vec<String>,
#[serde(default = "default_false_literals")]
false_literals: Vec<String>,
#[serde(default)]
case_sensitive: bool,
}
impl Default for BooleanConversionOptionsSerde {
fn default() -> Self {
Self::from(&BooleanConversionOptions::default())
}
}
impl From<&BooleanConversionOptions> for BooleanConversionOptionsSerde {
fn from(options: &BooleanConversionOptions) -> Self {
Self {
true_literals: options.true_literals().to_vec(),
false_literals: options.false_literals().to_vec(),
case_sensitive: options.case_sensitive,
}
}
}
impl TryFrom<BooleanConversionOptionsSerde> for BooleanConversionOptions {
type Error = String;
fn try_from(value: BooleanConversionOptionsSerde) -> Result<Self, Self::Error> {
let mut options = BooleanConversionOptions::strict();
let strict = BooleanConversionOptions::strict();
ensure_literal_prefix(
&value.true_literals,
strict.true_literals(),
"true_literals",
)?;
ensure_literal_prefix(
&value.false_literals,
strict.false_literals(),
"false_literals",
)?;
for literal in value
.true_literals
.iter()
.skip(strict.true_literals().len())
{
options = options.with_true_literal(literal);
}
for literal in value
.false_literals
.iter()
.skip(strict.false_literals().len())
{
options = options.with_false_literal(literal);
}
Ok(options.with_case_sensitive(value.case_sensitive))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct CollectionConversionOptionsSerde {
#[serde(default)]
split_scalar_strings: bool,
#[serde(default = "default_delimiters")]
delimiters: Vec<char>,
#[serde(default)]
trim_items: bool,
#[serde(default)]
empty_item_policy: EmptyItemPolicySerde,
}
impl Default for CollectionConversionOptionsSerde {
fn default() -> Self {
Self::from(&CollectionConversionOptions::default())
}
}
impl From<&CollectionConversionOptions> for CollectionConversionOptionsSerde {
fn from(options: &CollectionConversionOptions) -> Self {
Self {
split_scalar_strings: options.split_scalar_strings,
delimiters: options.delimiters.clone(),
trim_items: options.trim_items,
empty_item_policy: options.empty_item_policy.into(),
}
}
}
impl From<CollectionConversionOptionsSerde> for CollectionConversionOptions {
fn from(value: CollectionConversionOptionsSerde) -> Self {
Self {
split_scalar_strings: value.split_scalar_strings,
delimiters: value.delimiters,
trim_items: value.trim_items,
empty_item_policy: value.empty_item_policy.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct DurationConversionOptionsSerde {
#[serde(default)]
unit: DurationUnitSerde,
#[serde(default = "default_append_unit_suffix")]
append_unit_suffix: bool,
}
impl Default for DurationConversionOptionsSerde {
fn default() -> Self {
Self::from(&DurationConversionOptions::default())
}
}
impl From<&DurationConversionOptions> for DurationConversionOptionsSerde {
fn from(options: &DurationConversionOptions) -> Self {
Self {
unit: options.unit.into(),
append_unit_suffix: options.append_unit_suffix,
}
}
}
impl From<DurationConversionOptionsSerde> for DurationConversionOptions {
fn from(value: DurationConversionOptionsSerde) -> Self {
Self {
unit: value.unit.into(),
append_unit_suffix: value.append_unit_suffix,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum BlankStringPolicySerde {
Preserve,
TreatAsMissing,
Reject,
}
impl Default for BlankStringPolicySerde {
fn default() -> Self {
BlankStringPolicy::Preserve.into()
}
}
impl From<BlankStringPolicy> for BlankStringPolicySerde {
fn from(value: BlankStringPolicy) -> Self {
match value {
BlankStringPolicy::Preserve => Self::Preserve,
BlankStringPolicy::TreatAsMissing => Self::TreatAsMissing,
BlankStringPolicy::Reject => Self::Reject,
}
}
}
impl From<BlankStringPolicySerde> for BlankStringPolicy {
fn from(value: BlankStringPolicySerde) -> Self {
match value {
BlankStringPolicySerde::Preserve => Self::Preserve,
BlankStringPolicySerde::TreatAsMissing => Self::TreatAsMissing,
BlankStringPolicySerde::Reject => Self::Reject,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum EmptyItemPolicySerde {
Keep,
Skip,
Reject,
}
impl Default for EmptyItemPolicySerde {
fn default() -> Self {
EmptyItemPolicy::Keep.into()
}
}
impl From<EmptyItemPolicy> for EmptyItemPolicySerde {
fn from(value: EmptyItemPolicy) -> Self {
match value {
EmptyItemPolicy::Keep => Self::Keep,
EmptyItemPolicy::Skip => Self::Skip,
EmptyItemPolicy::Reject => Self::Reject,
}
}
}
impl From<EmptyItemPolicySerde> for EmptyItemPolicy {
fn from(value: EmptyItemPolicySerde) -> Self {
match value {
EmptyItemPolicySerde::Keep => Self::Keep,
EmptyItemPolicySerde::Skip => Self::Skip,
EmptyItemPolicySerde::Reject => Self::Reject,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum DurationUnitSerde {
Nanoseconds,
Microseconds,
Milliseconds,
Seconds,
Minutes,
Hours,
Days,
}
impl Default for DurationUnitSerde {
fn default() -> Self {
DurationUnit::default().into()
}
}
impl From<DurationUnit> for DurationUnitSerde {
fn from(value: DurationUnit) -> Self {
match value {
DurationUnit::Nanoseconds => Self::Nanoseconds,
DurationUnit::Microseconds => Self::Microseconds,
DurationUnit::Milliseconds => Self::Milliseconds,
DurationUnit::Seconds => Self::Seconds,
DurationUnit::Minutes => Self::Minutes,
DurationUnit::Hours => Self::Hours,
DurationUnit::Days => Self::Days,
}
}
}
impl From<DurationUnitSerde> for DurationUnit {
fn from(value: DurationUnitSerde) -> Self {
match value {
DurationUnitSerde::Nanoseconds => Self::Nanoseconds,
DurationUnitSerde::Microseconds => Self::Microseconds,
DurationUnitSerde::Milliseconds => Self::Milliseconds,
DurationUnitSerde::Seconds => Self::Seconds,
DurationUnitSerde::Minutes => Self::Minutes,
DurationUnitSerde::Hours => Self::Hours,
DurationUnitSerde::Days => Self::Days,
}
}
}
fn default_true_literals() -> Vec<String> {
BooleanConversionOptions::default().true_literals().to_vec()
}
fn default_false_literals() -> Vec<String> {
BooleanConversionOptions::default()
.false_literals()
.to_vec()
}
fn default_delimiters() -> Vec<char> {
CollectionConversionOptions::default().delimiters
}
fn default_append_unit_suffix() -> bool {
DurationConversionOptions::default().append_unit_suffix
}
fn ensure_literal_prefix(
actual: &[String],
expected: &[String],
field: &str,
) -> Result<(), String> {
if actual.len() < expected.len()
|| !actual
.iter()
.zip(expected.iter())
.all(|(left, right)| left == right)
{
return Err(format!(
"{field} must start with the default literals: {:?}",
expected
));
}
Ok(())
}