1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! # Eve: Environment editor
//! The **eve** utility reads the specified files, or standard input if no files
//! are specified, replacing all instances of `{{VAR}}` with the environment
//! variable of the same name e.g. `$VAR`. This utility is mainly useful as a
//! replacement to using `sed` to insert environment variables into files. As is
//! common when using Docker.
//!
//! ## Example
//! Here's an example of replacing variables in a nginx configuration with
//! environment variables, and comparsion with the equivalent `sed` command.
//!
//! #### `nginx.conf`
//! ```nginx
//! server {
//!     listen 80;
//!     listen [::]:80;
//!
//!     server_name {{NGINX_HOST}};
//!
//!     location / {
//!         proxy_pass {{NGINX_PROXY}};
//!         proxy_next_upstream error timeout invalid_header http_500 http_502
//!             http_503 http_504;
//!         proxy_redirect off;
//!         proxy_buffering off;
//!         proxy_set_header        Host            {{NGINX_HOST}};
//!         proxy_set_header        X-Real-IP       $remote_addr;
//!         proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
//!     }
//! }
//! ```
//!
//! #### `.env`
//! ```text
//! NGINX_HOST=localhost
//! NGINX_PROXY=localhost:8000
//! ```
//!
//! #### Commands
//!
//! ###### `sed`
//! ```bash
//! sed -e "s|{{NGINX_HOST}}|$NGINX_HOST|" \
//!     -e "s|{{NGINX_PROXY}}|$NGINX_PROXY|" \
//!     nginx.conf
//! ```
//! ###### `eve`
//! ```bash
//! eve nginx.conf
//! ```
//!
//! #### Output
//! ```bash
//! server {
//!     listen 80;
//!     listen [::]:80;
//!
//!     server_name localhost;
//!
//!     location / {
//!         proxy_pass localhost:8000;
//!         proxy_next_upstream error timeout invalid_header http_500 http_502
//!             http_503 http_504;
//!         proxy_redirect off;
//!         proxy_buffering off;
//!         proxy_set_header        Host            localhost;
//!         proxy_set_header        X-Real-IP       $remote_addr;
//!         proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
//!     }
//! }
//! ```

extern crate regex;
extern crate dotenv;

use std::borrow::Cow;
use std::path::Path;
use std::env;

use regex::{Captures, Regex, Replacer};

/// A struct to allow replacement of text with environment variables.
#[derive(Clone, Copy, Debug)]
pub struct Eve;

impl Eve {
    /// Creates a new `Eve` using environment variables from `./.env`.
    pub fn new() -> Result<Self, dotenv::Error> {
        dotenv::dotenv()?;
        Ok(Eve)
    }

    /// Creates a new `Eve` using environment variables from the path specified
    /// by the `path` variables.
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, dotenv::Error> {
        dotenv::from_path(path)?;
        Ok(Eve)
    }

    /// Perform a replacement on the provided `text`.
    pub fn replace<'e, 's>(&'e self, text: &'s str)
        -> Result<Cow<'s, str>, regex::Error>
    {
        let regex = Regex::new(r"\{\{(.*)\}\}")?;

        // `*self` used so that we can have `&self` without
        // requiring `&mut self`.
        Ok(regex.replace_all(text, *self))
    }
}

impl Replacer for Eve {
    fn replace_append(&mut self, caps: &Captures, dst: &mut String) {
        let env_var = caps.get(1).expect("Expected 2 captures,\
                                         is there a capture group in the braces\
                                         ?");

        dst.extend(env::var(env_var.as_str()))
    }
}