1#[cfg(feature = "derive")]
2use crate::{DescFromEnvironment, PrefixedFromEnvironment, SalakDescContext};
3use crate::{FromEnvironment, PropertyError, Res, SalakContext};
4use std::{
5 collections::HashSet,
6 ffi::OsString,
7 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
8 path::PathBuf,
9 time::Duration,
10};
11
12#[derive(Clone, Debug)]
14pub enum Property<'a> {
15 S(&'a str),
17 O(String),
19 I(i64),
21 F(f64),
23 B(bool),
25}
26
27pub trait IsProperty: Sized {
32 #[inline]
35 fn is_empty(p: &Property<'_>) -> bool {
36 match p {
37 Property::S(s) => s.is_empty(),
38 Property::O(s) => s.is_empty(),
39 _ => false,
40 }
41 }
42
43 fn from_property(_: Property<'_>) -> Res<Self>;
45}
46
47impl<T: IsProperty> FromEnvironment for T {
48 #[inline]
49 fn from_env(val: Option<Property<'_>>, env: &mut SalakContext<'_>) -> Res<Self> {
50 if let Some(v) = val {
51 if !Self::is_empty(&v) {
52 return Self::from_property(v);
53 }
54 }
55 Err(PropertyError::NotFound(env.current_key().to_string()))
56 }
57}
58
59#[cfg(feature = "derive")]
60#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
61impl<T: IsProperty> DescFromEnvironment for T {
62 #[inline]
63 fn key_desc(env: &mut SalakDescContext<'_>) {
64 env.current.ignore = false;
65 env.current.set_required(true);
66 }
67}
68
69impl FromEnvironment for () {
70 fn from_env(_: Option<Property<'_>>, _: &mut SalakContext<'_>) -> Res<Self> {
71 Ok(())
72 }
73}
74#[cfg(feature = "derive")]
75#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
76impl DescFromEnvironment for () {
77 fn key_desc(_: &mut SalakDescContext<'_>) {}
78}
79
80#[cfg(feature = "derive")]
81#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
82impl PrefixedFromEnvironment for () {
83 fn prefix() -> &'static str {
84 ""
85 }
86}
87
88#[inline]
89fn check_f64(f: f64) -> Result<f64, PropertyError> {
90 if f.is_finite() {
91 Ok(f)
92 } else {
93 Err(PropertyError::parse_fail("f64 value is infinite"))
94 }
95}
96
97impl IsProperty for String {
98 #[inline]
99 fn is_empty(_: &Property<'_>) -> bool {
100 false
101 }
102 #[inline]
103 fn from_property(p: Property<'_>) -> Res<Self> {
104 Ok(match p {
105 Property::S(v) => v.to_string(),
106 Property::O(v) => v,
107 Property::I(v) => v.to_string(),
108 Property::F(v) => check_f64(v)?.to_string(),
109 Property::B(v) => v.to_string(),
110 })
111 }
112}
113impl IsProperty for bool {
114 #[inline]
115 fn from_property(p: Property<'_>) -> Res<Self> {
116 fn str_to_bool(v: &str) -> Res<bool> {
117 match v {
118 "yes" | "true" => Ok(true),
119 "no" | "false" => Ok(false),
120 _ => Err(PropertyError::parse_fail("invalid bool value")),
121 }
122 }
123 match p {
124 Property::B(v) => Ok(v),
125 Property::S(v) => str_to_bool(v),
126 Property::O(v) => str_to_bool(&v),
127 _ => Err(PropertyError::parse_fail("can not num to bool")),
128 }
129 }
130}
131
132macro_rules! impl_property_num {
133 ($($x:ident),+) => {$(
134 impl IsProperty for $x {
135 #[inline]
136 fn from_property(p: Property<'_>) -> Res<Self> {
137 use std::convert::TryFrom;
138 Ok(match p {
139 Property::S(s) => s.parse::<$x>()?,
140 Property::O(s) => s.parse::<$x>()?,
141 Property::I(s) => $x::try_from(s)?,
142 Property::F(s) => check_f64(s)? as $x,
143 _ => return Err(PropertyError::parse_fail("can not convert bool to num")),
144 })
145 }
146
147 }
148
149 )+}
150}
151
152impl_property_num!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize);
153
154macro_rules! impl_property_float {
155 ($($x:ident),+) => {$(
156 #[allow(trivial_numeric_casts)]
157 impl IsProperty for $x {
158 #[inline]
159 fn from_property(p: Property<'_>) -> Res<Self> {
160 Ok(match p {
161 Property::S(s) => s.parse::<$x>()?,
162 Property::O(s) => s.parse::<$x>()?,
163 Property::I(s) => s as $x,
164 Property::F(s) => check_f64(s)? as $x,
165 _ => return Err(PropertyError::parse_fail("can not convert bool to num")),
166 })
167 }
168
169 }
170
171 )+}
172}
173
174impl_property_float!(f32, f64);
175
176#[inline]
177fn parse_duration_from_str(du: &str) -> Res<Duration> {
178 let mut i = 0;
179 let mut multi = 1;
180 let mut last = None;
181 for c in du.chars().rev() {
182 match c {
183 'h' | 'm' | 's' if last.is_none() => {
184 if c == 'm' {
185 last = Some('M');
186 } else {
187 last = Some(c);
188 }
189 }
190 'm' | 'u' | 'n' if last == Some('s') => {
191 last = Some(c);
192 }
193 c if ('0'..='9').contains(&c) => {
194 if last.is_none() {
195 last = Some('s');
196 }
197 i += multi * (c as u64 - '0' as u64);
198 multi *= 10;
199 }
200 _ => return Err(PropertyError::parse_fail("Invalid duration")),
201 }
202 }
203 Ok(match last.unwrap_or('s') {
204 'h' => Duration::new(i * 3600, 0),
205 'M' => Duration::new(i * 60, 0),
206 's' => Duration::from_secs(i),
207 'm' => Duration::from_millis(i),
208 'u' => Duration::from_micros(i),
209 'n' => Duration::from_nanos(i),
210 _ => return Err(PropertyError::parse_fail("Invalid duration")),
211 })
212}
213
214impl IsProperty for Duration {
215 fn from_property(p: Property<'_>) -> Res<Self> {
216 match p {
217 Property::O(du) => parse_duration_from_str(&du),
218 Property::S(du) => parse_duration_from_str(du),
219 Property::I(seconds) => Ok(Duration::from_secs(seconds as u64)),
220 Property::F(sec) => Ok(Duration::new(0, 0).mul_f64(sec)),
221 Property::B(_) => Err(PropertyError::parse_fail("bool cannot convert to duration")),
222 }
223 }
224}
225
226#[derive(Debug)]
228pub(crate) enum SubKey<'a> {
229 S(&'a str),
231 I(usize),
233}
234
235impl SubKey<'_> {
236 pub(crate) fn is_empty(&self) -> bool {
237 if let SubKey::S(v) = self {
238 return v.is_empty();
239 }
240 false
241 }
242}
243
244lazy_static::lazy_static! {
245 static ref P: &'static [char] = &['.', '[', ']'];
246}
247#[derive(Debug)]
249pub struct Key<'a> {
250 buf: String,
251 key: Vec<SubKey<'a>>,
252}
253
254impl<'a> Key<'a> {
255 #[inline]
256 pub(crate) fn new() -> Self {
257 Self {
258 buf: String::new(),
259 key: vec![],
260 }
261 }
262
263 pub(crate) fn from_str(key: &'a str) -> Self {
264 let mut k = Self::new();
265 for n in key.split(&P[..]) {
266 if let Some(c) = n.chars().next() {
267 if c.is_ascii_digit() {
268 if let Ok(v) = n.parse() {
269 k.push(SubKey::I(v));
270 continue;
271 }
272 }
273 k.push(SubKey::S(n));
274 }
275 }
276 k
277 }
278
279 #[allow(dead_code)]
280 pub(crate) fn as_generic(&self) -> String {
281 self.as_str().replace("[0]", "[*]")
282 }
283
284 #[allow(dead_code)]
285 pub(crate) fn iter(&self) -> std::slice::Iter<'_, SubKey<'_>> {
286 self.key.iter()
287 }
288
289 pub(crate) fn as_str(&self) -> &str {
290 if self.buf.starts_with('.') {
291 return &self.buf.as_str()[1..];
292 }
293 self.buf.as_str()
294 }
295
296 pub(crate) fn push(&mut self, k: SubKey<'a>) {
297 match &k {
298 SubKey::S(v) => {
299 self.buf.push('.');
300 self.buf.push_str(*v);
301 }
302 SubKey::I(v) => {
303 self.buf.push_str(&format!("[{}]", *v));
304 }
305 }
306 self.key.push(k);
307 }
308
309 pub(crate) fn pop(&mut self) {
310 if let Some(v) = self.key.pop() {
311 match v {
312 SubKey::S(n) => self.buf.truncate(self.buf.len() - n.len() - 1),
313 SubKey::I(n) => self.buf.truncate(self.buf.len() - n.to_string().len() - 2),
314 }
315 }
316 }
317}
318
319impl<'a> From<&'a str> for SubKey<'a> {
320 fn from(mut u: &'a str) -> Self {
321 if u.starts_with('[') {
322 u = &u[1..];
323 let mut x = 0;
324 for i in u.chars() {
325 if ('0'..='9').contains(&i) {
326 x = x * 10 + (i as usize) - ('0' as usize);
327 } else {
328 break;
329 }
330 }
331 return SubKey::I(x);
332 }
333 SubKey::S(u)
334 }
335}
336
337impl From<usize> for SubKey<'_> {
338 #[inline]
339 fn from(u: usize) -> Self {
340 SubKey::I(u)
341 }
342}
343#[derive(Debug)]
345pub struct SubKeys<'a> {
346 keys: HashSet<&'a str>,
347 upper: Option<usize>,
348}
349
350impl<'a> SubKeys<'a> {
351 pub(crate) fn insert<K: Into<SubKey<'a>>>(&mut self, key: K) {
353 match key.into() {
354 SubKey::S(s) => {
355 self.keys.insert(s);
356 }
357 SubKey::I(i) => {
358 if let Some(max) = self.upper {
359 if i <= max {
360 return;
361 }
362 }
363 self.upper = Some(i);
364 }
365 }
366 }
367
368 pub(crate) fn str_keys(&self) -> Vec<&'a str> {
369 self.keys
370 .iter()
371 .filter(|a| {
372 if let Some(c) = a.chars().next() {
373 c < '0' && c > '9'
374 } else {
375 false
376 }
377 })
378 .copied()
379 .collect()
380 }
381
382 #[inline]
383 pub(crate) fn new() -> Self {
384 Self {
385 keys: HashSet::new(),
386 upper: None,
387 }
388 }
389
390 #[inline]
391 pub(crate) fn max(&self) -> Option<usize> {
392 self.upper
393 }
394}
395
396macro_rules! impl_property_from_str {
397 ($($x:ident),+) => {$(
398 impl IsProperty for $x {
399 #[inline]
400 fn from_property(p: Property<'_>) -> Res<Self> {
401 use std::str::FromStr;
402 Ok(match p {
403 Property::S(s) => <$x>::from_str(s)?,
404 Property::O(s) => <$x>::from_str(&s)?,
405 _ => return Err(PropertyError::parse_fail("can not convert")),
406 })
407 }
408
409 }
410 )+}
411}
412
413impl_property_from_str!(
414 Ipv4Addr,
415 Ipv6Addr,
416 IpAddr,
417 SocketAddrV4,
418 SocketAddrV6,
419 SocketAddr,
420 PathBuf,
421 OsString
422);
423
424#[cfg(feature = "ipnet")]
425mod ipnet {
426 use crate::*;
427 use ipnet::*;
428 impl_property_from_str!(IpNet, Ipv4Net, Ipv6Net);
429}
430
431#[cfg(test)]
432mod tests {
433 use crate::*;
434
435 #[test]
436 fn property_test() {
437 let env = Salak::builder()
438 .set("a", "0")
439 .set("b", "${b}")
440 .set("c", "${a}")
441 .set("d", "${z}")
442 .set("e", "${z:}")
443 .set("f", "${z:${a}}")
444 .set("g", "a")
445 .set("h", "${${g}}")
446 .set("i", "\\$\\{a\\}")
447 .set("j", "${${g}:a}")
448 .set("k", "${a} ${a}")
449 .set("l", "${c}")
450 .set("m", "${no_found:${no_found_2:hello}}")
451 .build()
452 .unwrap();
453
454 fn validate<T: std::fmt::Debug + FromEnvironment>(env: &Salak, key: &str, val: &str) {
455 println!("{} key: {}", std::any::type_name::<T>(), key);
456 assert_eq!(val, &format!("{:?}", env.require::<T>(key)));
457 }
458
459 validate::<String>(&env, "a", "Ok(\"0\")");
460 validate::<String>(&env, "b", "Err(RecursiveFail(\"b\"))");
461 validate::<String>(&env, "c", "Ok(\"0\")");
462 validate::<String>(&env, "d", "Err(ResolveNotFound(\"z\"))");
463 validate::<String>(&env, "e", "Ok(\"\")");
464 validate::<String>(&env, "f", "Ok(\"0\")");
465 validate::<String>(&env, "g", "Ok(\"a\")");
466 validate::<String>(&env, "h", "Ok(\"0\")");
467 validate::<String>(&env, "i", "Ok(\"${a}\")");
468 validate::<String>(&env, "j", "Ok(\"0\")");
469 validate::<String>(&env, "k", "Ok(\"0 0\")");
470 validate::<String>(&env, "l", "Ok(\"0\")");
471 validate::<String>(&env, "m", "Ok(\"hello\")");
472
473 validate::<bool>(
474 &env,
475 "a",
476 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
477 );
478 validate::<bool>(&env, "b", "Err(RecursiveFail(\"b\"))");
479 validate::<bool>(
480 &env,
481 "c",
482 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
483 );
484 validate::<bool>(&env, "d", "Err(ResolveNotFound(\"z\"))");
485 validate::<bool>(&env, "e", "Err(NotFound(\"e\"))");
486 validate::<bool>(
487 &env,
488 "f",
489 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
490 );
491 validate::<bool>(
492 &env,
493 "g",
494 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
495 );
496 validate::<bool>(
497 &env,
498 "h",
499 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
500 );
501 validate::<bool>(
502 &env,
503 "i",
504 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
505 );
506 validate::<bool>(
507 &env,
508 "j",
509 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
510 );
511 validate::<bool>(
512 &env,
513 "k",
514 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
515 );
516 validate::<bool>(
517 &env,
518 "l",
519 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
520 );
521 validate::<bool>(
522 &env,
523 "m",
524 "Err(ParseFail(None, SalakParseError(\"invalid bool value\")))",
525 );
526
527 validate::<u8>(&env, "a", "Ok(0)");
528 validate::<u8>(&env, "b", "Err(RecursiveFail(\"b\"))");
529 validate::<u8>(&env, "c", "Ok(0)");
530 validate::<u8>(&env, "d", "Err(ResolveNotFound(\"z\"))");
531 validate::<u8>(&env, "e", "Err(NotFound(\"e\"))");
532 validate::<u8>(&env, "f", "Ok(0)");
533 validate::<u8>(
534 &env,
535 "g",
536 "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
537 );
538 validate::<u8>(&env, "h", "Ok(0)");
539 validate::<u8>(
540 &env,
541 "i",
542 "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
543 );
544 validate::<u8>(&env, "j", "Ok(0)");
545 validate::<u8>(
546 &env,
547 "k",
548 "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
549 );
550 validate::<u8>(&env, "l", "Ok(0)");
551 validate::<u8>(
552 &env,
553 "m",
554 "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
555 );
556
557 validate::<Option<u8>>(&env, "a", "Ok(Some(0))");
558 validate::<Option<u8>>(&env, "b", "Err(RecursiveFail(\"b\"))");
559 validate::<Option<u8>>(&env, "c", "Ok(Some(0))");
560 validate::<Option<u8>>(&env, "d", "Err(ResolveNotFound(\"z\"))");
561 validate::<Option<u8>>(&env, "e", "Ok(None)");
562 validate::<Option<u8>>(&env, "f", "Ok(Some(0))");
563 validate::<Option<u8>>(
564 &env,
565 "g",
566 "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
567 );
568 validate::<Option<u8>>(&env, "h", "Ok(Some(0))");
569 validate::<Option<u8>>(
570 &env,
571 "i",
572 "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
573 );
574 validate::<Option<u8>>(&env, "j", "Ok(Some(0))");
575 validate::<Option<u8>>(
576 &env,
577 "k",
578 "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
579 );
580 validate::<Option<u8>>(&env, "l", "Ok(Some(0))");
581 validate::<Option<u8>>(
582 &env,
583 "m",
584 "Err(ParseFail(None, ParseIntError { kind: InvalidDigit }))",
585 );
586 }
587
588 #[test]
589 fn bool_tests() {
590 assert_eq!(true, bool::from_property(Property::S("yes")).unwrap());
591 assert_eq!(true, bool::from_property(Property::S("true")).unwrap());
592 assert_eq!(false, bool::from_property(Property::S("no")).unwrap());
593 assert_eq!(false, bool::from_property(Property::S("false")).unwrap());
594
595 assert_eq!(true, bool::from_property(Property::S("x")).is_err());
596 assert_eq!(true, bool::from_property(Property::S("n")).is_err());
597 assert_eq!(true, bool::from_property(Property::S("f")).is_err());
598 assert_eq!(true, bool::from_property(Property::S("y")).is_err());
599 assert_eq!(true, bool::from_property(Property::S("t")).is_err());
600 assert_eq!(true, bool::from_property(Property::I(0)).is_err());
601 assert_eq!(true, bool::from_property(Property::I(1)).is_err());
602 assert_eq!(true, bool::from_property(Property::F(0.0)).is_err());
603 assert_eq!(true, bool::from_property(Property::F(1.0)).is_err());
604 }
605
606 #[quickcheck]
607 fn num_tests(i: i64) {
608 assert_eq!(
609 i,
610 i64::from_property(Property::O(format!("{}", i))).unwrap()
611 );
612 assert_eq!(i, i64::from_property(Property::I(i)).unwrap());
613 assert_eq!(true, i64::from_property(Property::B(true)).is_err());
614 }
615
616 #[quickcheck]
617 fn i64_convert_tests(i: i64) -> bool {
618 let u8: Result<u8, PropertyError> = IsProperty::from_property(Property::I(i));
619 let u16: Result<u16, PropertyError> = IsProperty::from_property(Property::I(i));
620 let u32: Result<u32, PropertyError> = IsProperty::from_property(Property::I(i));
621 let u64: Result<u64, PropertyError> = IsProperty::from_property(Property::I(i));
622 let u128: Result<u128, PropertyError> = IsProperty::from_property(Property::I(i));
623 let i8: Result<i8, PropertyError> = IsProperty::from_property(Property::I(i));
624 let i16: Result<i16, PropertyError> = IsProperty::from_property(Property::I(i));
625 let i32: Result<i32, PropertyError> = IsProperty::from_property(Property::I(i));
626 let i64: Result<i64, PropertyError> = IsProperty::from_property(Property::I(i));
627 let i128: Result<i128, PropertyError> = IsProperty::from_property(Property::I(i));
628 let f32: Result<f32, PropertyError> = IsProperty::from_property(Property::I(i));
629 let f64: Result<f64, PropertyError> = IsProperty::from_property(Property::I(i));
630 vec![
631 i >= 0 && i <= (u8::MAX as i64) && u8.is_ok() || u8.is_err(),
632 i >= 0 && i <= (u16::MAX as i64) && u16.is_ok() || u16.is_err(),
633 i >= 0 && i <= (u32::MAX as i64) && u32.is_ok() || u32.is_err(),
634 i >= 0 && u64.is_ok() || u64.is_err(),
635 i >= 0 && u128.is_ok() || u128.is_err(),
636 i >= (i8::MIN as i64) && i <= (i8::MAX as i64) && i8.is_ok() || i8.is_err(),
637 i >= (i16::MIN as i64) && i <= (i16::MAX as i64) && i16.is_ok() || i16.is_err(),
638 i >= (i32::MIN as i64) && i <= (i32::MAX as i64) && i32.is_ok() || i32.is_err(),
639 i64.is_ok(),
640 i128.is_ok(),
641 f32.is_ok() && f32.unwrap_or(0.0).is_finite(),
642 f64.is_ok() && f64.unwrap_or(0.0).is_finite(),
643 ]
644 .iter()
645 .all(|a| *a)
646 }
647
648 #[quickcheck]
649 fn f64_convert_tests(i: f64) -> bool {
650 let u8: Result<u8, PropertyError> = IsProperty::from_property(Property::F(i));
651 let u16: Result<u16, PropertyError> = IsProperty::from_property(Property::F(i));
652 let u32: Result<u32, PropertyError> = IsProperty::from_property(Property::F(i));
653 let u64: Result<u64, PropertyError> = IsProperty::from_property(Property::F(i));
654 let u128: Result<u128, PropertyError> = IsProperty::from_property(Property::F(i));
655 let i8: Result<i8, PropertyError> = IsProperty::from_property(Property::F(i));
656 let i16: Result<i16, PropertyError> = IsProperty::from_property(Property::F(i));
657 let i32: Result<i32, PropertyError> = IsProperty::from_property(Property::F(i));
658 let i64: Result<i64, PropertyError> = IsProperty::from_property(Property::F(i));
659 let i128: Result<i128, PropertyError> = IsProperty::from_property(Property::F(i));
660 let f32: Result<f32, PropertyError> = IsProperty::from_property(Property::F(i));
661 let f64: Result<f64, PropertyError> = IsProperty::from_property(Property::F(i));
662
663 vec![
664 i.is_finite() && u8.is_ok() || u8.is_err(),
665 i.is_finite() && u16.is_ok() || u16.is_err(),
666 i.is_finite() && u32.is_ok() || u32.is_err(),
667 i.is_finite() && u64.is_ok() || u64.is_err(),
668 i.is_finite() && u128.is_ok() || u128.is_err(),
669 i.is_finite() && i8.is_ok() || i8.is_err(),
670 i.is_finite() && i16.is_ok() || i16.is_err(),
671 i.is_finite() && i32.is_ok() || i32.is_err(),
672 i.is_finite() && i64.is_ok() || i64.is_err(),
673 i.is_finite() && i128.is_ok() || i128.is_err(),
674 i.is_finite() && f32.is_ok() || f32.is_err(),
675 i.is_finite() && f64.is_ok() || f64.is_err(),
676 ]
677 .iter()
678 .all(|a| *a)
679 }
680
681 #[test]
682 fn duration_test() {
683 use super::*;
684 assert_eq!(
685 Duration::new(123, 0),
686 parse_duration_from_str("123").unwrap()
687 );
688 assert_eq!(
689 Duration::new(123, 0),
690 parse_duration_from_str("123s").unwrap()
691 );
692 assert_eq!(
693 Duration::new(10 * 60, 0),
694 parse_duration_from_str("10m").unwrap()
695 );
696 assert_eq!(
697 Duration::new(123 * 3600, 0),
698 parse_duration_from_str("123h").unwrap()
699 );
700 assert_eq!(
701 Duration::new(0, 123 * 1000_000),
702 parse_duration_from_str("123ms").unwrap()
703 );
704 assert_eq!(
705 Duration::new(0, 123 * 1000),
706 parse_duration_from_str("123us").unwrap()
707 );
708 assert_eq!(
709 Duration::new(0, 123),
710 parse_duration_from_str("123ns").unwrap()
711 );
712 assert_eq!(
713 Duration::new(1, 0),
714 parse_duration_from_str("1000ms").unwrap()
715 );
716 }
717
718 #[derive(Debug)]
719 struct Config {
720 i8: i8,
721 }
722
723 impl FromEnvironment for Config {
724 fn from_env(_: Option<Property<'_>>, env: &mut SalakContext<'_>) -> Res<Self> {
725 Ok(Config {
726 i8: env.require_def("i8", None)?,
727 })
728 }
729 }
730
731 #[cfg(feature = "derive")]
732 #[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
733 impl DescFromEnvironment for Config {
734 fn key_desc(env: &mut SalakDescContext<'_>) {
735 env.add_key_desc::<i8>("i8", None, None, None);
736 }
737 }
738 #[test]
739 fn config_test() {
740 let env = Salak::builder()
741 .set("a", "0")
742 .set("b", "${b}")
743 .set("c", "${a}")
744 .set("d", "${z}")
745 .set("e", "${z:}")
746 .set("f", "${z:${a}}")
747 .set("g", "a")
748 .set("h", "${${g}}")
749 .set("i", "\\$\\{a\\}")
750 .set("j", "${${g}:a}")
751 .set("k", "${a} ${a}")
752 .set("l", "${c}")
753 .build()
754 .unwrap();
755 println!("{:?}", env.require::<Config>(""));
756 println!("{:?}", env.require::<Option<Config>>(""));
757 }
758
759 #[test]
760 fn key_test() {
761 fn assert_key(prefix: &str, target: &str) {
762 assert_eq!(Key::from_str(prefix).as_str(), target);
763 }
764
765 assert_key("salak.prop", "salak.prop");
766 assert_key(".salak.prop", "salak.prop");
767 assert_key("[]salak.prop", "salak.prop");
768 assert_key("[0]salak.prop", "[0].salak.prop");
769 assert_key("salak[0].prop", "salak[0].prop");
770 assert_key("salak.0.prop", "salak[0].prop");
771 assert_key("", "");
772 assert_key("hello", "hello");
773 assert_key(".", "");
774 assert_key("[0]", "[0]");
775 assert_key("0", "[0]");
776 }
777
778 #[test]
779 fn key_modification_test() {
780 fn assert_key<'a>(key: &mut Key<'a>, target: &'a str) {
781 let prefix = key.as_str().to_string();
782 let p = key.as_str().to_string();
783 key.push(SubKey::S(target));
784 assert_eq!(key.as_str(), &format!("{}.{}", p, target));
785 key.pop();
786 assert_eq!(prefix, key.as_str());
787 }
788
789 fn assert_keys(key: &str, targets: Vec<&str>) {
790 let mut key = Key::from_str(key);
791 for target in targets {
792 assert_key(&mut key, target);
793 }
794 }
795
796 assert_keys("redis", vec!["port", "host", "ssl", "pool"]);
797 assert_keys("hello.hey", vec!["world"]);
798 assert_keys("hello[0].hey", vec!["world"]);
799 }
800}