Compare commits
11 Commits
8dbdea7331
...
2b9e0366f4
Author | SHA1 | Date |
---|---|---|
Alexander Zhirov | 2b9e0366f4 | |
Alexander Zhirov | f1f50dc7f2 | |
Alexander Zhirov | 1e45d1a503 | |
Alexander Zhirov | 1d89b62354 | |
Alexander Zhirov | 1368b271b0 | |
Alexander Zhirov | 323ea03a45 | |
Alexander Zhirov | a1a208ba63 | |
Alexander Zhirov | fb3550b8ec | |
Alexander Zhirov | d9cf28a2a8 | |
Alexander Zhirov | 6952bab705 | |
Alexander Zhirov | de3690c94e |
29
CHANGELOG.md
29
CHANGELOG.md
|
@ -1,11 +1,36 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [0.2.0]()
|
## [v0.3.0](https://git.zhirov.kz/dlang/readconf/compare/v0.2.0...v0.3.0) (2023.03.30)
|
||||||
|
|
||||||
26.03.2023
|
### New
|
||||||
|
|
||||||
|
- Read multiple configuration files
|
||||||
|
- Quick access to a file/section/parameter using indexes
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- Updated unittests
|
||||||
|
- Added [examples](examples/) of configuration files
|
||||||
|
- [Wiki](https://git.zhirov.kz/dlang/readconf/wiki) added
|
||||||
|
|
||||||
|
## [v0.2.0](https://git.zhirov.kz/dlang/readconf/compare/v0.1.1...v0.2.0) (2023.03.26)
|
||||||
|
|
||||||
### New
|
### New
|
||||||
|
|
||||||
- Adding sections support
|
- Adding sections support
|
||||||
- Taking into account spaces and tabs to separate the parameter, value and comment
|
- Taking into account spaces and tabs to separate the parameter, value and comment
|
||||||
- Added aliases to convenient parameter access to the value
|
- Added aliases to convenient parameter access to the value
|
||||||
|
|
||||||
|
## [v0.1.1](https://git.zhirov.kz/dlang/readconf/compare/v0.1.0...v0.1.1) (2023.03.24)
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
|
||||||
|
- Checking empty keys
|
||||||
|
- Reading an expression in quotation marks
|
||||||
|
|
||||||
|
## [v0.1.0](https://git.zhirov.kz/dlang/readconf/commits/6409917cbe6a287db73fe3eea4bccaadf00379e7) (2023.03.23)
|
||||||
|
|
||||||
|
### The first stable working release
|
||||||
|
|
||||||
|
- The parameters are separated by `=` or `=>`
|
||||||
|
- The ability to comment lines through delimiters `;`, `#`, `//`
|
||||||
|
|
44
README.md
44
README.md
|
@ -1,13 +1,25 @@
|
||||||
# readconf
|
<p align="center">
|
||||||
|
<img src="img/logo.png" width=320>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h1 align="center">readconf</h1>
|
||||||
|
|
||||||
|
[![license](https://img.shields.io/github/license/AlexanderZhirov/readconf.svg?sort=semver&style=for-the-badge&color=green)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html)
|
||||||
|
[![main](https://img.shields.io/badge/dynamic/json.svg?label=git.zhirov.kz&style=for-the-badge&url=https://git.zhirov.kz/api/v1/repos/dlang/readconf/tags&query=$[0].name&color=violet&logo=D)](https://git.zhirov.kz/dlang/readconf)
|
||||||
|
[![githab](https://img.shields.io/github/v/tag/AlexanderZhirov/readconf.svg?sort=semver&style=for-the-badge&color=blue&label=github&logo=D)](https://github.com/AlexanderZhirov/readconf)
|
||||||
|
[![dub](https://img.shields.io/dub/v/readconf.svg?sort=semver&style=for-the-badge&color=orange&logo=D)](https://code.dlang.org/packages/readconf)
|
||||||
|
[![linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black)](https://www.linux.org/)
|
||||||
|
|
||||||
Singleton for reading the configuration file required for your program.
|
Singleton for reading the configuration file required for your program.
|
||||||
|
|
||||||
## What can do
|
## What can do
|
||||||
|
|
||||||
1. Separation of parameter and value by separators `=` and `=>`
|
- Reading multiple configuration files
|
||||||
2. Commenting on lines using special characters `;`, `#`, `//`, `/*`
|
- Separation of parameters by sections
|
||||||
3. Support for sections for describing parameter blocks (sections are set by the name in `[]`)
|
- Access to parameters and sections using keys and indexes
|
||||||
4. Support for spaces and tabs for visual separation
|
- Commenting on lines
|
||||||
|
|
||||||
|
You will get more detailed information on the [wiki](https://git.zhirov.kz/dlang/readconf/wiki).
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
|
@ -23,7 +35,7 @@ import std.stdio;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
rc.read("./settings.conf");
|
rc.read("./tests/settings.conf");
|
||||||
|
|
||||||
foreach (key, param; rc.sn.keys())
|
foreach (key, param; rc.sn.keys())
|
||||||
writefln("%s => %s", key, param);
|
writefln("%s => %s", key, param);
|
||||||
|
@ -33,7 +45,7 @@ void main()
|
||||||
foreach (key, param; rc.sn("part2").keys())
|
foreach (key, param; rc.sn("part2").keys())
|
||||||
writefln("%s => %s", key, param);
|
writefln("%s => %s", key, param);
|
||||||
|
|
||||||
writeln(rc.sn("part2").key("value1"));
|
writeln(rc["part2"]["value1"]);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -53,6 +65,20 @@ value3 => good value!
|
||||||
this value will be in the new section
|
this value will be in the new section
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dub
|
## Unittests
|
||||||
|
|
||||||
Add a dependency on `"readconf": "~>0.2.0"`
|
The unittests provide [examples](examples/) of configuration files and the `settings.conf` file located in the [tests](tests/):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Running bin/readconf-test-unittest
|
||||||
|
✓ test __unittest_L4_C1
|
||||||
|
✓ test __unittest_L106_C1
|
||||||
|
✓ test __unittest_L25_C1
|
||||||
|
✓ test __unittest_L51_C1
|
||||||
|
|
||||||
|
Summary: 4 passed, 0 failed in 7 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
## DUB
|
||||||
|
|
||||||
|
Add a dependency on `"readconf": "~>0.3.0"`
|
||||||
|
|
2
dub.json
2
dub.json
|
@ -33,6 +33,6 @@
|
||||||
],
|
],
|
||||||
"targetName": "readconf",
|
"targetName": "readconf",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"singlog": "~>0.1.0"
|
"singlog": "~>0.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,6 @@
|
||||||
"versions": {
|
"versions": {
|
||||||
"datefmt": "1.0.4",
|
"datefmt": "1.0.4",
|
||||||
"silly": "1.1.1",
|
"silly": "1.1.1",
|
||||||
"singlog": "0.1.0"
|
"singlog": "0.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
This line will be a comment, since it does not match the basic template
|
||||||
|
parameter1 => value1 ; This will be a comment
|
||||||
|
parameter2 => value2 # This will be a comment
|
||||||
|
parameter3 => value3 // This will be a comment
|
||||||
|
parameter4 => value4 /* This will be a comment
|
||||||
|
parameter5 => value5;This will not be a comment
|
||||||
|
parameter6 => value6// This will also be a whole value
|
||||||
|
parameter7 => //value7 ;The value will not be read
|
||||||
|
parameter8 => "//value8" # Now the value is correctly
|
||||||
|
parameter9 => ';value9' // The value is correctly too
|
||||||
|
parameter10 => '"value10"' // Quotes inside
|
|
@ -0,0 +1,22 @@
|
||||||
|
parameter1 = value1
|
||||||
|
parameter_2 = value2
|
||||||
|
|
||||||
|
[first-section] ; Creating the first section
|
||||||
|
parameter1 = value3 ; A section may contain similar parameter names
|
||||||
|
parameter_2 = value4
|
||||||
|
|
||||||
|
[second-section] // Another section
|
||||||
|
parameter1 = value5
|
||||||
|
parameter_2 = value6
|
||||||
|
|
||||||
|
[] /* Addition of the main section
|
||||||
|
parameter3 = value7 /* A new parameter will be added
|
||||||
|
parameter1 = value8 /* And parameter1 will be redefined
|
||||||
|
|
||||||
|
[first-section] # Addition of the first section
|
||||||
|
parameter3 = value9
|
||||||
|
parameter4 = value10
|
||||||
|
|
||||||
|
[_section] # Creating the new section
|
||||||
|
parameter1 = value11
|
||||||
|
parameter2 = value12
|
|
@ -0,0 +1,16 @@
|
||||||
|
parameter1=value1
|
||||||
|
parameter2=>value2
|
||||||
|
parameter3=value3
|
||||||
|
parameter4=>value4
|
||||||
|
|
||||||
|
_parameter5 = value5
|
||||||
|
parameter6 => value6
|
||||||
|
parameter7 = value7
|
||||||
|
|
||||||
|
parameter8 => value8
|
||||||
|
parameter9 =value9
|
||||||
|
parameter-10 =>value10
|
||||||
|
parameter11 = value11
|
||||||
|
|
||||||
|
|
||||||
|
parameter12_ => value12
|
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
|
@ -7,27 +7,32 @@ import std.meta;
|
||||||
import singlog;
|
import singlog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read config object
|
* **Get an object to read the configuration file**
|
||||||
|
*
|
||||||
|
* - The `read()` will allow you to read the configuration file
|
||||||
|
* - `cf()` or `configFile()` will allow you to refer to the read file to get the parameters
|
||||||
*/
|
*/
|
||||||
alias rc = Config.file;
|
alias rc = Config.file;
|
||||||
|
|
||||||
|
private const string mainSection = "[]";
|
||||||
|
|
||||||
class Config
|
class Config
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
enum {
|
enum {
|
||||||
GROUP_PROPERTY = 4,
|
GROUP_PARAMETER = 4,
|
||||||
GROUP_VALUE_1 = 11, // string
|
GROUP_VALUE_1 = 11, // string
|
||||||
GROUP_VALUE_2 = 14, // "strin"
|
GROUP_VALUE_2 = 14, // "strin"
|
||||||
GROUP_VALUE_3 = 16, // 'string'
|
GROUP_VALUE_3 = 16, // 'string'
|
||||||
GROUP_SECTION_OTHER_OUTER = 17, // "[string]"
|
GROUP_SECTION_OTHER_OUTER = 17, // [string]
|
||||||
GROUP_SECTION_OTHER_INNER = 18, // "[string]"
|
GROUP_SECTION_OTHER_INNER = 18, // string
|
||||||
GROUP_SECTION_MAIN = 20, // "[]"
|
GROUP_SECTION_MAIN = 20, // []
|
||||||
}
|
}
|
||||||
|
|
||||||
static Config config;
|
static Config config;
|
||||||
string path;
|
string path;
|
||||||
bool readed = false;
|
bool readed = false;
|
||||||
ConfigSection[string] sections;
|
ConfigFile[string] configs;
|
||||||
|
|
||||||
const string pattern = "^( |\\t)*(((\\w(\\w|-)+)(( |\\t)*(=>|=){1}"
|
const string pattern = "^( |\\t)*(((\\w(\\w|-)+)(( |\\t)*(=>|=){1}"
|
||||||
~ "( |\\t)*)(?!\\/(\\/|\\*))(([^ >\"'=\\n\\t#;].*?)|(\"(.+)\")"
|
~ "( |\\t)*)(?!\\/(\\/|\\*))(([^ >\"'=\\n\\t#;].*?)|(\"(.+)\")"
|
||||||
|
@ -37,61 +42,63 @@ private:
|
||||||
/**
|
/**
|
||||||
* Reading the configuration file
|
* Reading the configuration file
|
||||||
*/
|
*/
|
||||||
bool readConfig()
|
bool readConfig(const string configName)
|
||||||
{
|
{
|
||||||
File configuration;
|
File configuration;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configuration = File(this.path, "r");
|
configuration = File(this.path, "r");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.msg.warning("Unable to open the configuration file " ~ this.path);
|
log.w("Unable to open the configuration file " ~ this.path);
|
||||||
Log.msg.error(e);
|
log.e(e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configName !in this.configs)
|
||||||
|
this.configs[configName] = ConfigFile(configName);
|
||||||
|
|
||||||
auto regular = regex(this.pattern, "m");
|
auto regular = regex(this.pattern, "m");
|
||||||
|
|
||||||
// reading from the main section
|
// reading from the main section
|
||||||
string sectionName = "[]";
|
string sectionName = mainSection;
|
||||||
|
|
||||||
while (!configuration.eof())
|
while (!configuration.eof())
|
||||||
{
|
{
|
||||||
string line = configuration.readln();
|
string line = configuration.readln();
|
||||||
auto match = matchFirst(line, regular);
|
auto match = matchFirst(line, regular);
|
||||||
if (match)
|
|
||||||
|
if (match.length == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// if again main section
|
||||||
|
if (match[GROUP_SECTION_MAIN].length)
|
||||||
{
|
{
|
||||||
// if again main section
|
sectionName = match[GROUP_SECTION_MAIN];
|
||||||
if (match[GROUP_SECTION_MAIN].length)
|
continue;
|
||||||
{
|
|
||||||
sectionName = match[GROUP_SECTION_MAIN];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (match[GROUP_SECTION_OTHER_OUTER].length)
|
|
||||||
{
|
|
||||||
sectionName = match[GROUP_SECTION_OTHER_INNER];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int group = GROUP_VALUE_1;
|
|
||||||
|
|
||||||
if (match[group][0] == '\"')
|
|
||||||
group = GROUP_VALUE_2;
|
|
||||||
else if (match[group][0] == '\'')
|
|
||||||
group = GROUP_VALUE_3;
|
|
||||||
|
|
||||||
if (sectionName !in this.sections)
|
|
||||||
this.sections[sectionName] = ConfigSection(sectionName);
|
|
||||||
|
|
||||||
this.sections[sectionName].add(ConfigParameter(match[GROUP_PROPERTY], match[group]));
|
|
||||||
}
|
}
|
||||||
|
if (match[GROUP_SECTION_OTHER_OUTER].length)
|
||||||
|
{
|
||||||
|
sectionName = match[GROUP_SECTION_OTHER_INNER];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int group = GROUP_VALUE_1;
|
||||||
|
|
||||||
|
if (match[group][0] == '\"')
|
||||||
|
group = GROUP_VALUE_2;
|
||||||
|
else if (match[group][0] == '\'')
|
||||||
|
group = GROUP_VALUE_3;
|
||||||
|
|
||||||
|
this.configs[configName].add(sectionName, ConfigParameter(match[GROUP_PARAMETER], match[group]));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configuration.close();
|
configuration.close();
|
||||||
this.readed = true;
|
this.readed = true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.msg.warning("Unable to close the configuration file " ~ this.path);
|
log.w("Unable to close the configuration file " ~ this.path);
|
||||||
Log.msg.error(e);
|
log.e(e);
|
||||||
|
this.configs.remove(configName);
|
||||||
this.readed = false;
|
this.readed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,38 +124,123 @@ public:
|
||||||
* Read the configuration file
|
* Read the configuration file
|
||||||
* Params:
|
* Params:
|
||||||
* path = the path to the configuration file
|
* path = the path to the configuration file
|
||||||
|
* configName = a specific name to bind to the configuration file (default file name)
|
||||||
|
* Returns: `true` if the file was read successfully
|
||||||
*/
|
*/
|
||||||
bool read(string path)
|
bool read(string path, string configName = "")
|
||||||
{
|
{
|
||||||
this.path = path;
|
this.path = path;
|
||||||
if (!path.exists)
|
if (!path.exists)
|
||||||
throw new Exception("The configuration file does not exist: " ~ path);
|
throw new Exception("The configuration file does not exist: " ~ path);
|
||||||
return readConfig();
|
if (configName.length == 0)
|
||||||
|
configName = path.baseName();
|
||||||
|
if (configName in configs)
|
||||||
|
throw new Exception("The configuration file with this name has already been read");
|
||||||
|
return readConfig(configName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessing the read configuration file
|
||||||
|
* Params:
|
||||||
|
* configName = specific name to bind to the configuration file
|
||||||
|
* (if the read files are > 1, then specify a specific name, otherwise default file name)
|
||||||
|
* Returns: configuration file object ConfigFile
|
||||||
|
*/
|
||||||
|
@property ConfigFile configFile(string configName = "")
|
||||||
|
{
|
||||||
|
if (configName.length == 0)
|
||||||
|
{
|
||||||
|
if (configs.length == 1)
|
||||||
|
return configs[configs.byKey.front];
|
||||||
|
else
|
||||||
|
throw new Exception("You must explicitly specify the name of the configuration file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return configName in configs ? configs[configName] : ConfigFile(configName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the read configuration file
|
||||||
|
* Params:
|
||||||
|
* configName = specific name to bind to the configuration file
|
||||||
|
* (if the read files are > 1, then specify a specific name, otherwise default file name)
|
||||||
|
* Returns: configuration file object ConfigFile
|
||||||
|
*/
|
||||||
|
alias cf = configFile;
|
||||||
|
|
||||||
|
@property ConfigFile opIndex(string configName = "")
|
||||||
|
{
|
||||||
|
if (configName.length == 0)
|
||||||
|
{
|
||||||
|
if (configs.length == 1)
|
||||||
|
return configs[configs.byKey.front];
|
||||||
|
else
|
||||||
|
throw new Exception("More than one configuration file has been read. "
|
||||||
|
~ "It is necessary to specify the name of a specific");
|
||||||
|
}
|
||||||
|
|
||||||
|
return configName in configs ? configs[configName] : ConfigFile(configName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ConfigFile
|
||||||
|
{
|
||||||
|
private string name;
|
||||||
|
private ConfigSection[string] sections;
|
||||||
|
|
||||||
|
@property bool exist()
|
||||||
|
{
|
||||||
|
return this.sections.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the section
|
* Get the section
|
||||||
* Params:
|
* Params:
|
||||||
* section = section name (default main "[]")
|
* section = section name (default main section)
|
||||||
|
* Returns: the object of the configuration file section ConfigSection
|
||||||
*/
|
*/
|
||||||
@property ConfigSection sectionName(string section = "[]")
|
@property ConfigSection sectionName(string section = mainSection)
|
||||||
{
|
{
|
||||||
return section in sections ? sections[section] : ConfigSection();
|
if (!this.exist)
|
||||||
|
throw new Exception("The configuration file does not exist");
|
||||||
|
if (section == mainSection && sections.length == 1)
|
||||||
|
return sections[sections.byKey.front];
|
||||||
|
if (section.length == 0)
|
||||||
|
section = mainSection;
|
||||||
|
return section in sections ? sections[section] : ConfigSection(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Section name
|
|
||||||
*
|
|
||||||
* Get the section
|
* Get the section
|
||||||
* Params:
|
* Params:
|
||||||
* section = section name (default main "[]")
|
* section = section name (default main section)
|
||||||
|
* Returns: the object of the configuration file section ConfigSection
|
||||||
*/
|
*/
|
||||||
alias sn = sectionName;
|
alias sn = sectionName;
|
||||||
|
|
||||||
|
private void add(string sectionName, ConfigParameter parameter)
|
||||||
|
{
|
||||||
|
if (sectionName !in this.sections)
|
||||||
|
this.sections[sectionName] = ConfigSection(sectionName);
|
||||||
|
|
||||||
|
this.sections[sectionName].add(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@property ConfigSection opIndex(string section = mainSection)
|
||||||
|
{
|
||||||
|
if (!this.exist)
|
||||||
|
throw new Exception("The configuration file does not exist");
|
||||||
|
if (section == mainSection && sections.length == 1)
|
||||||
|
return sections[sections.byKey.front];
|
||||||
|
if (section.length == 0)
|
||||||
|
section = mainSection;
|
||||||
|
return section in sections ? sections[section] : ConfigSection(section);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConfigSection
|
struct ConfigSection
|
||||||
{
|
{
|
||||||
private string name = "[]";
|
private string name = mainSection;
|
||||||
private ConfigParameter[string] parameters;
|
private ConfigParameter[string] parameters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,16 +256,20 @@ struct ConfigSection
|
||||||
* Get the parameter value
|
* Get the parameter value
|
||||||
* Params:
|
* Params:
|
||||||
* key = parameter from the configuration file
|
* key = parameter from the configuration file
|
||||||
* Returns: the value of the parameter in the PP structure view
|
* Returns: the object of the parameter ConfigParameter
|
||||||
*/
|
*/
|
||||||
ConfigParameter key(string key)
|
ConfigParameter key(string key)
|
||||||
{
|
{
|
||||||
return key in this.parameters ? this.parameters[key] : ConfigParameter();
|
if (key.length == 0)
|
||||||
|
throw new Exception("The key cannot be empty");
|
||||||
|
if (this.empty)
|
||||||
|
throw new Exception("The selected section has no parameters or does not exist");
|
||||||
|
return key in this.parameters ? this.parameters[key] : ConfigParameter(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all keys and their values
|
* Get all keys and their values
|
||||||
* Returns: collection of properties structures PP
|
* Returns: collection of parameters
|
||||||
*/
|
*/
|
||||||
ConfigParameter[string] keys()
|
ConfigParameter[string] keys()
|
||||||
{
|
{
|
||||||
|
@ -183,14 +279,20 @@ struct ConfigSection
|
||||||
private void add(ConfigParameter parameter)
|
private void add(ConfigParameter parameter)
|
||||||
{
|
{
|
||||||
if (parameter.property in parameters)
|
if (parameter.property in parameters)
|
||||||
Log.msg.warning("The parameter exists but will be overwritten");
|
log.w("The parameter exists but will be overwritten");
|
||||||
this.parameters[parameter.property] = parameter;
|
this.parameters[parameter.property] = parameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConfigParameter opIndex(string key)
|
||||||
|
{
|
||||||
|
if (key.length == 0)
|
||||||
|
throw new Exception("The key cannot be empty");
|
||||||
|
if (this.empty)
|
||||||
|
throw new Exception("The selected section has no parameters or does not exist");
|
||||||
|
return key in this.parameters ? this.parameters[key] : ConfigParameter(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parameter and its value with the ability to convert to the desired data type
|
|
||||||
*/
|
|
||||||
struct ConfigParameter
|
struct ConfigParameter
|
||||||
{
|
{
|
||||||
private string property;
|
private string property;
|
||||||
|
@ -202,7 +304,7 @@ struct ConfigParameter
|
||||||
*/
|
*/
|
||||||
@property bool empty()
|
@property bool empty()
|
||||||
{
|
{
|
||||||
return this.property.length == 0 || this.value.length == 0;
|
return this.value.length == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -221,8 +323,8 @@ struct ConfigParameter
|
||||||
try {
|
try {
|
||||||
return this.value.to!T;
|
return this.value.to!T;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.msg.warning("Cannot convert type");
|
log.w("Cannot convert type");
|
||||||
Log.msg.error(e);
|
log.e(e);
|
||||||
return T.init;
|
return T.init;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
141
tests/test.d
141
tests/test.d
|
@ -1,34 +1,121 @@
|
||||||
import readconf;
|
import readconf;
|
||||||
|
import std.conv;
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
rc.read("./examples/simple.conf");
|
||||||
|
|
||||||
|
auto configFile = rc.cf;
|
||||||
|
auto mainSection = configFile.sn;
|
||||||
|
|
||||||
|
assert(mainSection.key("parameter1") == "value1");
|
||||||
|
assert(mainSection["parameter2"] == "value2");
|
||||||
|
assert(mainSection.key("parameter3") == "value3");
|
||||||
|
assert(mainSection["parameter4"] == "value4");
|
||||||
|
assert(mainSection.key("_parameter5") == "value5");
|
||||||
|
assert(mainSection["parameter6"] == "value6");
|
||||||
|
assert(mainSection.key("parameter7") == "value7");
|
||||||
|
assert(mainSection["parameter8"] == "value8");
|
||||||
|
assert(mainSection.key("parameter9") == "value9");
|
||||||
|
assert(mainSection["parameter-10"] == "value10");
|
||||||
|
assert(mainSection.key("parameter11") == "value11");
|
||||||
|
assert(mainSection["parameter12_"] == "value12");
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
rc.read("./examples/sections.conf");
|
||||||
|
auto configFile = rc.cf;
|
||||||
|
|
||||||
|
auto mainSection = configFile.sn;
|
||||||
|
auto firstSection = configFile.sn("first-section");
|
||||||
|
auto secondSection = configFile["second-section"];
|
||||||
|
auto section = configFile["_section"];
|
||||||
|
|
||||||
|
assert(mainSection.key("parameter1") == "value8");
|
||||||
|
assert(mainSection["parameter_2"] == "value2");
|
||||||
|
assert(mainSection["parameter3"] == "value7");
|
||||||
|
|
||||||
|
assert(firstSection["parameter1"] == "value3");
|
||||||
|
assert(firstSection["parameter_2"] == "value4");
|
||||||
|
assert(firstSection["parameter3"] == "value9");
|
||||||
|
assert(firstSection["parameter4"] == "value10");
|
||||||
|
|
||||||
|
assert(secondSection["parameter1"] == "value5");
|
||||||
|
assert(secondSection["parameter_2"] == "value6");
|
||||||
|
|
||||||
|
assert(section["parameter1"] == "value11");
|
||||||
|
assert(section["parameter2"] == "value12");
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
rc.read("./examples/simple.conf", "simple");
|
||||||
|
rc.read("./examples/sections.conf");
|
||||||
|
rc.read("./examples/comments.conf", "comments");
|
||||||
|
|
||||||
|
auto simpleConfig = rc.cf("simple");
|
||||||
|
auto sectionsConfig = rc["sections.conf"];
|
||||||
|
auto commentsConfig = rc["comments"];
|
||||||
|
|
||||||
|
auto simConMaiSec = simpleConfig.sn;
|
||||||
|
|
||||||
|
assert(simConMaiSec.key("parameter1") == "value1");
|
||||||
|
assert(simConMaiSec["parameter2"] == "value2");
|
||||||
|
assert(simConMaiSec.key("parameter3") == "value3");
|
||||||
|
assert(simConMaiSec["parameter4"] == "value4");
|
||||||
|
assert(simConMaiSec.key("_parameter5") == "value5");
|
||||||
|
assert(simConMaiSec["parameter6"] == "value6");
|
||||||
|
assert(simConMaiSec.key("parameter7") == "value7");
|
||||||
|
assert(simConMaiSec["parameter8"] == "value8");
|
||||||
|
assert(simConMaiSec.key("parameter9") == "value9");
|
||||||
|
assert(simConMaiSec["parameter-10"] == "value10");
|
||||||
|
assert(simConMaiSec.key("parameter11") == "value11");
|
||||||
|
assert(simConMaiSec["parameter12_"] == "value12");
|
||||||
|
|
||||||
|
auto secConMaiSec = sectionsConfig.sn;
|
||||||
|
auto secConFirSec = sectionsConfig.sn("first-section");
|
||||||
|
auto secConSecSec = sectionsConfig["second-section"];
|
||||||
|
auto secConSec = sectionsConfig["_section"];
|
||||||
|
|
||||||
|
assert(secConMaiSec.key("parameter1") == "value8");
|
||||||
|
assert(secConMaiSec["parameter_2"] == "value2");
|
||||||
|
assert(secConMaiSec["parameter3"] == "value7");
|
||||||
|
assert(secConFirSec["parameter1"] == "value3");
|
||||||
|
assert(secConFirSec["parameter_2"] == "value4");
|
||||||
|
assert(secConFirSec["parameter3"] == "value9");
|
||||||
|
assert(secConFirSec["parameter4"] == "value10");
|
||||||
|
assert(secConSecSec["parameter1"] == "value5");
|
||||||
|
assert(secConSecSec["parameter_2"] == "value6");
|
||||||
|
assert(secConSec["parameter1"] == "value11");
|
||||||
|
assert(secConSec["parameter2"] == "value12");
|
||||||
|
|
||||||
|
auto comConMaiSec = commentsConfig.sn;
|
||||||
|
|
||||||
|
assert(comConMaiSec["parameter1"] == "value1");
|
||||||
|
assert(comConMaiSec["parameter2"] == "value2");
|
||||||
|
assert(comConMaiSec["parameter3"] == "value3");
|
||||||
|
assert(comConMaiSec["parameter4"] == "value4");
|
||||||
|
assert(comConMaiSec["parameter5"] == "value5;This will not be a comment");
|
||||||
|
assert(comConMaiSec["parameter6"] == "value6// This will also be a whole value");
|
||||||
|
assert(comConMaiSec["parameter8"] == "//value8");
|
||||||
|
assert(comConMaiSec["parameter9"] == ";value9");
|
||||||
|
assert(comConMaiSec["parameter10"] == "\"value10\"");
|
||||||
|
}
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
rc.read("./tests/settings.conf");
|
rc.read("./tests/settings.conf");
|
||||||
|
|
||||||
assert(rc.sn.key("value1") == "text without quotes");
|
assert(rc.cf.sn.key("value1") == "text without quotes");
|
||||||
assert(rc.sn.key("value2") == "Yes!");
|
assert(rc[][]["value2"] == "Yes!");
|
||||||
assert(rc.sn.key("value3") == "value in apostrophes");
|
assert(rc.cf.sn.key("value3") == "value in apostrophes");
|
||||||
assert(rc.sn.key("value4") == "1000");
|
assert(rc[][]["value4"] == "1000");
|
||||||
assert(rc.sn.key("value5") == "0.000");
|
assert(rc.cf.sn["value5"] == "0.000");
|
||||||
assert(rc.sn.key("value7") == "//path");
|
assert(rc[][].key("value7") == "//path");
|
||||||
assert(rc.sn.key("value8") == "\"Hey!\"");
|
assert(rc.cf.sn.key("value8") == "\"Hey!\"");
|
||||||
assert(rc.sn("part2").key("value1") == "this value will be in the new section");
|
assert(rc[]["part2"]["value1"] == "this value will be in the new section");
|
||||||
assert(rc.sn("part2").key("value3") == "good value!");
|
assert(rc.cf.sn("part2").key("value3") == "good value!");
|
||||||
assert(rc.sn("part3").key("value1") == "-2");
|
assert(rc[].sn("part3").key("value1") == "-2");
|
||||||
assert(rc.sn("part3").key("value3") == "100");
|
assert(rc.cf["part3"]["value3"] == "100");
|
||||||
}
|
}
|
||||||
|
|
||||||
// void main()
|
|
||||||
// {
|
|
||||||
// import std.stdio;
|
|
||||||
// rc.read("./tests/settings.conf");
|
|
||||||
|
|
||||||
// foreach (key, param; rc.sn.keys())
|
|
||||||
// writefln("%s => %s", key, param);
|
|
||||||
|
|
||||||
// writeln(rc.sn.key("value1"));
|
|
||||||
|
|
||||||
// foreach (key, param; rc.sn("part2").keys())
|
|
||||||
// writefln("%s => %s", key, param);
|
|
||||||
|
|
||||||
// writeln(rc.sn("part2").key("value1"));
|
|
||||||
// }
|
|
||||||
|
|
Loading…
Reference in New Issue