1use qubit_datatype::{
12 BlankStringPolicy,
13 BooleanConversionOptions,
14 CollectionConversionOptions,
15 DataConversionOptions,
16 DurationConversionOptions,
17 DurationUnit,
18 EmptyItemPolicy,
19 StringConversionOptions,
20};
21use serde::de::Error as DeError;
22use serde::{
23 Deserialize,
24 Deserializer,
25 Serialize,
26 Serializer,
27};
28
29#[derive(Debug, Clone, PartialEq, Eq, Default)]
32pub struct ConfigReadOptions {
33 conversion: DataConversionOptions,
35 env_variable_substitution_enabled: bool,
38}
39
40impl Serialize for ConfigReadOptions {
41 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
43 where
44 S: Serializer,
45 {
46 ConfigReadOptionsSerde::from(self).serialize(serializer)
47 }
48}
49
50impl<'de> Deserialize<'de> for ConfigReadOptions {
51 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
53 where
54 D: Deserializer<'de>,
55 {
56 ConfigReadOptionsSerde::deserialize(deserializer)?
57 .try_into()
58 .map_err(D::Error::custom)
59 }
60}
61
62impl ConfigReadOptions {
63 #[must_use]
73 pub fn env_friendly() -> Self {
74 Self {
75 conversion: DataConversionOptions::env_friendly(),
76 env_variable_substitution_enabled: false,
77 }
78 }
79
80 #[inline]
86 pub fn conversion_options(&self) -> &DataConversionOptions {
87 &self.conversion
88 }
89
90 #[inline]
97 pub fn is_env_variable_substitution_enabled(&self) -> bool {
98 self.env_variable_substitution_enabled
99 }
100
101 #[must_use]
113 pub fn with_env_variable_substitution_enabled(mut self, enabled: bool) -> Self {
114 self.env_variable_substitution_enabled = enabled;
115 self
116 }
117
118 #[must_use]
128 pub fn with_blank_string_policy(mut self, policy: BlankStringPolicy) -> Self {
129 self.conversion = self.conversion.with_blank_string_policy(policy);
130 self
131 }
132
133 #[must_use]
143 pub fn with_empty_item_policy(mut self, policy: EmptyItemPolicy) -> Self {
144 self.conversion = self.conversion.with_empty_item_policy(policy);
145 self
146 }
147
148 #[must_use]
158 pub fn with_string_options(mut self, string: StringConversionOptions) -> Self {
159 self.conversion = self.conversion.with_string_options(string);
160 self
161 }
162
163 #[must_use]
173 pub fn with_boolean_options(mut self, boolean: BooleanConversionOptions) -> Self {
174 self.conversion = self.conversion.with_boolean_options(boolean);
175 self
176 }
177
178 #[must_use]
188 pub fn with_collection_options(mut self, collection: CollectionConversionOptions) -> Self {
189 self.conversion = self.conversion.with_collection_options(collection);
190 self
191 }
192
193 #[must_use]
203 pub fn with_duration_options(mut self, duration: DurationConversionOptions) -> Self {
204 self.conversion = self.conversion.with_duration_options(duration);
205 self
206 }
207}
208
209impl AsRef<DataConversionOptions> for ConfigReadOptions {
210 #[inline]
212 fn as_ref(&self) -> &DataConversionOptions {
213 &self.conversion
214 }
215}
216
217impl From<DataConversionOptions> for ConfigReadOptions {
218 #[inline]
222 fn from(conversion: DataConversionOptions) -> Self {
223 Self {
224 conversion,
225 env_variable_substitution_enabled: false,
226 }
227 }
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
232struct ConfigReadOptionsSerde {
233 #[serde(default)]
235 conversion: DataConversionOptionsSerde,
236 #[serde(default)]
238 env_variable_substitution_enabled: bool,
239}
240
241impl From<&ConfigReadOptions> for ConfigReadOptionsSerde {
242 fn from(options: &ConfigReadOptions) -> Self {
244 Self {
245 conversion: DataConversionOptionsSerde::from(&options.conversion),
246 env_variable_substitution_enabled: options.env_variable_substitution_enabled,
247 }
248 }
249}
250
251impl TryFrom<ConfigReadOptionsSerde> for ConfigReadOptions {
252 type Error = String;
253
254 fn try_from(value: ConfigReadOptionsSerde) -> Result<Self, Self::Error> {
256 Ok(Self {
257 conversion: value.conversion.try_into()?,
258 env_variable_substitution_enabled: value.env_variable_substitution_enabled,
259 })
260 }
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
265struct DataConversionOptionsSerde {
266 #[serde(default)]
268 string: StringConversionOptionsSerde,
269 #[serde(default)]
271 boolean: BooleanConversionOptionsSerde,
272 #[serde(default)]
274 collection: CollectionConversionOptionsSerde,
275 #[serde(default)]
277 duration: DurationConversionOptionsSerde,
278}
279
280impl Default for DataConversionOptionsSerde {
281 fn default() -> Self {
283 Self::from(&DataConversionOptions::default())
284 }
285}
286
287impl From<&DataConversionOptions> for DataConversionOptionsSerde {
288 fn from(options: &DataConversionOptions) -> Self {
290 Self {
291 string: StringConversionOptionsSerde::from(&options.string),
292 boolean: BooleanConversionOptionsSerde::from(&options.boolean),
293 collection: CollectionConversionOptionsSerde::from(&options.collection),
294 duration: DurationConversionOptionsSerde::from(&options.duration),
295 }
296 }
297}
298
299impl TryFrom<DataConversionOptionsSerde> for DataConversionOptions {
300 type Error = String;
301
302 fn try_from(value: DataConversionOptionsSerde) -> Result<Self, Self::Error> {
304 Ok(Self {
305 string: value.string.into(),
306 boolean: value.boolean.try_into()?,
307 collection: value.collection.into(),
308 duration: value.duration.into(),
309 })
310 }
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
315struct StringConversionOptionsSerde {
316 #[serde(default)]
318 trim: bool,
319 #[serde(default)]
321 blank_string_policy: BlankStringPolicySerde,
322}
323
324impl Default for StringConversionOptionsSerde {
325 fn default() -> Self {
327 Self::from(&StringConversionOptions::default())
328 }
329}
330
331impl From<&StringConversionOptions> for StringConversionOptionsSerde {
332 fn from(options: &StringConversionOptions) -> Self {
334 Self {
335 trim: options.trim,
336 blank_string_policy: options.blank_string_policy.into(),
337 }
338 }
339}
340
341impl From<StringConversionOptionsSerde> for StringConversionOptions {
342 fn from(value: StringConversionOptionsSerde) -> Self {
344 Self {
345 trim: value.trim,
346 blank_string_policy: value.blank_string_policy.into(),
347 }
348 }
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
353struct BooleanConversionOptionsSerde {
354 #[serde(default = "default_true_literals")]
356 true_literals: Vec<String>,
357 #[serde(default = "default_false_literals")]
359 false_literals: Vec<String>,
360 #[serde(default)]
362 case_sensitive: bool,
363}
364
365impl Default for BooleanConversionOptionsSerde {
366 fn default() -> Self {
368 Self::from(&BooleanConversionOptions::default())
369 }
370}
371
372impl From<&BooleanConversionOptions> for BooleanConversionOptionsSerde {
373 fn from(options: &BooleanConversionOptions) -> Self {
375 Self {
376 true_literals: options.true_literals().to_vec(),
377 false_literals: options.false_literals().to_vec(),
378 case_sensitive: options.case_sensitive,
379 }
380 }
381}
382
383impl TryFrom<BooleanConversionOptionsSerde> for BooleanConversionOptions {
384 type Error = String;
385
386 fn try_from(value: BooleanConversionOptionsSerde) -> Result<Self, Self::Error> {
388 let mut options = BooleanConversionOptions::strict();
389 let strict = BooleanConversionOptions::strict();
390 ensure_literal_prefix(&value.true_literals, strict.true_literals(), "true_literals")?;
391 ensure_literal_prefix(&value.false_literals, strict.false_literals(), "false_literals")?;
392 for literal in value.true_literals.iter().skip(strict.true_literals().len()) {
393 options = options.with_true_literal(literal);
394 }
395 for literal in value.false_literals.iter().skip(strict.false_literals().len()) {
396 options = options.with_false_literal(literal);
397 }
398 Ok(options.with_case_sensitive(value.case_sensitive))
399 }
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize)]
404struct CollectionConversionOptionsSerde {
405 #[serde(default)]
407 split_scalar_strings: bool,
408 #[serde(default = "default_delimiters")]
410 delimiters: Vec<char>,
411 #[serde(default)]
413 trim_items: bool,
414 #[serde(default)]
416 empty_item_policy: EmptyItemPolicySerde,
417}
418
419impl Default for CollectionConversionOptionsSerde {
420 fn default() -> Self {
422 Self::from(&CollectionConversionOptions::default())
423 }
424}
425
426impl From<&CollectionConversionOptions> for CollectionConversionOptionsSerde {
427 fn from(options: &CollectionConversionOptions) -> Self {
429 Self {
430 split_scalar_strings: options.split_scalar_strings,
431 delimiters: options.delimiters.clone(),
432 trim_items: options.trim_items,
433 empty_item_policy: options.empty_item_policy.into(),
434 }
435 }
436}
437
438impl From<CollectionConversionOptionsSerde> for CollectionConversionOptions {
439 fn from(value: CollectionConversionOptionsSerde) -> Self {
441 Self {
442 split_scalar_strings: value.split_scalar_strings,
443 delimiters: value.delimiters,
444 trim_items: value.trim_items,
445 empty_item_policy: value.empty_item_policy.into(),
446 }
447 }
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize)]
452struct DurationConversionOptionsSerde {
453 #[serde(default)]
455 unit: DurationUnitSerde,
456 #[serde(default = "default_append_unit_suffix")]
458 append_unit_suffix: bool,
459}
460
461impl Default for DurationConversionOptionsSerde {
462 fn default() -> Self {
464 Self::from(&DurationConversionOptions::default())
465 }
466}
467
468impl From<&DurationConversionOptions> for DurationConversionOptionsSerde {
469 fn from(options: &DurationConversionOptions) -> Self {
471 Self {
472 unit: options.unit.into(),
473 append_unit_suffix: options.append_unit_suffix,
474 }
475 }
476}
477
478impl From<DurationConversionOptionsSerde> for DurationConversionOptions {
479 fn from(value: DurationConversionOptionsSerde) -> Self {
481 Self {
482 unit: value.unit.into(),
483 append_unit_suffix: value.append_unit_suffix,
484 }
485 }
486}
487
488#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
490#[serde(rename_all = "snake_case")]
491enum BlankStringPolicySerde {
492 Preserve,
494 TreatAsMissing,
496 Reject,
498}
499
500impl Default for BlankStringPolicySerde {
501 fn default() -> Self {
503 BlankStringPolicy::Preserve.into()
504 }
505}
506
507impl From<BlankStringPolicy> for BlankStringPolicySerde {
508 fn from(value: BlankStringPolicy) -> Self {
510 match value {
511 BlankStringPolicy::Preserve => Self::Preserve,
512 BlankStringPolicy::TreatAsMissing => Self::TreatAsMissing,
513 BlankStringPolicy::Reject => Self::Reject,
514 }
515 }
516}
517
518impl From<BlankStringPolicySerde> for BlankStringPolicy {
519 fn from(value: BlankStringPolicySerde) -> Self {
521 match value {
522 BlankStringPolicySerde::Preserve => Self::Preserve,
523 BlankStringPolicySerde::TreatAsMissing => Self::TreatAsMissing,
524 BlankStringPolicySerde::Reject => Self::Reject,
525 }
526 }
527}
528
529#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
531#[serde(rename_all = "snake_case")]
532enum EmptyItemPolicySerde {
533 Keep,
535 Skip,
537 Reject,
539}
540
541impl Default for EmptyItemPolicySerde {
542 fn default() -> Self {
544 EmptyItemPolicy::Keep.into()
545 }
546}
547
548impl From<EmptyItemPolicy> for EmptyItemPolicySerde {
549 fn from(value: EmptyItemPolicy) -> Self {
551 match value {
552 EmptyItemPolicy::Keep => Self::Keep,
553 EmptyItemPolicy::Skip => Self::Skip,
554 EmptyItemPolicy::Reject => Self::Reject,
555 }
556 }
557}
558
559impl From<EmptyItemPolicySerde> for EmptyItemPolicy {
560 fn from(value: EmptyItemPolicySerde) -> Self {
562 match value {
563 EmptyItemPolicySerde::Keep => Self::Keep,
564 EmptyItemPolicySerde::Skip => Self::Skip,
565 EmptyItemPolicySerde::Reject => Self::Reject,
566 }
567 }
568}
569
570#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
572#[serde(rename_all = "snake_case")]
573enum DurationUnitSerde {
574 Nanoseconds,
576 Microseconds,
578 Milliseconds,
580 Seconds,
582 Minutes,
584 Hours,
586 Days,
588}
589
590impl Default for DurationUnitSerde {
591 fn default() -> Self {
593 DurationUnit::default().into()
594 }
595}
596
597impl From<DurationUnit> for DurationUnitSerde {
598 fn from(value: DurationUnit) -> Self {
600 match value {
601 DurationUnit::Nanoseconds => Self::Nanoseconds,
602 DurationUnit::Microseconds => Self::Microseconds,
603 DurationUnit::Milliseconds => Self::Milliseconds,
604 DurationUnit::Seconds => Self::Seconds,
605 DurationUnit::Minutes => Self::Minutes,
606 DurationUnit::Hours => Self::Hours,
607 DurationUnit::Days => Self::Days,
608 }
609 }
610}
611
612impl From<DurationUnitSerde> for DurationUnit {
613 fn from(value: DurationUnitSerde) -> Self {
615 match value {
616 DurationUnitSerde::Nanoseconds => Self::Nanoseconds,
617 DurationUnitSerde::Microseconds => Self::Microseconds,
618 DurationUnitSerde::Milliseconds => Self::Milliseconds,
619 DurationUnitSerde::Seconds => Self::Seconds,
620 DurationUnitSerde::Minutes => Self::Minutes,
621 DurationUnitSerde::Hours => Self::Hours,
622 DurationUnitSerde::Days => Self::Days,
623 }
624 }
625}
626
627fn default_true_literals() -> Vec<String> {
629 BooleanConversionOptions::default().true_literals().to_vec()
630}
631
632fn default_false_literals() -> Vec<String> {
634 BooleanConversionOptions::default().false_literals().to_vec()
635}
636
637fn default_delimiters() -> Vec<char> {
639 CollectionConversionOptions::default().delimiters
640}
641
642fn default_append_unit_suffix() -> bool {
644 DurationConversionOptions::default().append_unit_suffix
645}
646
647fn ensure_literal_prefix(actual: &[String], expected: &[String], field: &str) -> Result<(), String> {
649 if actual.len() < expected.len() || !actual.iter().zip(expected.iter()).all(|(left, right)| left == right) {
650 return Err(format!("{field} must start with the default literals: {:?}", expected));
651 }
652 Ok(())
653}