Skip to content

Commit 556e4fd

Browse files
authored
Merge pull request adeharo9#18 from adeharo9/dev
v1.0.0-alpha merge
2 parents 453d2a5 + 3b4ce7f commit 556e4fd

File tree

395 files changed

+35384
-728
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

395 files changed

+35384
-728
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,10 @@
3232
*.app
3333

3434
# ANTLR
35-
.antlr
35+
.antlr/
3636

3737
# Project specifics
3838
*.env
39-
*.cpp
4039

4140
# VSCode specifics
4241
.vscode/

CMakeLists.txt

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,47 @@
1-
#----------------------- PROJECT CONFIGURATION -------------------------
1+
#----------------------- PROJECT CONFIGURATION --------------------------------
22
cmake_minimum_required(VERSION 3.10)
3-
project(cpp-dotenv VERSION 0.2.0)
3+
project(cpp-dotenv VERSION 1.0.0)
44

5-
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD 11)
66
set(CMAKE_CXX_STANDARD_REQUIRED ON)
77

8-
#----------------------- LIBRARY CONFIGURATION -------------------------
8+
if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
9+
set(CMAKE_BUILD_TYPE RELEASE)
10+
else()
11+
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
12+
endif()
13+
message(STATUS "Building CPP-DOTENV in ${CMAKE_BUILD_TYPE} mode")
914

10-
set(CPP_DOTENV_LIB cpp_dotenv CACHE INTERNAL "")
15+
#------------------- SUBDIRECTORY ADDITION ------------------------------------
16+
17+
add_subdirectory(common)
18+
add_subdirectory(src)
19+
20+
#----------------------- LIBRARY CONFIGURATION --------------------------------
21+
22+
set(CPP_DOTENV cpp_dotenv CACHE INTERNAL "")
1123
set(CPP_DOTENV_SRC
12-
dotenv.h
24+
src/dotenv.cpp
25+
include/dotenv.h
1326
)
1427

