1use qubit_datatype::{
12 BlankStringPolicy, BooleanConversionOptions, CollectionConversionOptions,
13 DataConversionOptions, DurationConversionOptions, DurationUnit, EmptyItemPolicy,
14 StringConversionOptions,
15};
16use serde::de::Error as DeError;
17use serde::{Deserialize, Deserializer, Serialize, Serializer};
18
19#[derive(Debug, Clone, PartialEq, Eq, Default)]
22pub struct ConfigReadOptions {
23 conversion: DataConversionOptions,
25 env_variable_substitution_enabled: bool,
28}
29
30impl Serialize for ConfigReadOptions {
31 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
33 where
34 S: Serializer,
35 {
36 ConfigReadOptionsSerde::from(self).serialize(serializer)
37 }
38}
39
40impl<'de> Deserialize<'de> for ConfigReadOptions {
41 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
43 where
44 D: Deserializer<'de>,
45 {
46 ConfigReadOptionsSerde::deserialize(deserializer)?
47 .try_into()
48 .map_err(D::Error::custom)
49 }
50}
51
52impl ConfigReadOptions {
53 #[must_use]
63 pub fn env_friendly() -> Self {
64 Self {
65 conversion: DataConversionOptions::env_friendly(),
66 env_variable_substitution_enabled: false,
67 }
68 }
69
70 #[inline]
76 pub fn conversion_options(&self) -> &DataConversionOptions {
77 &self.conversion
78 }
79
80 #[inline]
87 pub fn is_env_variable_substitution_enabled(&self) -> bool {
88 self.env_variable_substitution_enabled
89 }
90
91 #[must_use]
103 pub fn with_env_variable_substitution_enabled(mut self, enabled: bool) -> Self {
104 self.env_variable_substitution_enabled = enabled;
105 self
106 }
107
108 #[must_use]
118 pub fn with_blank_string_policy(mut self, policy: BlankStringPolicy) -> Self {
119 self.conversion = self.conversion.with_blank_string_policy(policy);
120 self
121 }
122
123 #[must_use]
133 pub fn with_empty_item_policy(mut self, policy: EmptyItemPolicy) -> Self {
134 self.conversion = self.conversion.with_empty_item_policy(policy);
135 self
136 }
137
138 #[must_use]
148 pub fn with_string_options(mut self, string: StringConversionOptions) -> Self {
149 self.conversion = self.conversion.with_string_options(string);
150 self
151 }
152
153 #[must_use]
163 pub fn with_boolean_options(mut self, boolean: BooleanConversionOptions) -> Self {
164 self.conversion = self.conversion.with_boolean_options(boolean);
165 self
166 }
167
168 #[must_use]
178 pub fn with_collection_options(mut self, collection: CollectionConversionOptions) -> Self {
179 self.conversion = self.conversion.with_collection_options(collection);
180 self
181 }
182
183 #[must_use]
193 pub fn with_duration_options(mut self, duration: DurationConversionOptions) -> Self {
194 self.conversion = self.conversion.with_duration_options(duration);
195 self
196 }
197}
198
199impl AsRef<DataConversionOptions> for ConfigReadOptions {
200 #[inline]
202 fn as_ref(&self) -> &DataConversionOptions {
203 &self.conversion
204 }
205}
206
207impl From<DataConversionOptions> for ConfigReadOptions {
208 #[inline]
212 fn from(conversion: DataConversionOptions) -> Self {
213 Self {
214 conversion,
215 env_variable_substitution_enabled: false,
216 }
217 }
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222struct ConfigReadOptionsSerde {
223 #[serde(default)]
225 conversion: DataConversionOptionsSerde,
226 #[serde(default)]
228 env_variable_substitution_enabled: bool,
229}
230
231impl From<&ConfigReadOptions> for ConfigReadOptionsSerde {
232 fn from(options: &ConfigReadOptions) -> Self {
234 Self {
235 conversion: DataConversionOptionsSerde::from(&options.conversion),
236 env_variable_substitution_enabled: options.env_variable_substitution_enabled,
237 }
238 }
239}
240
241impl TryFrom<ConfigReadOptionsSerde> for ConfigReadOptions {
242 type Error = String;
243
244 fn try_from(value: ConfigReadOptionsSerde) -> Result<Self, Self::Error> {
246 Ok(Self {
247 conversion: value.conversion.try_into()?,
248 env_variable_substitution_enabled: value.env_variable_substitution_enabled,
249 })
250 }
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255struct DataConversionOptionsSerde {
256 #[serde(default)]
258 string: StringConversionOptionsSerde,
259 #[serde(default)]
261 boolean: BooleanConversionOptionsSerde,
262 #[serde(default)]
264 collection: CollectionConversionOptionsSerde,
265 #[serde(default)]
267 duration: DurationConversionOptionsSerde,
268}
269
270impl Default for DataConversionOptionsSerde {
271 fn default() -> Self {
273 Self::from(&DataConversionOptions::default())
274 }
275}
276
277impl From<&DataConversionOptions> for DataConversionOptionsSerde {
278 fn from(options: &DataConversionOptions) -> Self {
280 Self {
281 string: StringConversionOptionsSerde::from(&options.string),
282 boolean: BooleanConversionOptionsSerde::from(&options.boolean),
283 collection: CollectionConversionOptionsSerde::from(&options.collection),
284 duration: DurationConversionOptionsSerde::from(&options.duration),
285 }
286 }
287}
288
289impl TryFrom<DataConversionOptionsSerde> for DataConversionOptions {
290 type Error = String;
291
292 fn try_from(value: DataConversionOptionsSerde) -> Result<Self, Self::Error> {
294 Ok(Self {
295 string: value.string.into(),
296 boolean: value.boolean.try_into()?,
297 collection: value.collection.into(),
298 duration: value.duration.into(),
299 })
300 }
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize)]
305struct StringConversionOptionsSerde {
306 #[serde(default)]
308 trim: bool,
309 #[serde(default)]
311 blank_string_policy: BlankStringPolicySerde,
312}
313
314impl Default for StringConversionOptionsSerde {
315 fn default() -> Self {
317 Self::from(&StringConversionOptions::default())
318 }
319}
320
321impl From<&StringConversionOptions> for StringConversionOptionsSerde {
322 fn from(options: &StringConversionOptions) -> Self {
324 Self {
325 trim: options.trim,
326 blank_string_policy: options.blank_string_policy.into(),
327 }
328 }
329}
330
331impl From<StringConversionOptionsSerde> for StringConversionOptions {
332 fn from(value: StringConversionOptionsSerde) -> Self {
334 Self {
335 trim: value.trim,
336 blank_string_policy: value.blank_string_policy.into(),
337 }
338 }
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
343struct BooleanConversionOptionsSerde {
344 #[serde(default = "default_true_literals")]
346 true_literals: Vec<String>,
347 #[serde(default = "default_false_literals")]
349 false_literals: Vec<String>,
350 #[serde(default)]
352 case_sensitive: bool,
353}
354
355impl Default for BooleanConversionOptionsSerde {
356 fn default() -> Self {
358 Self::from(&BooleanConversionOptions::default())
359 }
360}
361
362impl From<&BooleanConversionOptions> for BooleanConversionOptionsSerde {
363 fn from(options: &BooleanConversionOptions) -> Self {
365 Self {
366 true_literals: options.true_literals().to_vec(),
367 false_literals: options.false_literals().to_vec(),
368 case_sensitive: options.case_sensitive,
369 }
370 }
371}
372
373impl TryFrom<BooleanConversionOptionsSerde> for BooleanConversionOptions {
374 type Error = String;
375
376 fn try_from(value: BooleanConversionOptionsSerde) -> Result<Self, Self::Error> {
378 let mut options = BooleanConversionOptions::strict();
379 let strict = BooleanConversionOptions::strict();
380 ensure_literal_prefix(
381 &value.true_literals,
382 strict.true_literals(),
383 "true_literals",
384 )?;
385 ensure_literal_prefix(
386 &value.false_literals,
387 strict.false_literals(),
388 "false_literals",
389 )?;
390 for literal in value
391 .true_literals
392 .iter()
393 .skip(strict.true_literals().len())
394 {
395 options = options.with_true_literal(literal);
396 }
397 for literal in value
398 .false_literals
399 .iter()
400 .skip(strict.false_literals().len())
401 {
402 options = options.with_false_literal(literal);
403 }
404 Ok(options.with_case_sensitive(value.case_sensitive))
405 }
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410struct CollectionConversionOptionsSerde {
411 #[serde(default)]
413 split_scalar_strings: bool,
414 #[serde(default = "default_delimiters")]
416 delimiters: Vec<char>,
417 #[serde(default)]
419 trim_items: bool,
420 #[serde(default)]
422 empty_item_policy: EmptyItemPolicySerde,
423}
424
425impl Default for CollectionConversionOptionsSerde {
426 fn default() -> Self {
428 Self::from(&CollectionConversionOptions::default())
429 }
430}
431
432impl From<&CollectionConversionOptions> for CollectionConversionOptionsSerde {
433 fn from(options: &CollectionConversionOptions) -> Self {
435 Self {
436 split_scalar_strings: options.split_scalar_strings,
437 delimiters: options.delimiters.clone(),
438 trim_items: options.trim_items,
439 empty_item_policy: options.empty_item_policy.into(),
440 }
441 }
442}
443
444impl From<CollectionConversionOptionsSerde> for CollectionConversionOptions {
445 fn from(value: CollectionConversionOptionsSerde) -> Self {
447 Self {
448 split_scalar_strings: value.split_scalar_strings,
449 delimiters: value.delimiters,
450 trim_items: value.trim_items,
451 empty_item_policy: value.empty_item_policy.into(),
452 }
453 }
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458struct DurationConversionOptionsSerde {
459 #[serde(default)]
461 unit: DurationUnitSerde,
462 #[serde(default = "default_append_unit_suffix")]
464 append_unit_suffix: bool,
465}
466
467impl Default for DurationConversionOptionsSerde {
468 fn default() -> Self {
470 Self::from(&DurationConversionOptions::default())
471 }
472}
473
474impl From<&DurationConversionOptions> for DurationConversionOptionsSerde {
475 fn from(options: &DurationConversionOptions) -> Self {
477 Self {
478 unit: options.unit.into(),
479 append_unit_suffix: options.append_unit_suffix,
480 }
481 }
482}
483
484impl From<DurationConversionOptionsSerde> for DurationConversionOptions {
485 fn from(value: DurationConversionOptionsSerde) -> Self {
487 Self {
488 unit: value.unit.into(),
489 append_unit_suffix: value.append_unit_suffix,
490 }
491 }
492}
493
494#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
496#[serde(rename_all = "snake_case")]
497enum BlankStringPolicySerde {
498 Preserve,
500 TreatAsMissing,
502 Reject,
504}
505
506impl Default for BlankStringPolicySerde {
507 fn default() -> Self {
509 BlankStringPolicy::Preserve.into()
510 }
511}
512
513impl From<BlankStringPolicy> for BlankStringPolicySerde {
514 fn from(value: BlankStringPolicy) -> Self {
516 match value {
517 BlankStringPolicy::Preserve => Self::Preserve,
518 BlankStringPolicy::TreatAsMissing => Self::TreatAsMissing,
519 BlankStringPolicy::Reject => Self::Reject,
520 }
521 }
522}
523
524impl From<BlankStringPolicySerde> for BlankStringPolicy {
525 fn from(value: BlankStringPolicySerde) -> Self {
527 match value {
528 BlankStringPolicySerde::Preserve => Self::Preserve,
529 BlankStringPolicySerde::TreatAsMissing => Self::TreatAsMissing,
530 BlankStringPolicySerde::Reject => Self::Reject,
531 }
532 }
533}
534
535#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
537#[serde(rename_all = "snake_case")]
538enum EmptyItemPolicySerde {
539 Keep,
541 Skip,
543 Reject,
545}
546
547impl Default for EmptyItemPolicySerde {
548 fn default() -> Self {
550 EmptyItemPolicy::Keep.into()
551 }
552}
553
554impl From<EmptyItemPolicy> for EmptyItemPolicySerde {
555 fn from(value: EmptyItemPolicy) -> Self {
557 match value {
558 EmptyItemPolicy::Keep => Self::Keep,
559 EmptyItemPolicy::Skip => Self::Skip,
560 EmptyItemPolicy::Reject => Self::Reject,
561 }
562 }
563}
564
565impl From<EmptyItemPolicySerde> for EmptyItemPolicy {
566 fn from(value: EmptyItemPolicySerde) -> Self {
568 match value {
569 EmptyItemPolicySerde::Keep => Self::Keep,
570 EmptyItemPolicySerde::Skip => Self::Skip,
571 EmptyItemPolicySerde::Reject => Self::Reject,
572 }
573 }
574}
575
576#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
578#[serde(rename_all = "snake_case")]
579enum DurationUnitSerde {
580 Nanoseconds,
582 Microseconds,
584 Milliseconds,
586 Seconds,
588 Minutes,
590 Hours,
592 Days,
594}
595
596impl Default for DurationUnitSerde {
597 fn default() -> Self {
599 DurationUnit::default().into()
600 }
601}
602
603impl From<DurationUnit> for DurationUnitSerde {
604 fn from(value: DurationUnit) -> Self {
606 match value {
607 DurationUnit::Nanoseconds => Self::Nanoseconds,
608 DurationUnit::Microseconds => Self::Microseconds,
609 DurationUnit::Milliseconds => Self::Milliseconds,
610 DurationUnit::Seconds => Self::Seconds,
611 DurationUnit::Minutes => Self::Minutes,
612 DurationUnit::Hours => Self::Hours,
613 DurationUnit::Days => Self::Days,
614 }
615 }
616}
617
618impl From<DurationUnitSerde> for DurationUnit {
619 fn from(value: DurationUnitSerde) -> Self {
621 match value {
622 DurationUnitSerde::Nanoseconds => Self::Nanoseconds,
623 DurationUnitSerde::Microseconds => Self::Microseconds,
624 DurationUnitSerde::Milliseconds => Self::Milliseconds,
625 DurationUnitSerde::Seconds => Self::Seconds,
626 DurationUnitSerde::Minutes => Self::Minutes,
627 DurationUnitSerde::Hours => Self::Hours,
628 DurationUnitSerde::Days => Self::Days,
629 }
630 }
631}
632
633fn default_true_literals() -> Vec<String> {
635 BooleanConversionOptions::default().true_literals().to_vec()
636}
637
638fn default_false_literals() -> Vec<String> {
640 BooleanConversionOptions::default()
641 .false_literals()
642 .to_vec()
643}
644
645fn default_delimiters() -> Vec<char> {
647 CollectionConversionOptions::default().delimiters
648}
649
650fn default_append_unit_suffix() -> bool {
652 DurationConversionOptions::default().append_unit_suffix
653}
654
655fn ensure_literal_prefix(
657 actual: &[String],
658 expected: &[String],
659 field: &str,
660) -> Result<(), String> {
661 if actual.len() < expected.len()
662 || !actual
663 .iter()
664 .zip(expected.iter())
665 .all(|(left, right)| left == right)
666 {
667 return Err(format!(
668 "{field} must start with the default literals: {:?}",
669 expected
670 ));
671 }
672 Ok(())
673}