Skip to main content

rust_env/
lib.rs

1//! # rust-env
2//!
3//! `rust-env` is a lightweight environment configuration library for Rust.
4//!
5//! It helps you manage `.env`-style configuration files with support for:
6//! - Key-value pairs
7//! - Vector (list) values using `;` separators
8//! - Runtime OS environment variables
9//! - Reading and writing environment files
10//!
11//! ---
12//!
13//! # Features
14//!
15//! - Parse `.env` files into structured data
16//! - Support for string and vector values
17//! - Access local and global environment variables
18//! - Modify and persist environment files
19//! - Simple helper API for extracting values
20//!
21//! ---
22//!
23//! # Value Model
24//!
25//! Internally, `rust-env` represents data using:
26//!
27//! - `Str(key, value)`
28//! - `Vec(key, Vec<String>)`
29//! - `Comment(String)`
30//!
31//! ---
32//!
33//! # Example
34//!
35//! ```rust
36//! use rust_env::Env;
37//!
38//! let mut env = Env::new("./.env");
39//!
40//! let port = env.get_pair("PORT");
41//! env.set(Str("PORT", "8080"));
42//! ```
43//!
44//! ---
45//!
46//! # Local vs Global
47//!
48//! - **Local**: values loaded from `.env` file
49//! - **Global**: OS environment variables
50//!
51//! When using merged lookup:
52//! ```text
53//! global > local
54//! ```
55//!
56//! ---
57//!
58//! # Notes
59//!
60//! - All values are stored as strings internally
61//! - Vector values are separated using `;`
62//! - File writes occur immediately when using `set`
63//!
64//! ---
65//!
66//! # Version History
67//!
68//! ## v0.2.0
69//! - Added `match_str` and `match_vec` helpers
70//! - Refactored codebase into multiple files
71//! - Fixed 3 known bugs
72//!
73//! ---
74//!
75//! # Experimental
76//!
77//! Experimental features such as a future markup language (RML) are **not part of this crate**.
78//!
79//! They may be developed separately in the future.
80
81use std::env::vars;
82use std::fs::{read_to_string, write};
83
84use crate::Pair::Comment;
85
86#[derive(Debug, Clone)]
87/// This enum mainly rap two type of data
88/// String and `Vec<String>`
89/// See docs of `struct Env` of this package for learn more
90pub enum Wrapper {
91    Str(String),
92    Vec(Vec<String>),
93    Empty
94}
95
96
97#[derive(Clone, Debug, PartialEq)]
98pub enum Pair {
99    Str(String, String),
100    Vec(String, Vec<String>),
101    Comment(String)
102}
103
104#[allow(dead_code)]
105struct SPair(String, String);
106
107/// The main environment container used by `rust-env`.
108///
109/// `Env` manages both:
110/// - Local environment variables loaded from a `.env` file
111/// - Global environment variables from the operating system
112///
113/// It provides functionality to:
114/// - Parse environment files into structured data
115/// - Retrieve values (local, global, or merged)
116/// - Modify environment entries
117/// - Persist changes back to a file
118///
119/// # Internal Representation
120///
121/// Environment data is stored as a vector of `Pair`:
122///
123/// - `Pair::Str(key, value)` → single value
124/// - `Pair::Vec(key, values)` → list of values (semicolon-separated in file)
125/// - `Pair::Comment(text)` → comments in the file
126///
127/// # Fields
128///
129/// - `data`: Local environment variables loaded from file
130/// - `global`: OS environment variables captured at runtime
131/// - `path`: Path to the associated `.env` file
132///
133/// # Behavior Notes
134///
135/// - Local data comes from the file at `path`
136/// - Global data is loaded via `std::env::vars()`
137/// - `set()` writes changes immediately to disk
138/// - Lookup functions may fall back between global/local depending on method
139///
140/// # Example
141///
142/// ```rust
143/// use rust_env::{Env, Str};
144///
145/// let mut env = Env::new("./.env");
146///
147/// // Get a value
148/// let port = env.get_pair("PORT");
149///
150/// // Set a value
151/// env.set(Str("PORT", "8080"));
152///
153/// // Load OS environment variables
154/// env.global_env();
155///
156/// // Debug print local env
157/// env.debug_local();
158/// ```
159///
160/// # Notes
161///
162/// - This struct is not thread-safe by default
163/// - All values are stored internally as strings
164/// - Vector values use `;` as delimiter in files
165pub struct Env {
166    pub data: Vec<Pair>,
167    pub global: Vec<Pair>,
168    pub path: String
169}
170
171/// A trait that defines the core behavior of an environment system.
172///
173/// This trait is mainly intended for advanced use cases where users want to
174/// implement a custom environment backend while keeping compatibility with
175/// the `rust-env` API.
176///
177/// Most users do NOT need to implement this trait directly.
178///
179/// # Purpose
180///
181/// `EnvFrame` allows you to:
182/// - Define custom storage for environment data
183/// - Customize parsing and serialization logic
184/// - Extend or replace default `Env` behavior
185///
186/// # Example
187///
188/// ```rust
189/// use rust_env::{EnvFrame, Pair};
190///
191/// struct CustomEnv {
192///     data: Vec<Pair>,
193///     global: Vec<Pair>,
194///     path: String,
195/// }
196///
197/// impl EnvFrame for CustomEnv {
198///     // implement custom behavior here
199/// }
200/// ```
201///
202/// # Note
203///
204/// This trait is intended for advanced extensibility only.
205/// Standard usage should rely on `Env`.
206pub trait EnvFrame {
207    fn marshal(val: Vec<Pair>) -> String;
208    fn parse(content: String) -> Vec<Pair>;
209    fn new(name: String) -> Env;
210    fn load(&mut self, e: &str);
211    fn get(&self, k: &str) -> Wrapper;
212    fn get_debug(self) -> Vec<Pair>;
213    fn set(&mut self, k: &str, v: Pair);
214    fn debug(self);
215    fn upload(path: &str, Pairs: Vec<Pair>) -> Env;
216    fn global_env(&mut self);
217    fn get_local(&self, k: &str) -> Wrapper;
218    fn get_global(&self, k: &str) -> Wrapper;
219}
220
221#[allow(non_snake_case)]
222#[allow(dead_code)]
223pub fn Str(a: &str, b: &str) -> Pair {
224    Pair::Str(a.to_string(), b.to_string())
225}
226
227pub fn get_d(d: Vec<Pair>, key: String) -> Wrapper {
228    for h in d.into_iter() {
229
230        match h {
231            Pair ::Str(k, v)
232            if k == key => {
233                return Wrapper::Str(v)
234            },
235            Pair ::Vec(k, v)
236            if k == key => {
237                return Wrapper::Vec(v)
238            },
239            _ => continue
240        }
241    }
242
243    return Wrapper::Empty;
244}
245
246/// On version 0.2.0 of rust-env DX improved
247/// You don't need to match a `Wrapper` variant
248/// Instead use `match_str` and `match vec`
249/// Here's a example
250/// # Example
251/// ```
252/// use rust_env::{Env, match_str, match_vec, Wrapper};
253///
254/// let env = Env::new("./config.env");
255///
256/// //On version 0.1.0
257/// let addr = match env.get_local("ADDR") {
258///     Wrapper::Str(e) => e,
259///     Wrapper::Vec(v) => panic!("Can't use vec instead of string"),
260///     _ => String::new()
261/// };
262/// let ip = match env.get_local("ip") {
263///     Wrapper::Vec(e) => e,
264///     Wrapper::Str(v) => panic!("Can't use string instead of vec"),
265///     _ => String::new()
266/// };
267///
268/// //Now (v0.2.0)
269///
270/// let addr: String = match_str(env.get_local("ADDR"));
271/// let ip: Vec<String> = match_vec(env.get_local("IP"));
272/// ```
273
274#[allow(dead_code)]
275pub fn match_str(w: Wrapper) -> String {
276    return match w {
277        Wrapper::Str(s) => s,
278        Wrapper::Vec(_) => {
279            panic!("Can't extract string from Wrapper::Vec");
280        }
281        _ => String::new()
282    }
283}
284
285/// On version 0.2.0 of rust-env DX improved
286/// You don't need to match a `Wrapper` variant
287/// Instead use `match_str` and `match vec`
288/// Here's a example
289/// # Example
290/// ```
291/// use rust_env::{Env, match_str, match_vec, Wrapper};
292///
293/// let env = Env::new("./config.env");
294///
295/// //On version 0.1.0
296/// let addr = match env.get_local("ADDR") {
297///     Wrapper::Str(e) => e,
298///     Wrapper::Vec(v) => panic!("Can't use vec instead of string"),
299///     _ => String::new()
300/// };
301/// let ip = match env.get_local("ip") {
302///     Wrapper::Vec(e) => e,
303///     Wrapper::Str(v) => panic!("Can't use string instead of vec"),
304///     _ => String::new()
305/// };
306///
307/// //Now (v0.2.0)
308///
309/// let addr: String = match_str(env.get_local("ADDR"));
310/// let ip: Vec<String> = match_vec(env.get_local("IP"));
311/// ```
312#[allow(dead_code)]
313pub fn match_vec(w: Wrapper) -> Vec<String> {
314    return match w {
315        Wrapper::Vec(s) => s,
316        Wrapper::Str(_) => {
317            panic!("Can't extract string from Wrapper::Str")
318        }
319        _ => Vec::new()
320    }
321}
322
323fn has(b: Vec<Pair>, h: Pair) -> bool {
324    let mut res = false;
325
326    for vals in b.into_iter() {
327        if vals == h {
328            res = true;
329            break;
330        }
331    }
332
333    res
334}
335
336impl Env {
337    #[allow(dead_code)]
338    /// Parses a raw environment string into a list of `Pair` values.
339    ///
340    /// This function converts `.env`-style text into structured data.
341    ///
342    /// # Format Rules
343    ///
344    /// Each line is expected to follow:
345    ///
346    /// ```text
347    /// KEY=VALUE
348    /// ```
349    ///
350    /// Supported formats:
351    ///
352    /// - Single value:
353    ///   ```text
354    ///   PORT=8080
355    ///   ```
356    ///
357    /// - Vector value (semicolon-separated):
358    ///   ```text
359    ///   IP=127;0;0;1
360    ///   ```
361    ///
362    /// # Behavior
363    ///
364    /// - Lines without `=` are ignored
365    /// - Empty keys are ignored
366    /// - Values containing `;` are treated as vectors
367    /// - Whitespace around values is trimmed
368    ///
369    /// # Returns
370    ///
371    /// A `Vec<Pair>` representing parsed environment entries.
372    ///
373    /// # Example
374    ///
375    /// ```rust
376    /// use rust_env::Env;
377    ///
378    /// let data = Env::parse("PORT=8080\nIP=127;0;0;1");
379    /// ```
380    ///
381    /// # Notes
382    ///
383    /// - This function does not read from files directly
384    /// - It only parses in-memory strings
385    /// - Comments are not fully supported unless explicitly handled
386    pub fn parse(content: &str) -> Vec<Pair> {
387        let s = content.to_string();
388        let lines = s.split("\n").collect::<Vec<&str>>();
389        let mut res: Vec<Pair> = Vec::new();
390
391        for _lines in lines.iter() {
392            let Pair_ = _lines.split_once("=").unwrap_or_default();
393
394            if Pair_.0 == "" {
395                if _lines.starts_with("#") {
396                    res.push(Comment(String::from(&_lines[1..])));
397                    continue; 
398                }
399                continue;
400            }
401
402            let raw_value = Pair_.1;
403            let mut value: Pair = Pair::Comment(String::new());
404
405            match raw_value.find(";") {
406                Some(_) => {
407                    let raw = raw_value.split(";").collect::<Vec<&str>>();
408                    let mut str_vec: Vec<String> = Vec::new();
409
410                    for r in raw.into_iter() {
411                        str_vec.push(r.to_string());
412                    }
413                    value = Pair::Vec(
414                        Pair_.0.to_string(),
415                        str_vec)
416                }
417                None => {
418                    value = Pair::Str(Pair_.0.to_string(),
419                                      Pair_.1.trim().to_string())
420                }
421            }
422
423            res.push(value);
424        }
425
426        return res;
427    }
428
429    /// Converts a vector of `Pair` values into a valid `.env` formatted string.
430    ///
431    /// This is the inverse of `parse()`.
432    ///
433    /// It serializes structured environment data back into text form.
434    ///
435    /// # Output Format
436    ///
437    /// - `Pair::Str(key, value)` →
438    ///   ```text
439    ///   KEY=VALUE
440    ///   ```
441    ///
442    /// - `Pair::Vec(key, values)` →
443    ///   ```text
444    ///   KEY=a;b;c
445    ///   ```
446    ///
447    /// - `Pair::Comment(text)` →
448    ///   ```text
449    ///   #text
450    ///   ```
451    ///
452    /// # Behavior
453    ///
454    /// - Values in vectors are joined using `;`
455    /// - Keys and values are written exactly as stored
456    /// - No escaping is applied for special characters
457    ///
458    /// # Returns
459    ///
460    /// A formatted `.env` string.
461    ///
462    /// # Example
463    ///
464    /// ```rust
465    /// use rust_env::{Env, Str};
466    ///
467    /// let data = vec![
468    ///     Str("PORT", "8080"),
469    /// ];
470    ///
471    /// let out = Env::marshal(data);
472    /// ```
473    ///
474    /// # Notes
475    ///
476    /// - This function does not write to files
477    /// - Formatting is minimal and not lossless (comments/order may be simplified)
478
479    pub fn marshal(val: Vec<Pair>) -> String {
480        let mut Pair = String::new();
481
482        for v in val.into_iter() {
483            Pair.push_str(match v.clone() {
484                Pair::Str(a, _) => a,
485                Pair::Vec(a, _) => a,
486                _ => String::new()
487            }.as_str());
488            Pair.push('=');
489            Pair.push_str(match v.clone() {
490                Pair::Str(_, b) => b,
491                Pair::Vec(_, v) => {
492                    let mut s = String::new();
493
494                    for v in v.clone() {
495                        s.push_str(v.as_str());
496                        s.push(';')
497                    }
498
499                    s
500                },
501                Pair::Comment(s) => format!("#{s}")
502            }.as_str());
503        }
504
505        return Pair;
506    }
507
508    /// Creates a new `Env` instance from a `.env` file.
509    ///
510    /// This function reads the file at the given path and parses its contents
511    /// into structured environment data.
512    ///
513    /// # Behavior
514    ///
515    /// - Loads local environment variables from the file
516    /// - Parses content using `.env` rules (`KEY=VALUE`)
517    /// - Stores results in `self.data`
518    /// - Initializes an empty global environment store
519    ///
520    /// # Arguments
521    ///
522    /// - `name`: Path to the `.env` file
523    ///
524    /// # Returns
525    ///
526    /// A fully initialized `Env` instance containing:
527    /// - `data`: parsed local environment variables
528    /// - `global`: empty until `global_env()` is called
529    /// - `path`: stored file path
530    ///
531    /// # Example
532    ///
533    /// ```rust
534    /// use rust_env::Env;
535    ///
536    /// let env = Env::new("./.env");
537    /// ```
538    ///
539    /// # Notes
540    ///
541    /// - Panics if the file path is invalid or unreadable
542    /// - This does NOT load OS environment variables automatically
543    /// - Use `global_env()` to populate global environment data
544    pub fn new(name: &str) -> Env {
545        let content = read_to_string(name.clone()).expect("Invalid path");
546        let local = Env::parse(content.as_str());
547
548        Self {
549            data: local,
550            path: name.to_string().clone(),
551            global: Vec::new()
552        }
553    }
554
555    #[allow(dead_code)]
556    /// It will return the entire env
557    /// local and global
558    /// # Example
559    /// ```
560    /// use rust_env::{Env, Pair};
561    ///
562    /// let env = Env::new("./.env");
563    /// let e: Vec<Pair> = env.get_debug();
564    /// ```
565
566    pub fn get_debug(self) -> Vec<Pair> { return self.data.clone() }
567    #[allow(dead_code)]
568
569    /// Creates a new `Env` instance from a `.env` file.
570    ///
571    /// This function reads the file at the given path and parses its contents
572    /// into structured environment data.
573    ///
574    /// # Behavior
575    ///
576    /// - Loads local environment variables from the file
577    /// - Parses content using `.env` rules (`KEY=VALUE`)
578    /// - Stores results in `self.data`
579    /// - Initializes an empty global environment store
580    ///
581    /// # Arguments
582    ///
583    /// - `name`: Path to the `.env` file
584    ///
585    /// # Returns
586    ///
587    /// A fully initialized `Env` instance containing:
588    /// - `data`: parsed local environment variables
589    /// - `global`: empty until `global_env()` is called
590    /// - `path`: stored file path
591    ///
592    /// # Example
593    ///
594    /// ```rust
595    /// use rust_env::Env;
596    ///
597    /// let env = Env::new("./.env");
598    /// ```
599    ///
600    /// # Notes
601    ///
602    /// - Panics if the file path is invalid or unreadable
603    /// - This does NOT load OS environment variables automatically
604    /// - Use `global_env()` to populate global environment data
605    pub fn set(&mut self, h: Pair) {
606
607        if !has(self.data.clone(), h.clone()) {
608            self.data.push(h.clone());
609        }
610        let Pair = Env::marshal(vec![h]);
611
612        write(&self.path, Pair).expect(
613            "Invalid path to write")
614    }
615
616    /// Loads environment data into the current `Env` instance.
617    ///
618    /// This function parses a raw `.env`-style string and merges it into
619    /// the existing local environment data.
620    ///
621    /// It is similar to `set`, but operates on multiple entries at once
622    /// and does not directly write to disk.
623    ///
624    /// # Behavior
625    ///
626    /// - Parses the input string using `.env` rules (`KEY=VALUE`)
627    /// - Supports vector values using `;` separators
628    /// - Ignores duplicate keys already present in `self.data`
629    /// - Only updates in-memory state (does NOT persist to file)
630    ///
631    /// # Arguments
632    ///
633    /// - `e`: A raw environment string (e.g. `"PORT=8080\nIP=127;0;0;1"`)
634    ///
635    /// # Example
636    ///
637    /// ```rust
638    /// use rust_env::Env;
639    ///
640    /// let mut env = Env::new("./.env");
641    ///
642    /// env.load("PORT=8080\nHOST=127.0.0.1");
643    /// env.debug();
644    /// ```
645    ///
646    /// # Notes
647    ///
648    /// - This does NOT modify the file on disk
649    /// - Use `set` or `upload` for persistent changes
650    /// - Duplicate keys are ignored based on existing local data
651    #[allow(dead_code)]
652    pub fn load(&mut self, e: &str) {
653        let h = Env::parse(e);
654
655        for Pair in h.into_iter() {
656            if !has(self.data.clone(), Pair.clone()) {
657                self.data.push(Pair.clone())
658            }
659        }
660    }
661
662
663    #[allow(dead_code)]
664    /// It's a function to debug the entire env
665    /// # Example
666    /// ```
667    /// use rust_env::Env;
668    /// let env: Env = Env::new("./.env");
669    ///
670    /// env.debug();
671    /// ```
672
673    pub fn debug(&self) { println!("{:?}", self.data) }
674    #[allow(dead_code)]
675    /// This function allows one to marshal env pairs and write them to a certain file
676    /// It's similar to the `new` function
677    /// But you can write external data to the
678    /// env file
679    /// # Example
680    /// ```
681    /// use rust_env::{Env, Str, Vct};
682    ///
683    /// let env = Env::upload("./env", vec![
684    ///       Str("PORT", "6778"),
685    ///       Vct("IP", vec![
686    ///          "127",
687    ///           "0",
688    ///           "0"
689    ///       ])
690    /// ]);
691    /// ```
692    pub fn upload(path: &str, Pairs: Vec<Pair>) -> Env {
693        let mut data: Vec<Pair> = Vec::new();
694        let mut out = String::new();
695
696        for Pair in Pairs.into_iter() {
697            match Pair.clone() {
698                Pair::Str(a, b) => {
699                    data.push(Pair.clone());
700
701                    out.push_str(&*a);
702                    out.push('=');
703                    out.push_str(&*b);
704                    out.push_str("\n");
705                },
706                Pair::Vec(a, vector) => {
707                    data.push(Pair::Vec(a.clone(), vector.clone()));
708
709                    let mut raw_literal = a;
710                    raw_literal.push('=');
711
712                    for ve in vector.clone().into_iter() {
713                        raw_literal.push_str(&*ve);
714                        raw_literal.push_str(";")
715                    }
716
717                    out.push_str(&*raw_literal);
718                }
719                _ => {}
720            }
721        }
722        write(path, out).expect(
723            "Invalid path to write");
724
725        Self {
726            data,
727            global: Vec::new(),
728            path: path.to_string()
729        }
730    }
731
732    ///You can upload the global env data on the environment
733    ///# Example
734    ///```
735    /// use rust_env::Env;
736    ///
737    /// let mut env: Env = Env::upload("./.env", vec![
738    ///     //put your local config
739    /// ]);
740    ///
741    /// env.debug();
742    /// env.global_env();
743    /// env.debug();
744    #[allow(dead_code)]
745    pub fn global_env(&mut self) {
746        for (k, v) in vars() {
747            self.global.push(Pair::Str(k, v));
748        }
749    }
750
751    #[allow(dead_code)]
752    /// get_local is similar to `get_Pair`
753    /// But, You can just gt the local config
754    /// Not the global
755    /// # Example
756    /// ```
757    /// use rust_env::Wrapper;
758    ///
759    /// let port = match get_local("PORT") {
760    ///     Wrapper::Str(v) => v,
761    ///     e => e
762    /// };
763    pub fn get_local(&self, k: &str) -> Wrapper {
764        get_d(self.data[..].to_vec(), k.to_string())
765    }
766
767    #[allow(dead_code)]
768    /// Retrieves a value from the global (OS-level) environment variables.
769    ///
770    /// This function searches only in the runtime environment provided by
771    /// `std::env::vars()`, which is captured when `global_env()` is called.
772    ///
773    /// # Behavior
774    ///
775    /// - Searches only global environment variables
776    /// - Does NOT fall back to local `.env` data
777    /// - Returns the first matching key if found
778    ///
779    /// # Returns
780    ///
781    /// A `Wrapper` enum:
782    /// - `Wrapper::Str(String)` for single values
783    /// - `Wrapper::Vec(Vec<String>)` (rare, depending on parsing rules)
784    /// - `Wrapper::Empty` if the key does not exist
785    ///
786    /// # Arguments
787    ///
788    /// - `k`: The environment variable key to search for
789    ///
790    /// # Example
791    ///
792    /// ```rust
793    /// use rust_env::{Env, Wrapper};
794    ///
795    /// let mut env = Env::new("./.env");
796    /// env.global_env(); // load OS environment variables
797    ///
798    /// match env.get_global("PATH") {
799    ///     Wrapper::Str(v) => println!("PATH = {}", v),
800    ///     Wrapper::Empty => println!("Not found"),
801    ///     _ => println!("Unexpected format"),
802    /// }
803    /// ```
804    ///
805    /// # Notes
806    ///
807    /// - This only works after calling `global_env()`
808    /// - Values come from the OS environment at runtime
809    /// - This does not read from `.env` files
810    /// - Lookup is linear (O(n))
811    pub fn get_global(&self, k: &str) -> Wrapper { get_d(self.global[..].to_vec(), k.to_string()) }
812
813    /// It will print the global env
814    #[allow(dead_code)]
815    pub fn debug_global(&self) { println!("{:?}", self.global) }
816
817    /// It will print the local env
818    /// # Example
819    /// ```
820    /// use rust_env::Env;
821    ///
822    /// let mut env = Env::new("./.env");
823    /// env.global_env();
824    ///
825    /// //printing just global env
826    /// env.debug_global();
827    ///
828    /// //printing just local env
829    /// env.debug_local()
830    /// ```
831    #[allow(dead_code)]
832    pub fn debug_local(&self) { println!("{:?}", self.data) }
833
834    #[allow(dead_code)]
835    /// Retrieves a value from the environment, checking both global and local scopes.
836    ///
837    /// This function performs a merged lookup:
838    ///
839    /// 1. Checks global environment variables (OS-level)
840    /// 2. Falls back to local `.env` data if not found
841    ///
842    /// # Lookup Order
843    ///
844    /// ```text
845    /// global → local
846    /// ```
847    ///
848    /// # Behavior
849    ///
850    /// - Returns the first matching key found
851    /// - If the value is a string, returns `Wrapper::Str`
852    /// - If the value is a list, returns `Wrapper::Vec`
853    /// - If the key does not exist, returns `Wrapper::Empty`
854    ///
855    /// # Arguments
856    ///
857    /// - `k`: The key to search for
858    ///
859    /// # Returns
860    ///
861    /// A `Wrapper` enum containing:
862    /// - `Wrapper::Str(String)` for single values
863    /// - `Wrapper::Vec(Vec<String>)` for list values
864    /// - `Wrapper::Empty` if not found
865    ///
866    /// # Example
867    ///
868    /// ```rust
869    /// use rust_env::{Env, Wrapper};
870    ///
871    /// let mut env = Env::new("./.env");
872    /// env.global_env();
873    ///
874    /// match env.get_pair("PORT") {
875    ///     Wrapper::Str(v) => println!("PORT = {}", v),
876    ///     Wrapper::Vec(v) => println!("List = {:?}", v),
877    ///     Wrapper::Empty => println!("Not found"),
878    /// }
879    /// ```
880    ///
881    /// # Notes
882    ///
883    /// - Global variables override local ones
884    /// - This function does not modify state
885    /// - Lookup is linear (O(n)) in both environments
886    pub fn get_pair(&mut self, k: &str) -> Wrapper {
887        return match get_d(self.global[..].to_vec(), k.to_string()) {
888            Wrapper::Empty => get_d(self.data[..].to_vec(), k.to_string()),
889            e => e
890        };
891    }
892}
893