15-
add_library(${CPP_DOTENV_LIB} ${CPP_DOTENV_SRC})
16-
set_target_properties(${CPP_DOTENV_LIB} PROPERTIES
17-
LINKER_LANGUAGE CXX
28+
add_library(${CPP_DOTENV} ${CPP_DOTENV_SRC})
29+
30+
target_link_libraries(${CPP_DOTENV}
31+
${ENVIRON_LIB}
32+
${PARSER_LIB}
1833
)
1934

20-
target_include_directories(${CPP_DOTENV_LIB} PUBLIC
21-
${CMAKE_CURRENT_SOURCE_DIR}
35+
target_include_directories(${CPP_DOTENV} PUBLIC
36+
${CMAKE_CURRENT_SOURCE_DIR}/include
2237
)
38+
39+
if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG")
40+
target_compile_options(${CPP_DOTENV} PRIVATE
41+
-g -Wall -O0
42+
)
43+
else()
44+
target_compile_options(${CPP_DOTENV} PRIVATE
45+
-O3
46+
)
47+
endif()

LICENSE

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
BSD 2-Clause License
1+
BSD 3-Clause License
22

3-
Copyright (c) 2018, Alejandro de Haro
3+
Copyright (c) 2020, Alejandro de Haro
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without
77
modification, are permitted provided that the following conditions are met:
88

9-
* Redistributions of source code must retain the above copyright notice, this
10-
list of conditions and the following disclaimer.
9+
1. Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
1111

12-
* Redistributions in binary form must reproduce the above copyright notice,
13-
this list of conditions and the following disclaimer in the documentation
14-
and/or other materials provided with the distribution.
12+
2. Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
3. Neither the name of the copyright holder nor the names of its
17+
contributors may be used to endorse or promote products derived from
18+
this software without specific prior written permission.
1519

1620
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1721
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

README.md

Lines changed: 166 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,163 @@
1-
# cpp-dotenv
1+
# [C++ .ENV](https://github.com/adeharo9/cpp-dotenv)
22

3-
![version 0.2.0](https://img.shields.io/badge/version-0.2.0-blue)
3+
<img src="https://raw.githubusercontent.com/adeharo9/cpp-dotenv/dev/cpp-dotenv.png" alt="cpp-dotenv" align="right"/>
44

5-
C++ implementation of NodeJS [dotenv](https://github.com/motdotla/dotenv) project. Loads environment variables from `.env` for C++ projects.
5+
![v1.0.0-alpha](https://img.shields.io/badge/version-v1.0.0--alpha-blue "v1.0.0-alpha")
6+
![BSD 3-clause license](https://img.shields.io/badge/license-BSD%203--clause-green "BSD 3-clause license")
67

7-
**Please take into account this is still a developing project.**
8+
Loads environment variables from `.env` files for C++ projects.
89

9-
**cpp-dotenv** is implemented as a single C++ header file, so there is no need to compile nor to add complex file dependencies to your project. Simply include the header file wherever you want to use it and ta-da!, you're done.
10+
C++ implementation of NodeJS [dotenv](https://github.com/motdotla/dotenv) project. `load_dotenv()` method API inspired by Python's [python-dotenv](https://pypi.org/project/python-dotenv/) port of the dotenv project.
11+
12+
> _**NOTE: please take into account this is still a developing project.**_
1013
1114
## Table of contents
1215

1316
1. [Dependencies](#dependencies)
14-
2. [Usage](#usage)
17+
2. [Build](#build)
1518
1. [CMake](#cmake)
16-
3. [Examples](#examples)
19+
3. [Usage](#usage)
20+
1. [`load_dotenv()` method](#load_dotenv-method)
21+
4. [Features](#features)
22+
1. [Error reporting](#error-reporting)
23+
2. [Escape sequence expansion](#escape-sequence-expansion)
24+
3. [Variable overwritting](#variable-overwritting)
25+
4. [Variable resolution](#variable-resolution)
26+
5. [Examples](#examples)
1727
1. [Basic usage](#basic-usage)
1828
2. [Reference renaming](#reference-renaming)
1929
3. [Several dotenv files](#several-dotenv-files)
20-
4. [Grammar](#grammar)
30+
4. [Variable resolution](#variable-resolution-1)
31+
6. [Known limitations](#known-limitations)
32+
7. [Grammar](#grammar)
2133

2234
## Dependencies
2335

24-
**NONE**, for sure! :sunglasses: If it had, it wouldn't follow the basic dotenv principles.
36+
**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.
37+
38+
## Build
39+
40+
Supported build methods are:
41+
42+
- [CMake](#cmake) (>=3.10)
43+
44+
### CMake
45+
46+
**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:
47+
48+
```cmake
49+
add_subdirectory(cpp-dotenv)
50+
```
51+
52+
```cmake
53+
target_link_libraries(YOUR_TARGET cpp_dotenv)
54+
```
55+
56+
After this, you might use the library as described in [usage](#usage); no extra scoping, no need to worry about the project's directory structure.
2557

2658
## Usage
2759

28-
To be able to use the dotenv classes, simply include the header file:
60+
To be able to use the dotenv classes, simply include the main header file:
2961

3062
```cpp
3163
#include "dotenv.h"
3264
```
3365

34-
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:
66+
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:
3567

3668
```cpp
3769
using namespace dotenv;
3870
```
3971

40-
For convenience, **cpp-dotenv** auto-configures a class object (which is instance of the singleton class `dotenv`) by calling the `load_dotenv()` method at the very beginning of your file (just right before the end of `dotenv.h`) and trying to load a `.env` file, although if you need to add-in your own files (like `.myenv`), simply re-run the loading step passing the file name as parameter; everything new will show up on the `dotenv` instances.
72+
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.
4173

42-
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_config()` function with the `overwrite` parameter set to `true`. For an example, take a look at [this one](#several-dotenv-files).
74+
```cpp
75+
env.load_dotenv();
76+
```
4377

44-
Also for convenience, there is a namespace-global pre-loaded 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:
78+
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.
79+
80+
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:
4581

4682
```cpp
4783
auto& dotenv = env; // 'auto' here is 'dotenv::dotenv'
4884
```
4985

50-
### CMake
86+
### `load_dotenv()` method
5187

52-
`cpp-dotenv` also 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_LIB` library to your own targets where needed:
88+
The `load_dotenv()` method, part of `class dotenv`, is declared in the [`include/dotenv.h`](/include/dotenv.h) file. Since all of its parameters have default values, it can be called with any number of arguments.
5389

54-
```cmake
55-
add_subdirectory(cpp-dotenv)
56-
```
90+
#### Signature
5791

58-
```cmake
59-
target_link_libraries(YOUR_TARGET ${CPP_DOTENV_LIB})
92+
```cpp
93+
dotenv& load_dotenv(const std::string& dotenv_path = ".env",
94+
const bool overwrite = false,
95+
const bool interpolate = true);
6096
```
6197
62-
After this, you might use the library as described in [usage](#usage); no extra scoping, no need to worry about the project's directory structure.
98+
#### Parameters
99+
100+
- `dotenv_path`: path string, absolute or relative to the executable calling the function, of the dotenv file to be loaded. Default is `".env"`.
101+
- `overwrite`: boolean representing whether or not to overwrite already-defined environment variables at loading time. Default is `false`.
102+
- `interpolate`: boolean representing whether or not to resolve in-value variable references. Default is `true`.
103+
104+
#### Return
105+
106+
A reference to the `class dotenv` `this` object being used is returned, which allows for concatenating several `load_dotenv()` calls and serially load files.
107+
108+
## Features
109+
110+
The **cpp-dotenv** library has the following built-in features:
111+
112+
- [Error reporting](#error-reporting)
113+
- [Escape sequence expansion](#escape-sequence-expansion)
114+
- [Variable overwritting](#variable-overwritting)
115+
- [Variable resolution](#variable-resolution)
116+
117+
### Error reporting
118+
119+
**cpp-dotenv** reports and handles four different types of errors:
120+
121+
- **Lexer errors**: errors produced by an incorrect format of the input language, i.e. usage of language features that do not belong to the dotenv syntax. This kind of errors throw irrecoverable exceptions.
122+
- **Parsing errors**: errors produced by an incorrect use of the input language, i.e. the language syntax itself is correct, but it is used in an unexpected way. This kind of errors invalidate the variable they occurr in, making it to become as if it was not defined, but allowing the loading process to recover and continue.
123+
- **Circular reference errors**: errors produced by variables that define a cycle of references, which are ultimately not resolved and their references are deleted from all the corresponding values. The variables they occurr in are ultimately defined as without the cycling references.
124+
- **Undefined external reference warnings**: warnings produced by references to externally-loaded variables that are not present in the host environment. This variables are ultimately assigned their value as the empty string.
125+
126+
### Escape sequence expansion
127+
128+
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.
129+
130+
Typical one-character escape sequences are supported (`\n`, `\t`, `\\`, etc.). Dotenv-spefic escape sequences are:
131+
132+
| Character | Escape sequence |
133+
| :-------: | :-------------: |
134+
| `=` | `\=` |
135+
| `$` | `\$` |
136+
| `#` | `\#` |
137+
138+
> _NOTE: escape sequences on externally-loaded variables **ARE NOT EXPANDED**._
139+
140+
### Variable overwritting
141+
142+
By default, already-defined environment variables are not overwritten even if redefined in some of the loaded files.
143+
144+
This behavior can be changed, however, by calling the [`load_dotenv()` method](#load_dotenv-method) with the [`overwrite` parameter](#parameters) set to `true`. For an example on how to use it, take a look at [this one](#several-dotenv-files).
145+
146+
### Variable resolution
147+
148+
**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.
149+
150+
- Variable reference with variables declared in the **same file** is order-independent: there's no need to worry about the declaration order of the variables inside a same file, **cpp-dotenv** will resolve all of the symbols regardless of their order of declaration.
151+
- Variable reference with variables declared on **different files** is order-depdendent on the loading order of the files via the `load_dotenv()` method: variables defined in later calls to `load_dotenv()` are not yet visible to files being processed at a previous load and will be treated as external variables.
152+
153+
Variable resolution can be explicitly turned off by setting the [`interpolate` parameter](#parameters) of the [`load_dotenv()` method](#load_dotenv-method) to `false`.
154+
155+
> _NOTE: variable references inside externally-loaded variables **ARE NOT RESOLVED**._
156+
157+
There are two different types of supported variable references:
158+
159+
- **Raw-style unbounded references** of the style `$VAR_NAME`, which only support references composed by letters, numbers and underscores. Their name must start by a letter or by an underscore, and have at least one character.
160+
- **Bounded references**, of the style `${VAR_NAME}`, which support a wider set of character possibilities.
63161
64162
## Examples
65163
@@ -89,6 +187,7 @@ using namespace std;
89187

90188
int main()
91189
{
190+
env.load_dotenv();
92191
cout << "DB_NAME: " << env["DB_NAME"] << endl;
93192
cout << "eval \"" << env["COMMAND"] << " " << env["HOST"] << "\"" << endl;
94193
}
@@ -104,7 +203,7 @@ $ ./main
104203

105204
### Reference renaming
106205

107-
Assuming the same `.env` file as in the [previous case](#basic-usage), the predefined `env` reference can be easily renamed and used just exactly as the original one.
206+
Assuming the same `.env` file as in the [previous case](#basic-usage), 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.
108207

109208
The following code:
110209

@@ -116,7 +215,7 @@ using namespace std;
116215

117216
int main()
118217
{
119-
auto& dotenv = dotenv::env;
218+
auto& dotenv = dotenv::env.load_dotenv();
120219
cout << "DB_NAME: " << dotenv["DB_NAME"] << endl;
121220
cout << "eval \"" << dotenv["COMMAND"] << " " << dotenv["HOST"] << "\"" << endl;
122221
}
@@ -132,7 +231,7 @@ $ ./main
132231

133232
### Several dotenv files
134233

135-
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 in addition to the default one and overwritting any variables that are redefined on the files can be done as follows:
234+
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:
136235

137236
Assume the following `.env` file:
138237

@@ -162,8 +261,7 @@ using namespace std;
162261

163262
int main()
164263
{
165-
env.load_dotenv(".env", true);
166-
env.load_dotenv(".pubenv", true);
264+
env.load_dotenv(".env", true).load_dotenv(".pubenv", true);
167265
cout << "DB_NAME: " << env["DB_NAME"] << endl;
168266
cout << "eval \"" << env["COMMAND"] << " " << env["HOST"] << "\"" << endl;
169267
}
@@ -177,12 +275,48 @@ $ ./main
177275
eval "ping 8.8.8.8"
178276
```
179277

180-
## Grammar
278+
### Variable resolution
279+
280+
Assume an environment variable `${HOST}` already defined on the host environment as `myweb.com` and the following `.env` file:
281+
282+
```env
283+
# FULL URL
284+
URL=${URL_PROT}://${HOST}/${URL_SUBD}
285+
286+
# PARTIAL DEFINITIONS
287+
URL_PROT=https
288+
URL_SUBD=some/sub/page.html
289+
```
290+
291+
The following `.cpp` file:
181292

182-
For the geeks, you can check the grammar I've implemented on the `grammar/env.g4` file. Despite being written in an ANTLR4 fashion, I've implemented a simple recursive parser myself given the basic nature of the language. The parser and its methods are publicly available under the `dotenv::parser` class.
293+
```cpp
294+
#include "dotenv.h"
295+
#include <iostream>
296+
297+
using namespace dotenv;
298+
using namespace std;
183299

184-
## Known issues
300+
int main()
301+
{
302+
env.load_dotenv();
303+
cout << "URL: " << env["URL"] << endl;
304+
}
305+
```
185306

186-
The complete list of issues can be consulted at the [issues page](https://github.com/adeharo9/cpp-dotenv/issues).
307+
would produce the following output:
308+
309+
```shell
310+
$ ./main
311+
URL: https://myweb.com/some/sub/page.html
312+
```
313+
314+
## Known limitations
315+
316+
- _Arbitrary octal value escape sequences are not expanded._
317+
- _Arbitrary hexadecimal value escape sequences are not expanded._
318+
- _Arbitrary unicode value escape sequences are not expanded._
319+
320+
## Grammar
187321

188-
1. [Variable resolution on values not yet vailable](https://github.com/adeharo9/cpp-dotenv/issues/3)
322+
For the geeks, you can check the implemented grammars and all of the ANTLR-related files on the [`common/antlr/` directory](/common/antlr).

common/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#------------------- SUBDIRECTORY ADDITION ------------------------------------
2+
3+
add_subdirectory(libs)
4+
add_subdirectory(antlr)

common/antlr/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#------------------- SUBDIRECTORY ADDITION ------------------------------------
2+
3+
add_subdirectory(parser)

0 commit comments

Comments
 (0)