adeharo9 / cpp-dotenv

Loads environment variables from .env files for C++ projects.
BSD 3-Clause "New" or "Revised" License
89 stars 21 forks source link
cpp dotenv dotenv-files dotenv-parser environment-variables

C++ .ENV

cpp-dotenv

v1.0.0-alpha BSD 3-clause license

Loads environment variables from .env files for C++ projects.

C++ implementation of NodeJS dotenv project. load_dotenv() method API inspired by Python's python-dotenv port of the dotenv project.

NOTE: please take into account this is still a developing project.

Table of contents

  1. Dependencies
  2. Build
    1. CMake
  3. Usage
    1. load_dotenv() method
  4. Features
    1. Error reporting
    2. Escape sequence expansion
    3. Variable overwritting
    4. Variable resolution
  5. Examples
    1. Basic usage
    2. Reference renaming
    3. Several dotenv files
    4. Variable resolution
  6. Known limitations
  7. Grammar

Dependencies

NONE, for sure! :sunglasses: If it had any, it wouldn't follow the basic dotenv principles. All the needed libraries are shipped with this repository right out of the box.

Build

Supported build methods are:

CMake

cpp-dotenv comes with support for CMake right out of the box. In order to use it, simply include this repository's directory and link the cpp_dotenv target to your own targets where needed:

add_subdirectory(cpp-dotenv)
target_link_libraries(YOUR_TARGET cpp_dotenv)

After this, you might use the library as described in usage; no extra scoping, no need to worry about the project's directory structure.

Usage

To be able to use the dotenv classes, simply include the main header file:

#include "dotenv.h"

For the sake of simplycity (and if your project namespace density allows to), you can also use the dotenv namespace under which all definitions are placed:

using namespace dotenv;

In order to bring your environment variables from your configuration files, simply make as many calls to the load_dotenv() method as needed with the appropriate paths (either relative to your executable's path or absolute) and arguments.

env.load_dotenv();

Not passing any argument to the function is equivalent to tell cpp-dotenv to search for a file named .env at the same level as the executable that is making the call to the function.

For your convenience, there is a namespace-global reference variable to the dotenv singleton class instance named env. Simply use it as you would use a dotenv object on NodeJS, or you can define your own references:

auto& dotenv = env; // 'auto' here is 'dotenv::dotenv'

load_dotenv() method

The load_dotenv() method, part of class dotenv, is declared in the include/dotenv.h file. Since all of its parameters have default values, it can be called with any number of arguments.

Signature

dotenv& load_dotenv(const std::string& dotenv_path = ".env",
                    const bool overwrite = false,
                    const bool interpolate = true);

Parameters

Return

A reference to the class dotenv this object being used is returned, which allows for concatenating several load_dotenv() calls and serially load files.

Features

The cpp-dotenv library has the following built-in features:

Error reporting

cpp-dotenv reports and handles four different types of errors:

Escape sequence expansion

Escape sequence expansion happens in all of the loaded values (both string and raw form) right at the end of the loading process, after the variable resolution has already been performed.

Typical one-character escape sequences are supported (\n, \t, \\, etc.). Dotenv-spefic escape sequences are:

Character Escape sequence
= \=
$ \$
# \#

NOTE: escape sequences on externally-loaded variables ARE NOT EXPANDED.

Variable overwritting

By default, already-defined environment variables are not overwritten even if redefined in some of the loaded files.

This behavior can be changed, however, by calling the load_dotenv() method with the overwrite parameter set to true. For an example on how to use it, take a look at this one.

Variable resolution

cpp-dotenv by default resolves variables nested inside variable definitions in the parsed files, both with those defined in the file being loaded or already present in the hosting environment itself.

Variable resolution can be explicitly turned off by setting the interpolate parameter of the load_dotenv() method to false.

NOTE: variable references inside externally-loaded variables ARE NOT RESOLVED.

There are two different types of supported variable references:

Examples

Basic usage

Assume the following .env file:

# DB THINGS
DB_NAME=DontDoThisAtHome
DB_PASS=such_security

# CONNECTIONS THINGS
COMMAND=ping
HOST=8.8.8.8
MESSAGE="Hey buddy!"

The following .cpp file:

#include "dotenv.h"
#include <iostream>

using namespace dotenv;
using namespace std;

int main()
{
    env.load_dotenv();
    cout << "DB_NAME: " << env["DB_NAME"] << endl;
    cout << "eval \"" << env["COMMAND"] << " " << env["HOST"] << "\"" << endl;
}

would produce the following output:

$ ./main
  DB_NAME: DontDoThisAtHome
  eval "ping 8.8.8.8"

Reference renaming

Assuming the same .env file as in the previous case, the predefined env reference can be easily renamed and used just exactly as the original one. The load_dotenv() method also returns a reference to the object it is being applied to, so it can be easily nested in a case like this.

The following code:

#include "dotenv.h"
#include <iostream>

using namespace std;

int main()
{
    auto& dotenv = dotenv::env.load_dotenv();
    cout << "DB_NAME: " << dotenv["DB_NAME"] << endl;
    cout << "eval \"" << dotenv["COMMAND"] << " " << dotenv["HOST"] << "\"" << endl;
}

would produce the following output:

$ ./main
  DB_NAME: DontDoThisAtHome
  eval "ping 8.8.8.8"

Several dotenv files

The situation of having several different dotenv files is no stranger one (.env for private configuration variables, .pubenv for public variables, etc.). Loading several files and overwritting any variables that are redefined on the files can be done as follows:

Assume the following .env file:

# DB THINGS
DB_NAME=DontDoThisAtHome
DB_PASS=such_security

And the following .pubenv file:

# CONNECTIONS THINGS
COMMAND=ping
HOST=8.8.8.8
MESSAGE="Hey buddy!"

The following source file:

#include "dotenv.h"
#include <iostream>

using namespace dotenv;
using namespace std;

int main()
{
    env.load_dotenv(".env", true).load_dotenv(".pubenv", true);
    cout << "DB_NAME: " << env["DB_NAME"] << endl;
    cout << "eval \"" << env["COMMAND"] << " " << env["HOST"] << "\"" << endl;
}

would produce the following output:

$ ./main
  DB_NAME: DontDoThisAtHome
  eval "ping 8.8.8.8"

Variable resolution

Assume an environment variable ${HOST} already defined on the host environment as myweb.com and the following .env file:

# FULL URL
URL=${URL_PROT}://${HOST}/${URL_SUBD}

# PARTIAL DEFINITIONS
URL_PROT=https
URL_SUBD=some/sub/page.html

The following .cpp file:

#include "dotenv.h"
#include <iostream>

using namespace dotenv;
using namespace std;

int main()
{
    env.load_dotenv();
    cout << "URL: " << env["URL"] << endl;
}

would produce the following output:

$ ./main
  URL: https://myweb.com/some/sub/page.html

Known limitations

Grammar

For the geeks, you can check the implemented grammars and all of the ANTLR-related files on the common/antlr/ directory.