commit f1daabbc9459d044fbe75abc8f9bfe69a65bbb79 Author: Alexander Zhirov Date: Thu Mar 23 18:10:56 2023 +0300 v0.0.1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a55aef --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vscode +.dub +*.o +lib diff --git a/README.md b/README.md new file mode 100644 index 0000000..471accd --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +# readconf + +Singleton for reading the configuration file required for your program. + +The `settings.conf` file: + +```conf +Such a line will not be read +value1 = This is the full value +value2 = "Take the value in quotation marks" +value3 = 'Or take in apostrophes' +value4 => You can also comment // Another separator and comment +value5 => 'So you can also comment' # Yeah! +value6 => 'And you can even do that!' ; He-he;) +value7 = 1234567890 # decimal value +value8 => 12345.67890 ; float value +value9 => You can use large margins +value10 = // But a line without a value will not be read +value11 = //path # not working +value12 = "//path" // nice way (or '//path') +``` + +Read `settings.conf` file: + +```d +import readconf; +import std.stdio; + +void main() +{ + Config.file.read("./settings.conf"); + + foreach (key, param; Config.file.keys()) + writefln("%s => %s", key, param); + + int val7Int = Config.file.key("value7").toInt; + float val8Float = Config.file.key("value8").toFloat; + // Return default value as 0 + int val8Int = Config.file.key("value8").toInt; + float val5Float = Config.file.key("value9").toFloat; + + writefln( + "val7Int = %s; val8Float = %s; val8Int = %s; val5Float = %s;", + val7Int, val8Float, val8Int, val5Float + ); +} +``` + +Result: + +![matches.png](img/matches.png) + +``` +value1 => This is the full value +value2 => Take the value in quotation marks +value3 => Or take in apostrophes +value4 => You can also comment +value5 => So you can also comment +value6 => And you can even do that! +value7 => 1234567890 +value8 => 12345.67890 +value9 => You can use large margins +value12 => //path +val7Int = 1234567890; val8Float = 12345.7; val8Int = 0; val5Float = 0; +``` diff --git a/dub.json b/dub.json new file mode 100644 index 0000000..2408d48 --- /dev/null +++ b/dub.json @@ -0,0 +1,17 @@ +{ + "name": "readconf", + "authors": [ + "Alexander Zhirov " + ], + "homepage": "https://git.zhirov.kz/dlang/readconf.git", + "license": "GPL-2.0", + "copyright": "© Alexander Zhirov, 2023", + "description": "Singleton for simple reading of the configuration file", + "targetType": "library", + "targetPath": "lib", + "targetName": "readconf", + "dependencies": { + "singlog": "~>0.1.0" + }, + "version": "0.0.1" +} diff --git a/dub.selections.json b/dub.selections.json new file mode 100644 index 0000000..716e57f --- /dev/null +++ b/dub.selections.json @@ -0,0 +1,7 @@ +{ + "fileVersion": 1, + "versions": { + "datefmt": "1.0.4", + "singlog": "0.1.0" + } +} diff --git a/dub.settings.json b/dub.settings.json new file mode 100644 index 0000000..72b16b2 --- /dev/null +++ b/dub.settings.json @@ -0,0 +1,4 @@ +{ + "defaultArchitecture": "x86_64", + "defaultCompiler": "ldc2" +} diff --git a/img/matches.png b/img/matches.png new file mode 100644 index 0000000..8f05290 Binary files /dev/null and b/img/matches.png differ diff --git a/source/readconf.d b/source/readconf.d new file mode 100644 index 0000000..f4ebc37 --- /dev/null +++ b/source/readconf.d @@ -0,0 +1,174 @@ +module readconf; + +import std.stdio, std.conv, std.path, std.file; +import core.stdc.stdlib : exit; +import std.regex; +import std.meta; +import singlog; + +class Config +{ +private: + static Config config; + string path; + PP[string] properties; + bool readed = false; + + /** + * Parameter and its value with the ability to convert to the desired data type + */ + struct PP + { + 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() + { + return this.property.length = 0; + } + + /** + * Get an integer value + * Returns: integer value or 0 if missing + */ + @property int toInt() const + { + try { + return to!int(this.value); + } catch (Exception) { + Log.msg.warning("Failed to convert parameter to integer type: " ~ this.property); + return 0; + } + } + + /** + * Get a floating point value + * Returns: floating point value or 0.0 if missing + */ + @property float toFloat() const + { + try { + return to!float(this.value); + } catch (Exception) { + Log.msg.warning("Failed to convert parameter to float type: " ~ this.property); + return 0.0; + } + } + + /** + * Get a string representation of the value + * Returns: default string value + */ + @property string toString() const + { + return value; + } + + alias toString this; + } + + /** + * Reading the configuration file + */ + void readConfig() + { + File configuration; + + try { + configuration = File(this.path, "r"); + } catch (Exception e) { + Log.msg.error("Unable to open the configuration file " ~ this.path); + Log.msg.warning(e); + return; + } + + string pattern = "^ *(\\w+)(( +=> +)|( += +))(?!\\/\\/)(([^ >\"'\\n#;].*?)| + (\"(.+?)\")|('(.+?)')){1} *( #.*?)?( ;.*?)?( \\/\\/.*)?$"; + auto regular = regex(pattern, "m"); + + while (!configuration.eof()) + { + string line = configuration.readln(); + auto match = matchFirst(line, regular); + if (match) + { + int group = 5; + if (match[group].length) + { + if (match[group][0] == '\'') + group = 10; + if (match[group][0] == '\"') + group = 8; + } + this.properties[match[1]] = PP(match[1], match[group]); + } + } + + try { + configuration.close(); + this.readed = true; + } catch (Exception e) { + Log.msg.error("Unable to close the configuration file " ~ this.path); + Log.msg.warning(e); + return; + } + } + + 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 + */ + void read(string path) + { + this.path = path; + if (!path.exists) + { + Log.msg.error("The configuration file does not exist: " ~ path); + return; + } + readConfig(); + } + + /** + * Get the parameter value + * Params: + * key = parameter from the configuration file + * Returns: the value of the parameter in the PP structure view + */ + PP key(string key) + { + if (this.readed) + return key in this.properties ? this.properties[key] : PP(); + Log.msg.warning("The configuration file was not read!"); + return PP(); + } + + /** + * Get all keys and their values + * Returns: collection of properties structures PP + */ + PP[string] keys() + { + return this.properties; + } +}