readconf/source/readconf.d

340 lines
9.5 KiB
D
Raw Normal View History

2023-03-23 15:10:56 +00:00
module readconf;
import std.stdio, std.conv, std.path, std.file;
import core.stdc.stdlib : exit;
import std.regex;
import std.meta;
import singlog;
2023-03-25 22:45:41 +00:00
/**
2023-03-29 16:41:43 +00:00
* **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
2023-03-25 22:45:41 +00:00
*/
alias rc = Config.file;
2023-03-27 13:39:46 +00:00
private const string mainSection = "[]";
2023-03-23 15:10:56 +00:00
class Config
{
private:
2023-03-25 23:35:08 +00:00
enum {
2023-03-29 16:41:43 +00:00
GROUP_PARAMETER = 4,
GROUP_VALUE_1 = 12, // string
GROUP_VALUE_2 = 15, // "strin"
GROUP_VALUE_3 = 17, // 'string'
GROUP_SECTION_OTHER_OUTER = 19, // [string]
GROUP_SECTION_OTHER_INNER = 20, // string
GROUP_SECTION_MAIN = 23, // []
2023-03-25 23:35:08 +00:00
}
2023-03-23 15:10:56 +00:00
static Config config;
string path;
bool readed = false;
2023-03-26 17:16:46 +00:00
ConfigFile[string] configs;
2023-03-25 23:35:08 +00:00
const string pattern = "^( |\\t)*((((\\w(\\w|-)?)+)(( |\\t)*(=>|=){1}"
~ "( |\\t)*)(?!\\/(\\/|\\*))(([^ >\"'=\\n\\t#;].*?)|(\"(.+)?\")"
~ "|('(.+)?')|()){1})|(\\[((\\w(\\w|-)?)+)\\])|(\\[\\]))( |\\t)*"
2023-03-25 12:37:46 +00:00
~ "(( |\\t)(#|;|\\/\\/|\\/\\*).*)?$";
2023-03-23 15:10:56 +00:00
/**
* Reading the configuration file
*/
2023-03-26 17:16:46 +00:00
bool readConfig(const string configName)
2023-03-23 15:10:56 +00:00
{
File configuration;
try {
configuration = File(this.path, "r");
} catch (Exception e) {
log.w("Unable to open the configuration file " ~ this.path);
log.e(e);
2023-03-25 23:03:03 +00:00
return false;
2023-03-23 15:10:56 +00:00
}
2023-03-26 17:16:46 +00:00
if (configName !in this.configs)
this.configs[configName] = ConfigFile(configName);
2023-03-25 13:36:59 +00:00
auto regular = regex(this.pattern, "m");
2023-03-23 15:10:56 +00:00
2023-03-25 23:35:08 +00:00
// reading from the main section
2023-03-27 13:39:46 +00:00
string sectionName = mainSection;
2023-03-25 21:31:25 +00:00
2023-03-23 15:10:56 +00:00
while (!configuration.eof())
{
string line = configuration.readln();
auto match = matchFirst(line, regular);
2023-03-27 15:52:01 +00:00
if (match.length == 0)
continue;
// if again main section
if (match[GROUP_SECTION_MAIN].length)
{
sectionName = match[GROUP_SECTION_MAIN];
continue;
}
if (match[GROUP_SECTION_OTHER_OUTER].length)
2023-03-23 15:10:56 +00:00
{
2023-03-27 15:52:01 +00:00
sectionName = match[GROUP_SECTION_OTHER_INNER];
continue;
2023-03-25 21:31:25 +00:00
}
2023-03-27 15:52:01 +00:00
int group = GROUP_VALUE_1;
if (match[group].length) {
if (match[group][0] == '\"')
group = GROUP_VALUE_2;
else if (match[group][0] == '\'')
group = GROUP_VALUE_3;
}
2023-03-27 15:52:01 +00:00
2023-03-29 16:41:43 +00:00
this.configs[configName].add(sectionName, ConfigParameter(match[GROUP_PARAMETER], match[group]));
2023-03-23 15:10:56 +00:00
}
try {
configuration.close();
this.readed = true;
} catch (Exception e) {
log.w("Unable to close the configuration file " ~ this.path);
log.e(e);
2023-03-26 17:16:46 +00:00
this.configs.remove(configName);
2023-03-25 23:03:03 +00:00
this.readed = false;
2023-03-23 15:10:56 +00:00
}
2023-03-25 23:03:03 +00:00
return this.readed;
2023-03-23 15:10:56 +00:00
}
this() {}
public:
/**
* Accessing the Config object
* Returns: Config Object
*/
@property static Config file()
{
if (this.config is null)
this.config = new Config;
return this.config;
}
/**
* Read the configuration file
* Params:
* path = the path to the configuration file
2023-03-29 16:41:43 +00:00
* configName = a specific name to bind to the configuration file (default file name)
* Returns: `true` if the file was read successfully
2023-03-23 15:10:56 +00:00
*/
2023-03-26 17:16:46 +00:00
bool read(string path, string configName = "")
2023-03-23 15:10:56 +00:00
{
this.path = path;
if (!path.exists)
2023-03-25 23:35:08 +00:00
throw new Exception("The configuration file does not exist: " ~ path);
2023-03-26 17:16:46 +00:00
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);
2023-03-23 15:10:56 +00:00
}
2023-03-26 17:16:46 +00:00
/**
2023-03-29 16:41:43 +00:00
* Accessing the read configuration file
2023-03-26 17:16:46 +00:00
* Params:
2023-03-29 16:41:43 +00:00
* 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
2023-03-26 17:16:46 +00:00
*/
@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");
}
2023-03-27 13:39:46 +00:00
return configName in configs ? configs[configName] : ConfigFile(configName);
2023-03-26 17:16:46 +00:00
}
2023-03-29 16:41:43 +00:00
/**
* Get the read configuration file
2023-03-26 17:16:46 +00:00
* Params:
2023-03-29 16:41:43 +00:00
* 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
2023-03-26 17:16:46 +00:00
*/
alias cf = configFile;
2023-03-27 15:32:29 +00:00
@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);
}
2023-03-26 17:16:46 +00:00
}
struct ConfigFile
{
2023-03-27 13:39:46 +00:00
private string name;
2023-03-26 17:16:46 +00:00
private ConfigSection[string] sections;
2023-03-27 12:37:37 +00:00
@property bool exist()
{
return this.sections.length > 0;
}
2023-03-25 22:45:41 +00:00
/**
* Get the section
* Params:
2023-03-29 16:41:43 +00:00
* section = section name (default main section)
* Returns: the object of the configuration file section ConfigSection
2023-03-25 22:45:41 +00:00
*/
2023-03-27 13:39:46 +00:00
@property ConfigSection sectionName(string section = mainSection)
2023-03-25 21:31:25 +00:00
{
2023-03-27 12:37:37 +00:00
if (!this.exist)
throw new Exception("The configuration file does not exist");
2023-03-27 13:39:46 +00:00
if (section == mainSection && sections.length == 1)
2023-03-27 12:37:37 +00:00
return sections[sections.byKey.front];
2023-03-27 15:32:29 +00:00
if (section.length == 0)
section = mainSection;
if (section !in sections)
throw new Exception("The selected section does not exist");
return sections[section];
2023-03-25 21:31:25 +00:00
}
2023-03-25 22:45:41 +00:00
/**
* Get the section
* Params:
2023-03-29 16:41:43 +00:00
* section = section name (default main section)
* Returns: the object of the configuration file section ConfigSection
2023-03-25 22:45:41 +00:00
*/
alias sn = sectionName;
2023-03-26 17:16:46 +00:00
private void add(string sectionName, ConfigParameter parameter)
{
if (sectionName !in this.sections)
this.sections[sectionName] = ConfigSection(sectionName);
this.sections[sectionName].add(parameter);
}
2023-03-27 15:32:29 +00:00
@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;
if (section !in sections)
throw new Exception("The selected section does not exist");
return sections[section];
2023-03-27 15:32:29 +00:00
}
2023-03-25 21:31:25 +00:00
}
struct ConfigSection
{
2023-03-27 13:39:46 +00:00
private string name = mainSection;
2023-03-25 21:31:25 +00:00
private ConfigParameter[string] parameters;
/**
* Checking for the presence of a partition
* Returns: true if the parameter is missing, otherwise false
*/
@property bool empty()
2023-03-25 13:36:59 +00:00
{
2023-03-25 21:31:25 +00:00
return this.parameters.length == 0;
}
2023-03-25 13:36:59 +00:00
2023-03-25 21:31:25 +00:00
/**
* Get the parameter value
* Params:
* key = parameter from the configuration file
2023-03-29 16:41:43 +00:00
* Returns: the object of the parameter ConfigParameter
2023-03-25 21:31:25 +00:00
*/
ConfigParameter key(string key)
{
2023-03-27 15:32:29 +00:00
if (key.length == 0)
throw new Exception("The key cannot be empty");
2023-03-27 12:37:37 +00:00
if (this.empty)
throw new Exception("The selected section has no parameters or does not exist");
2023-03-27 13:39:46 +00:00
return key in this.parameters ? this.parameters[key] : ConfigParameter(key);
2023-03-25 21:31:25 +00:00
}
/**
* Get all keys and their values
2023-03-29 16:41:43 +00:00
* Returns: collection of parameters
2023-03-25 21:31:25 +00:00
*/
ConfigParameter[string] keys()
{
return this.parameters;
}
2023-03-25 23:03:03 +00:00
private void add(ConfigParameter parameter)
2023-03-25 21:31:25 +00:00
{
2023-03-25 23:03:03 +00:00
if (parameter.property in parameters)
log.w("The parameter exists but will be overwritten");
2023-03-25 21:31:25 +00:00
this.parameters[parameter.property] = parameter;
2023-03-25 13:36:59 +00:00
}
2023-03-27 15:32:29 +00:00
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);
}
2023-03-25 13:36:59 +00:00
}
2023-03-25 21:31:25 +00:00
struct ConfigParameter
2023-03-25 13:36:59 +00:00
{
private string property;
private string value;
/**
* Checking for the presence of a parameter
* Returns: true if the parameter is missing, otherwise false
*/
@property bool empty()
{
2023-03-27 13:39:46 +00:00
return this.value.length == 0;
2023-03-25 13:36:59 +00:00
}
/**
* Get a string representation of the value
* Returns: default string value
*/
@property string toString() const
{
return this.value;
}
alias toString this;
auto opCast(T)() const
{
try {
return this.value.to!T;
} catch (Exception e) {
log.w("Cannot convert type");
log.e(e);
2023-03-25 13:36:59 +00:00
return T.init;
2024-02-09 15:47:54 +00:00
}
2023-03-25 13:36:59 +00:00
}
}