cor_args/
lib.rs

1use serde_json::Value;
2use std::borrow::Cow;
3use std::env;
4use std::fs::File;
5use std::io::Read;
6use std::path::PathBuf;
7
8#[cfg(feature = "clap")]
9pub use self::internal_clap::*;
10#[cfg(feature = "config")]
11pub use self::internal_config::*;
12
13/// A trait for handling requests based on a key.
14///
15/// This trait provides a mechanism for handling requests by taking a key and
16/// returning an associated value wrapped in an `Option`.
17pub trait Handler {
18    /// Handles a request based on the provided key.
19    ///
20    /// # Arguments
21    ///
22    /// * `key` - The key associated with the request.
23    ///
24    /// # Returns
25    ///
26    /// An `Option` wrapping a `String` value associated with the key.
27    /// If there's no value associated with the key, it should return `None`.
28    fn handle_request(&self, key: &str) -> Option<String>;
29}
30
31/// A default implementation of the `Handler` trait.
32///
33/// This struct contains a single `value` that will be returned for any request,
34/// regardless of the provided key.
35///
36/// # Examples
37///
38/// ```
39/// use cor_args::{DefaultHandler, Handler};
40///
41/// // Create a new DefaultHandler for a specific value
42/// let handler = DefaultHandler::new("some_value");
43///
44/// // Add a fallback handler
45/// //let handler = handler.next(some_other_handler.into());
46///
47/// // Handle a configuration request
48/// let value = handler.handle_request("some_key");
49/// ```
50pub struct DefaultHandler {
51    value: String,
52}
53
54impl DefaultHandler {
55    /// Creates a new `DefaultHandler` with the specified value.
56    ///
57    /// # Arguments
58    ///
59    /// * `value` - The value to be returned for any request.
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// use cor_args::DefaultHandler;
65    ///
66    /// let handler = DefaultHandler::new("some_value");
67    /// ```
68    #[allow(dead_code)]
69    pub fn new(value: &str) -> Self {
70        DefaultHandler {
71            value: String::from(value),
72        }
73    }
74}
75
76impl Handler for DefaultHandler {
77    /// Always returns the stored value, regardless of the key.
78    ///
79    /// This implementation ignores the provided key and always returns the
80    /// value stored in the `DefaultHandler`.
81    fn handle_request(&self, _key: &str) -> Option<String> {
82        Some(self.value.clone())
83    }
84}
85
86impl Into<Box<dyn Handler>> for DefaultHandler {
87    fn into(self) -> Box<dyn Handler> {
88        Box::new(self)
89    }
90}
91
92#[cfg(feature = "clap")]
93pub mod internal_clap {
94    use super::*;
95    use clap::ArgMatches;
96    /// A handler for managing command-line arguments.
97    ///
98    /// This struct is responsible for handling command-line arguments passed to the application.
99    /// If a value for a given key is not found in the arguments, it delegates the request to the
100    /// next handler (if provided).
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// use cor_args::{ArgHandler, Handler};
106    ///
107    /// // Create a simple `clap` command
108    /// let args = clap::Command::new("myapp")
109    ///     .arg(clap::Arg::new("example").long("example"))
110    ///     .get_matches();
111    ///
112    /// // Create a new ArgHandler for a `clap::ArgMatches`
113    /// let handler = ArgHandler::new(&args);
114    ///
115    /// // Add a fallback handler
116    /// //let handler = handler.next(some_other_handler.into());
117    ///
118    /// // Handle a configuration request matching the `clap::Arg` name
119    /// let value = handler.handle_request("example");
120    /// ```
121    pub struct ArgHandler<'a> {
122        /// Parsed command-line arguments.
123        args: &'a ArgMatches,
124        /// An optional next handler to delegate requests if this handler can't fulfill them.
125        next: Option<Box<dyn Handler>>,
126    }
127
128    impl<'a> ArgHandler<'a> {
129        /// Creates a new `ArgHandler` with the specified arguments.
130        ///
131        /// # Arguments
132        ///
133        /// * `args` - The parsed command-line arguments.
134        ///
135        /// # Examples
136        ///
137        /// ```
138        /// use cor_args::ArgHandler;
139        ///
140        /// let args = clap::Command::new("myapp")
141        ///     .arg(clap::Arg::new("config").long("some-option"))
142        ///     .get_matches();
143        ///
144        /// let handler = ArgHandler::new(&args);
145        /// ```
146        #[allow(dead_code)]
147        pub fn new(args: &'a ArgMatches) -> Self {
148            ArgHandler { args, next: None }
149        }
150
151        #[allow(dead_code)]
152        pub fn next(mut self, handler: Box<dyn Handler>) -> Self {
153            self.next = Some(handler);
154            self
155        }
156    }
157
158    impl<'a> Handler for ArgHandler<'a> {
159        /// Retrieves a value for the specified key from the command-line arguments.
160        ///
161        /// If the key is not found in the arguments, and if a next handler is provided, it delegates the request
162        /// to the next handler. If there's no next handler or if the key is not found in both the arguments and
163        /// the next handler, it returns `None`.
164        ///
165        /// # Arguments
166        ///
167        /// * `key` - The key for which the value needs to be retrieved.
168        ///
169        /// # Returns
170        ///
171        /// An `Option` containing the value associated with the key, or `None` if the key is not found.
172        fn handle_request(&self, key: &str) -> Option<String> {
173            if let Ok(value) = self.args.try_get_one::<String>(key) {
174                if let Some(value) = value {
175                    return Some(value.clone());
176                }
177            }
178            if let Some(next_handler) = &self.next {
179                return next_handler.handle_request(key);
180            }
181            None
182        }
183    }
184
185    impl<'a> Into<Box<dyn Handler + 'a>> for ArgHandler<'a> {
186        fn into(self) -> Box<dyn Handler + 'a> {
187            Box::new(self)
188        }
189    }
190}
191
192/// A handler for retrieving values from environment variables.
193///
194/// This struct is responsible for handling requests by checking for the existence of
195/// an environment variable corresponding to the provided key. If the environment variable
196/// is not found, it delegates the request to the next handler (if provided).
197///
198/// # Examples
199///
200/// ```
201/// use cor_args::{EnvHandler, Handler};
202///
203/// // Create a new EnvHandler specifying a prefix for environment variables
204/// let handler = EnvHandler::new().prefix("MYAPP_");
205///
206/// // Add a fallback handler
207/// //let handler = handler.next(some_other_handler.into());
208///
209/// // Handle a configuration request matching `MYAPP_some_key`
210/// let value = handler.handle_request("some_key");
211/// ```
212pub struct EnvHandler<'a> {
213    /// A prefix to prepend to the key passed to `handle_request()`.
214    prefix: Option<Cow<'a, str>>,
215    /// An optional next handler to delegate requests if this handler can't fulfill them.
216    next: Option<Box<dyn Handler>>,
217}
218
219impl<'a> EnvHandler<'a> {
220    /// Creates a new `EnvHandler`.
221    ///
222    /// # Arguments
223    ///
224    /// * `prefix` - An optional prefix to which requests will prepend when `handle_request()` is executed.` If `None`, an empty string is assigned.
225    ///
226    /// # Examples
227    ///
228    /// ```
229    /// use cor_args::EnvHandler;
230    ///
231    /// let handler = EnvHandler::new();
232    /// ```
233    #[allow(dead_code)]
234    pub fn new() -> Self {
235        EnvHandler {
236            prefix: None,
237            next: None,
238        }
239    }
240
241    #[allow(dead_code)]
242    pub fn next(mut self, handler: Box<dyn Handler>) -> Self {
243        self.next = Some(handler);
244        self
245    }
246
247    #[allow(dead_code)]
248    pub fn prefix<S>(mut self, prefix: S) -> Self
249    where
250        S: Into<Cow<'a, str>>,
251    {
252        self.prefix = Some(prefix.into());
253        self
254    }
255}
256
257impl<'a> Handler for EnvHandler<'a> {
258    /// Retrieves a value for the specified key from the environment variables.
259    ///
260    /// If the environment variable corresponding to the key is not found, and if a next handler is provided,
261    /// it delegates the request to the next handler. If there's no next handler or if the key is not found
262    /// both in the environment and the next handler, it returns `None`.
263    ///
264    /// # Arguments
265    ///
266    /// * `key` - The key for which the value needs to be retrieved from environment variables.
267    ///
268    /// # Returns
269    ///
270    /// An `Option` containing the value associated with the key, or `None` if the key is not found.
271    fn handle_request(&self, key: &str) -> Option<String> {
272        if let Some(prefix) = &self.prefix {
273            let key = format!("{prefix}{key}");
274            if let Ok(value) = env::var(key) {
275                return Some(value);
276            }
277        } else {
278            if let Ok(value) = env::var(key) {
279                return Some(value);
280            }
281        }
282        if let Some(next_handler) = &self.next {
283            return next_handler.handle_request(key);
284        }
285        None
286    }
287}
288
289impl<'a> Into<Box<dyn Handler + 'a>> for EnvHandler<'a> {
290    fn into(self) -> Box<dyn Handler + 'a> {
291        Box::new(self)
292    }
293}
294
295/// A handler for retrieving values from a file.
296///
297/// This struct is responsible for handling requests by checking for values within a specified file.
298///
299/// # Examples
300///
301/// ```
302/// use cor_args::{FileHandler, Handler};
303///
304/// // Create a new FileHandler specifying a path to a file.
305/// let handler = FileHandler::new("/path/to/file");
306///
307/// // Add a fallback handler
308/// //let handler = handler.next(some_other_handler.into());
309///
310/// // Handle a configuration request returning contents of `/path/to/file`
311/// let value = handler.handle_request("");
312/// ```
313pub struct FileHandler {
314    /// Path to the file from which values are to be retrieved.
315    file_path: PathBuf,
316    /// An optional next handler to delegate requests if this handler can't fulfill them.
317    next: Option<Box<dyn Handler>>,
318}
319
320impl FileHandler {
321    /// Creates a new `FileHandler` with the specified file path.
322    ///
323    /// # Arguments
324    ///
325    /// * `file_path` - The path to the file from which values are to be retrieved.
326    ///
327    /// # Examples
328    ///
329    /// ```
330    /// use cor_args::FileHandler;
331    ///
332    /// let handler = FileHandler::new("/path/to/file");
333    /// ```
334    #[allow(dead_code)]
335    pub fn new<P>(file_path: P) -> Self
336    where
337        P: Into<PathBuf>,
338    {
339        FileHandler {
340            file_path: file_path.into(),
341            next: None,
342        }
343    }
344
345    #[allow(dead_code)]
346    pub fn next(mut self, handler: Box<dyn Handler>) -> Self {
347        self.next = Some(handler);
348        self
349    }
350}
351
352impl Handler for FileHandler {
353    /// Retrieves content from the specified file.
354    ///
355    /// This implementation attempts to read content from the file specified by `file_path`.
356    /// If reading fails, and if a next handler is provided, it delegates the request
357    /// to the next handler. If there's no next handler or if the file reading fails,
358    /// it returns `None`.
359    ///
360    /// # Arguments
361    ///
362    /// * `key` - The key for which the value needs to be retrieved. (Note: The `key` is currently not used directly, just passed on to the next handler.)
363    ///
364    /// # Returns
365    ///
366    /// An `Option` containing the contents of the file, or `None` if the key is not found.
367    fn handle_request(&self, key: &str) -> Option<String> {
368        if let Ok(mut file) = File::open(&self.file_path) {
369            let mut content = String::new();
370            if let Ok(_byte_count) = file.read_to_string(&mut content) {
371                return Some(content);
372            }
373        }
374        if let Some(next_handler) = &self.next {
375            return next_handler.handle_request(key);
376        }
377        None
378    }
379}
380
381impl Into<Box<dyn Handler>> for FileHandler {
382    fn into(self) -> Box<dyn Handler> {
383        Box::new(self)
384    }
385}
386
387/// A handler for retrieving values from a specified JSON file.
388///
389/// This struct is responsible for handling requests by reading content from the file
390/// specified in the underlying `FileHandler`, and then searching for a specific key
391/// within the parsed JSON structure. If the key is not found in the JSON structure,
392/// it delegates the request to the next handler (if provided).
393///
394/// ```
395/// use cor_args::{JSONFileHandler, Handler};
396///
397/// // Create a new JSONFileHandler specifying a path to a file.
398/// let handler = JSONFileHandler::new("file.json");
399///
400/// // Add a fallback handler
401/// //let handler = handler.next(some_other_handler.into());
402///
403/// // Handle a configuration request matching a `"some_key"` within `file.json`
404/// let value = handler.handle_request("some_key");
405/// ```
406pub struct JSONFileHandler {
407    /// Underlying file handler used to read content from the specified file.
408    file_handler: FileHandler,
409}
410
411impl JSONFileHandler {
412    /// Creates a new `JSONFileHandler` with the specified file path.
413    ///
414    /// # Arguments
415    ///
416    /// * `file_path` - The path to the JSON file from which values are to be retrieved.
417    ///
418    /// # Examples
419    ///
420    /// ```
421    /// use cor_args::FileHandler;
422    ///
423    /// let handler = FileHandler::new("file.json");
424    /// ```
425    #[allow(dead_code)]
426    pub fn new<P>(file_path: P) -> Self
427    where
428        P: Into<PathBuf>,
429    {
430        JSONFileHandler {
431            file_handler: FileHandler::new(file_path),
432        }
433    }
434
435    #[allow(dead_code)]
436    pub fn next(mut self, handler: Box<dyn Handler>) -> Self {
437        self.file_handler.next = Some(handler);
438        self
439    }
440
441    /// Recursively searches for a key within the parsed JSON structure.
442    ///
443    /// # Arguments
444    ///
445    /// * `json_value` - The current JSON value being inspected.
446    /// * `key` - The key for which the value needs to be retrieved.
447    ///
448    /// # Returns
449    ///
450    /// If found, returns an `Option` wrapping a `String` value associated with the key.
451    /// Otherwise, returns `None`.
452    pub fn find_key_recursive(json_value: &Value, key: &str) -> Option<String> {
453        match json_value {
454            Value::Object(map) => {
455                if let Some(value) = map.get(key) {
456                    match value {
457                        serde_json::Value::String(value) => {
458                            return Some(value.as_str().to_string())
459                        }
460                        _ => return Some(value.to_string()),
461                    }
462                }
463                for (_, value) in map.iter() {
464                    if let Some(found) = Self::find_key_recursive(value, key) {
465                        return Some(found);
466                    }
467                }
468            }
469            Value::Array(arr) => {
470                for value in arr.iter() {
471                    if let Some(found) = Self::find_key_recursive(value, key) {
472                        return Some(found);
473                    }
474                }
475            }
476            _ => {}
477        }
478        None
479    }
480}
481
482impl Handler for JSONFileHandler {
483    /// Retrieves a value for the specified key from the JSON file.
484    ///
485    /// This implementation attempts to read content from the file specified in the underlying `FileHandler`,
486    /// parses the content as JSON, and then searches for the specified key within the parsed JSON structure.
487    /// If the key is not found in the JSON structure, and if a next handler is provided, it delegates the request
488    /// to the next handler. If there's no next handler, or if the key is not found in both the JSON structure
489    /// and the next handler, it returns `None`.
490    ///
491    /// # Arguments
492    ///
493    /// * `key` - The key for which the value needs to be retrieved from the JSON file.
494    ///
495    /// # Returns
496    ///
497    /// An `Option` containing the value associated with the key, or `None` if the key is not found.
498    fn handle_request(&self, key: &str) -> Option<String> {
499        if let Some(file_data) = self.file_handler.handle_request(key) {
500            if let Ok(parsed_json) = serde_json::from_str::<Value>(&file_data) {
501                if let Some(value) = Self::find_key_recursive(&parsed_json, key) {
502                    return Some(value);
503                }
504            } else {
505                if let Some(next_handler) = &self.file_handler.next {
506                    return next_handler.handle_request(key);
507                }
508            }
509        }
510        None
511    }
512}
513
514impl Into<Box<dyn Handler>> for JSONFileHandler {
515    fn into(self) -> Box<dyn Handler> {
516        Box::new(self)
517    }
518}
519
520#[cfg(feature = "config")]
521pub mod internal_config {
522    use super::*;
523    use config::Config;
524    /// A configuration file handler for reading key-value pairs from a file.
525    ///
526    /// The `ConfigHandler` is used to read configuration data from a file and provide it
527    /// as key-value pairs. It supports chaining multiple handlers for fallback behavior.
528    ///
529    /// # Examples
530    ///
531    /// ```
532    /// use cor_args::{ConfigHandler, Handler};
533    ///
534    /// // Example YAML file
535    /// // ---
536    /// // test_obj:
537    /// //     some_key: "test_val"
538    ///
539    /// let config = config::Config::builder().build().unwrap();
540    /// //    .add_source(config::File::new("/path/to/file",
541    /// //        config::FileFormat::Yaml,
542    /// //    ))
543    /// //    .build()
544    /// //    .unwrap();
545    ///
546    /// // Create a new ConfigHandler for a specific config::Config instance
547    /// let handler = ConfigHandler::new(Box::new(config));
548    ///
549    /// // Add a fallback handler
550    /// //let handler = handler.next(some_other_handler.into());
551    ///
552    /// // Handle a configuration request
553    /// let value = handler.handle_request("some_key");
554    /// ```
555    pub struct ConfigHandler {
556        /// The Config instance ultimately being queried.
557        config: Box<config::Config>,
558        next: Option<Box<dyn Handler>>,
559    }
560
561    impl ConfigHandler {
562        /// Create a new `ConfigHandler` for the specified file path.
563        ///
564        /// # Parameters
565        ///
566        /// - `config`: A `config::Config` reference.
567        ///
568        /// # Returns
569        ///
570        /// A new `ConfigHandler` instance.
571        ///
572        /// # Examples
573        ///
574        /// ```
575        /// use cor_args::ConfigHandler;
576        ///
577        /// let handler = ConfigHandler::new(Box::new(config::Config::builder().build().unwrap()));
578        /// ```
579        #[allow(dead_code)]
580        pub fn new(config: Box<Config>) -> Self {
581            ConfigHandler { config, next: None }
582        }
583
584        #[allow(dead_code)]
585        pub fn next(mut self, handler: Box<dyn Handler>) -> Self {
586            self.next = Some(handler);
587            self
588        }
589
590        /// Recursively searches for a key within the parsed Config structure.
591        ///
592        /// # Arguments
593        ///
594        /// * `config_value` - The current Config value being inspected.
595        /// * `key` - The key for which the value needs to be retrieved.
596        ///
597        /// # Returns
598        ///
599        /// If found, returns an `Option` wrapping a `String` value associated with the key.
600        /// Otherwise, returns `None`.
601        pub fn find_key_recursive(config_value: &config::Value, key: &str) -> Option<String> {
602            match &config_value.kind {
603                config::ValueKind::Table(map) => {
604                    if let Some(value) = map.get(key) {
605                        match &value.kind {
606                            config::ValueKind::String(value) => {
607                                return Some(value.as_str().to_string())
608                            }
609                            _ => return Some(value.to_string()),
610                        }
611                    }
612                    for (_, value) in map.iter() {
613                        if let Some(found) = Self::find_key_recursive(value, key) {
614                            return Some(found);
615                        }
616                    }
617                }
618                config::ValueKind::Array(arr) => {
619                    for value in arr.iter() {
620                        if let Some(found) = Self::find_key_recursive(value, key) {
621                            return Some(found);
622                        }
623                    }
624                }
625                _ => {}
626            }
627            None
628        }
629    }
630
631    impl Handler for ConfigHandler {
632        /// Handle a configuration request and return the value associated with the provided key.
633        ///
634        /// This method attempts to read the configuration file and retrieve the value associated
635        /// with the given key. If the key is not found, it may delegate the request to a fallback
636        /// handler if one is defined.
637        ///
638        /// # Parameters
639        ///
640        /// - `key`: A string representing the configuration key to retrieve.
641        ///
642        /// # Returns
643        ///
644        /// An `Option` containing the value associated with the key, or `None` if the key is not found.
645        fn handle_request(&self, key: &str) -> Option<String> {
646            if let Ok(parsed_config) = self.config.clone().try_deserialize::<config::Value>() {
647                if let Some(value) = Self::find_key_recursive(&parsed_config, key) {
648                    return Some(value);
649                }
650            }
651            if let Some(next_handler) = &self.next {
652                return next_handler.handle_request(key);
653            }
654            None
655        }
656    }
657
658    impl Into<Box<dyn Handler>> for ConfigHandler {
659        fn into(self) -> Box<dyn Handler> {
660            Box::new(self)
661        }
662    }
663
664    impl From<Result<config::Config, config::ConfigError>> for ConfigHandler {
665        fn from(value: Result<config::Config, config::ConfigError>) -> Self {
666            if let Ok(config) = value {
667                ConfigHandler::new(Box::new(config))
668            } else {
669                panic!("Failed to convert into a Config")
670            }
671        }
672    }
673
674    impl From<config::Config> for ConfigHandler {
675        fn from(value: config::Config) -> Self {
676            ConfigHandler::new(Box::new(value))
677        }
678    }
679}
680
681#[cfg(test)]
682mod tests {
683    use std::io::Write;
684    use tempfile::NamedTempFile;
685
686    use super::*;
687
688    #[cfg(feature = "clap")]
689    #[test]
690    fn test_clap_features_chain_of_responsibility() {
691        env::set_var("TEST_KEY", "EnvHandler");
692        let args = clap::Command::new("test_app")
693            .arg(clap::Arg::new("example").long("example"))
694            .get_matches_from(vec!["test_app", "--example", "ArgHandler"]);
695        let temp_dir = tempfile::tempdir().unwrap();
696        // Don't create the temporary file so the chain keeps going to the end for this test.
697        let raw_file = temp_dir.path().join("should-not-exist.txt");
698        let mut json_file = NamedTempFile::new().unwrap();
699        writeln!(json_file, r#"{{"test_key": "JSONFileHandler"}}"#).unwrap();
700
701        let handler = ArgHandler::new(&args).next(Box::new(
702            EnvHandler::new().next(Box::new(
703                FileHandler::new(raw_file.as_path().to_str().unwrap())
704                    .next(Box::new(JSONFileHandler::new(
705                        json_file.path().to_str().unwrap(),
706                    )))
707                    .next(Box::new(DefaultHandler::new("DefaultHandler"))),
708            )),
709        ));
710        let actual = handler.handle_request("");
711        assert_eq!(actual, Some("DefaultHandler".to_string()));
712    }
713
714    #[test]
715    fn test_default_features_chain_of_responsibility() {
716        env::set_var("TEST_KEY", "EnvHandler");
717        let temp_dir = tempfile::tempdir().unwrap();
718        // Don't create the temporary file so the chain keeps going to the end for this test.
719        let raw_file = temp_dir.path().join("should-not-exist.txt");
720        let mut json_file = NamedTempFile::new().unwrap();
721        writeln!(json_file, r#"{{"test_key": "JSONFileHandler"}}"#).unwrap();
722
723        let handler = EnvHandler::new().next(Box::new(
724            FileHandler::new(raw_file.as_path().to_str().unwrap())
725                .next(Box::new(JSONFileHandler::new(
726                    json_file.path().to_str().unwrap(),
727                )))
728                .next(Box::new(DefaultHandler::new("DefaultHandler"))),
729        ));
730        let actual = handler.handle_request("");
731        assert_eq!(actual, Some("DefaultHandler".to_string()));
732    }
733
734    #[cfg(feature = "config")]
735    #[test]
736    fn test_config_features_chain_of_responsibility() {
737        env::set_var("UNUSED", "EnvHandler");
738        let mut temp_file = tempfile::Builder::new().suffix(".yml").tempfile().unwrap();
739        let expected = r#"
740        ---
741        test_obj:
742            test_key: "test_val"
743        "#;
744        writeln!(temp_file, "{}", unindent::unindent(expected)).unwrap();
745
746        let config = config::Config::builder()
747            .add_source(config::File::new(
748                temp_file.path().to_str().unwrap(),
749                config::FileFormat::Yaml,
750            ))
751            .build()
752            .unwrap();
753
754        let handler = EnvHandler::new().next(Box::new(ConfigHandler::new(Box::new(config))));
755        // let handler = EnvHandler::new().next(Box::<ConfigHandler>::new(config.into()));
756        let actual = handler.handle_request("test_key");
757        assert_eq!(actual, Some("test_val".to_string()));
758    }
759
760    mod default_handler {
761        use super::*;
762
763        #[test]
764        fn test_retrieves_set_value() {
765            let handler = DefaultHandler::new("TEST_VAL");
766            let actual = handler.handle_request("");
767            assert_eq!(actual, Some("TEST_VAL".to_string()));
768        }
769    }
770
771    mod env_handler {
772        use super::*;
773
774        #[test]
775        fn test_retrieves_set_value_without_prefix() {
776            env::set_var("TEST_KEY", "test_value");
777            let handler = EnvHandler::new();
778            let actual = handler.handle_request("TEST_KEY");
779            assert_eq!(actual, Some("test_value".to_string()));
780        }
781
782        #[test]
783        fn test_retrieves_set_value_with_prefix() {
784            env::set_var("TEST_KEY", "test_value");
785            let handler = EnvHandler::new().prefix("TEST_");
786            let actual = handler.handle_request("KEY");
787            assert_eq!(actual, Some("test_value".to_string()));
788        }
789
790        #[test]
791        fn test_returns_none_for_unset_value() {
792            env::remove_var("UNSET_KEY"); // Ensure the variable is not set
793            let handler = EnvHandler::new();
794            let actual = handler.handle_request("UNSET_KEY");
795            assert_eq!(actual, None);
796        }
797
798        #[test]
799        fn test_next_handler_called() {
800            env::remove_var("UNSET_KEY"); // Ensure the variable is not set
801            let next_handler = Box::new(DefaultHandler::new("DEFAULT_VALUE"));
802            let handler = EnvHandler::new().next(next_handler);
803            let actual = handler.handle_request("UNSET_KEY");
804            assert_eq!(actual, Some("DEFAULT_VALUE".to_string()));
805        }
806    }
807
808    #[cfg(feature = "clap")]
809    mod arg_handler {
810        use clap::Arg;
811
812        use super::*;
813
814        #[test]
815        fn test_retrieves_set_value() {
816            let args = clap::Command::new("test_app")
817                .arg(Arg::new("example").long("example"))
818                .get_matches_from(vec!["test_app", "--example", "test_value"]);
819
820            let handler = ArgHandler::new(&args);
821            let result = handler.handle_request("example");
822            assert_eq!(result, Some("test_value".to_string()));
823        }
824
825        #[test]
826        fn test_returns_none_for_unset_value() {
827            let args = clap::Command::new("test_app")
828                .arg(Arg::new("example").long("example"))
829                .get_matches_from(vec!["test_app"]);
830
831            let handler = ArgHandler::new(&args);
832            let result = handler.handle_request("example");
833            assert_eq!(result, None);
834        }
835
836        #[test]
837        fn test_next_handler_called() {
838            let args = clap::Command::new("test_app")
839                .arg(Arg::new("example").long("example"))
840                .get_matches_from(vec!["test_app"]);
841            let next_handler = Box::new(DefaultHandler::new("DEFAULT_VALUE"));
842            let handler = ArgHandler::new(&args).next(next_handler);
843            let actual = handler.handle_request("example");
844            assert_eq!(actual, Some("DEFAULT_VALUE".to_string()));
845        }
846    }
847
848    mod file_handler {
849        use std::io::Write;
850        use tempfile::NamedTempFile;
851
852        use super::*;
853
854        #[test]
855        fn test_retrieves_set_value() {
856            let mut temp_file = NamedTempFile::new().unwrap();
857            writeln!(temp_file, "test_content").unwrap();
858
859            let handler = FileHandler::new(temp_file.path().to_str().unwrap());
860            let result = handler.handle_request(""); // key is not used in this handler
861            assert_eq!(result, Some("test_content\n".to_string()));
862        }
863
864        #[test]
865        fn test_returns_none_for_nonexistent_file() {
866            let handler = FileHandler::new("");
867            let result = handler.handle_request("example");
868            assert_eq!(result, None);
869        }
870
871        #[test]
872        fn test_next_handler_called() {
873            let next_handler = Box::new(DefaultHandler::new("DEFAULT_VALUE"));
874            let handler = FileHandler::new("").next(next_handler);
875            let actual = handler.handle_request("example");
876            assert_eq!(actual, Some("DEFAULT_VALUE".to_string()));
877        }
878    }
879
880    mod json_file_handler {
881        use std::io::Write;
882        use tempfile::NamedTempFile;
883
884        use super::*;
885
886        #[test]
887        fn test_retrieves_set_value_number() {
888            let mut temp_file = NamedTempFile::new().unwrap();
889            writeln!(temp_file, r#"{{"test_key": 123}}"#).unwrap();
890
891            let handler = JSONFileHandler::new(temp_file.path().to_str().unwrap());
892            let actual = handler.handle_request("test_key"); // key is not used in this handler
893            assert_eq!(actual, Some("123".to_string()));
894        }
895
896        #[test]
897        fn test_retrieves_set_value_string() {
898            let mut temp_file = NamedTempFile::new().unwrap();
899            writeln!(temp_file, r#"{{"test_key": "example"}}"#).unwrap();
900
901            let handler = JSONFileHandler::new(temp_file.path().to_str().unwrap());
902            let actual = handler.handle_request("test_key"); // key is not used in this handler
903            assert_eq!(actual, Some("example".to_string()));
904        }
905
906        #[test]
907        fn test_retrieves_set_value_nested_object() {
908            let mut temp_file = NamedTempFile::new().unwrap();
909            writeln!(temp_file, r#"{{"test_obj": {{"test_key": "example"}} }}"#).unwrap();
910
911            let handler = JSONFileHandler::new(temp_file.path().to_str().unwrap());
912            let actual = handler.handle_request("test_key"); // key is not used in this handler
913            assert_eq!(actual, Some("example".to_string()));
914        }
915
916        #[test]
917        fn test_retrieves_set_value_in_array() {
918            let mut temp_file = NamedTempFile::new().unwrap();
919            writeln!(temp_file, r#"[{{"test_key": "example"}}]"#).unwrap();
920
921            let handler = JSONFileHandler::new(temp_file.path().to_str().unwrap());
922            let actual = handler.handle_request("test_key"); // key is not used in this handler
923            assert_eq!(actual, Some("example".to_string()));
924        }
925
926        #[test]
927        fn test_returns_none_for_nonexistent_file() {
928            let handler = JSONFileHandler::new("");
929            let result = handler.handle_request("example");
930            assert_eq!(result, None);
931        }
932
933        #[test]
934        fn test_next_handler_called() {
935            let next_handler = Box::new(DefaultHandler::new("DEFAULT_VALUE"));
936            let handler = JSONFileHandler::new("").next(next_handler);
937            let actual = handler.handle_request("example");
938            assert_eq!(actual, Some("DEFAULT_VALUE".to_string()));
939        }
940    }
941
942    #[cfg(feature = "config")]
943    mod config_handler {
944        use config::Config;
945        use std::io::Write;
946        use tempfile::Builder;
947        use unindent::unindent;
948
949        use super::*;
950
951        #[test]
952        fn test_retrieves_set_value_number_as_yaml() {
953            let mut temp_file = Builder::new().suffix(".yaml").tempfile().unwrap();
954            let expected = r#"
955            ---
956            test_key: 123
957            "#;
958            writeln!(temp_file, "{}", unindent(expected)).unwrap();
959            let config = config::Config::builder()
960                .add_source(config::File::new(
961                    temp_file.path().to_str().unwrap(),
962                    config::FileFormat::Yaml,
963                ))
964                .build()
965                .unwrap();
966
967            let handler = ConfigHandler::new(Box::new(config));
968            let actual = handler.handle_request("test_key");
969            assert_eq!(actual, Some("123".to_string()));
970        }
971
972        #[test]
973        fn test_retrieves_set_value_string_as_yaml() {
974            let mut temp_file = Builder::new().suffix(".yaml").tempfile().unwrap();
975            let expected = r#"
976            ---
977            test_key: "example"
978            "#;
979            writeln!(temp_file, "{}", unindent(expected)).unwrap();
980            let config = config::Config::builder()
981                .add_source(config::File::new(
982                    temp_file.path().to_str().unwrap(),
983                    config::FileFormat::Yaml,
984                ))
985                .build()
986                .unwrap();
987
988            let handler = ConfigHandler::new(Box::new(config));
989            let actual = handler.handle_request("test_key");
990            assert_eq!(actual, Some("example".to_string()));
991        }
992
993        #[test]
994        fn test_retrieves_set_value_nested_object() {
995            let mut temp_file = Builder::new().suffix(".yaml").tempfile().unwrap();
996            let expected = r#"
997            ---
998            test_obj:
999                test_key: "test_val"
1000            "#;
1001            writeln!(temp_file, "{}", unindent(expected)).unwrap();
1002            let config = config::Config::builder()
1003                .add_source(config::File::new(
1004                    temp_file.path().to_str().unwrap(),
1005                    config::FileFormat::Yaml,
1006                ))
1007                .build()
1008                .unwrap();
1009
1010            let handler = ConfigHandler::new(Box::new(config));
1011            let actual = handler.handle_request("test_key");
1012            assert_eq!(actual, Some("test_val".to_string()));
1013        }
1014
1015        #[test]
1016        fn test_next_handler_called() {
1017            let config = Config::default();
1018            let next_handler = Box::new(DefaultHandler::new("DEFAULT_VALUE"));
1019            let handler = ConfigHandler::new(Box::new(config)).next(next_handler);
1020            let actual = handler.handle_request("example");
1021            assert_eq!(actual, Some("DEFAULT_VALUE".to_string()));
1022        }
1023    }
1024}