June 2018 Update
This commit is contained in:
parent
ba8067c3b7
commit
22f33d4004
5278 changed files with 84726 additions and 14379 deletions
|
|
@ -1,4 +1,4 @@
|
|||
The task is to read a configuration file in standard configuration file,
|
||||
The task is to read a configuration file in standard configuration file format,
|
||||
and set variables accordingly.
|
||||
|
||||
For this task, we have a configuration file as follows:
|
||||
|
|
|
|||
|
|
@ -6,60 +6,53 @@ text an, d, k;
|
|||
|
||||
an = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
f_affix(f, "tmp/config");
|
||||
f.affix("tmp/config");
|
||||
|
||||
while ((c = f_peek(f)) ^ -1) {
|
||||
while ((c = f.peek) ^ -1) {
|
||||
integer removed;
|
||||
|
||||
f_side(f, " \t\r");
|
||||
c = f_peek(f);
|
||||
f.side(" \t\r");
|
||||
c = f.peek;
|
||||
removed = c == ';';
|
||||
if (removed) {
|
||||
f_pick(f);
|
||||
f_side(f, " \t\r");
|
||||
c = f_peek(f);
|
||||
f.pick;
|
||||
f.side(" \t\r");
|
||||
c = f.peek;
|
||||
}
|
||||
c = index(an, c);
|
||||
c = place(an, c);
|
||||
if (-1 < c && c < 52) {
|
||||
f_near(f, an, k);
|
||||
f.near(an, k);
|
||||
if (removed) {
|
||||
r[k] = "false";
|
||||
} else {
|
||||
data b;
|
||||
|
||||
f_side(f, " \t\r");
|
||||
if (f_peek(f) == '=') {
|
||||
f_pick(f);
|
||||
f_side(f, " \t\r");
|
||||
f.side(" \t\r");
|
||||
if (f.peek == '=') {
|
||||
f.pick;
|
||||
f.side(" \t\r");
|
||||
}
|
||||
f_ever(f, ",#\n", d);
|
||||
b = d;
|
||||
bb_drop(b, " \r\t");
|
||||
d = b_string(b);
|
||||
if (f_peek(f) != ',') {
|
||||
r[k] = length(d) ? d : "true";
|
||||
f.ever(",#\n", d);
|
||||
d = bb_drop(d, " \r\t");
|
||||
if (f.peek != ',') {
|
||||
r[k] = ~d ? d : "true";
|
||||
} else {
|
||||
f_news(f, l, 0, 0, ",");
|
||||
f.news(l, 0, 0, ",");
|
||||
lf_push(l, d);
|
||||
for (c, d in l) {
|
||||
b = d;
|
||||
bb_drop(b, " \r\t");
|
||||
bf_drop(b, " \r\t");
|
||||
l[c] = b_string(b);
|
||||
l[c] = bb_drop(d, " \r\t").bf_drop(" \r\t").string;
|
||||
}
|
||||
r_put(s, k, l);
|
||||
f_seek(f, -1, SEEK_CURRENT);
|
||||
s.put(k, l);
|
||||
f.seek(-1, SEEK_CURRENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f_slip(f);
|
||||
f.slip;
|
||||
}
|
||||
|
||||
r_wcall(r, o_, 0, 2, ": ", "\n");
|
||||
r.wcall(o_, 0, 2, ": ", "\n");
|
||||
|
||||
for (k, l in s) {
|
||||
o_(k, ": ");
|
||||
l_ucall(l, o_, 0, ", ");
|
||||
l.ucall(o_, 0, ", ");
|
||||
o_("\n");
|
||||
}
|
||||
|
|
|
|||
171
Task/Read-a-configuration-file/C/read-a-configuration-file.c
Normal file
171
Task/Read-a-configuration-file/C/read-a-configuration-file.c
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <confini.h>
|
||||
|
||||
#define rosetta_uint8_t unsigned char
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
|
||||
#define CONFIGS_TO_READ 5
|
||||
#define INI_ARRAY_DELIMITER ','
|
||||
|
||||
/* Assume that the config file represent a struct containing all the parameters to load */
|
||||
struct configs {
|
||||
char *fullname;
|
||||
char *favouritefruit;
|
||||
rosetta_uint8_t needspeeling;
|
||||
rosetta_uint8_t seedsremoved;
|
||||
char **otherfamily;
|
||||
size_t otherfamily_len;
|
||||
size_t _configs_left_;
|
||||
};
|
||||
|
||||
static char ** make_array (size_t * arrlen, const char * src, const size_t buffsize, IniFormat ini_format) {
|
||||
|
||||
/* Allocate a new array of strings and populate it from the stringified source */
|
||||
*arrlen = ini_array_get_length(src, INI_ARRAY_DELIMITER, ini_format);
|
||||
char ** const dest = *arrlen ? (char **) malloc(*arrlen * sizeof(char *) + buffsize) : NULL;
|
||||
if (!dest) { return NULL; }
|
||||
memcpy(dest + *arrlen, src, buffsize);
|
||||
char * iter = (char *) (dest + *arrlen);
|
||||
for (size_t idx = 0; idx < *arrlen; idx++) {
|
||||
dest[idx] = ini_array_release(&iter, INI_ARRAY_DELIMITER, ini_format);
|
||||
ini_string_parse(dest[idx], ini_format);
|
||||
}
|
||||
return dest;
|
||||
|
||||
}
|
||||
|
||||
static int configs_member_handler (IniDispatch *this, void *v_confs) {
|
||||
|
||||
struct configs *confs = (struct configs *) v_confs;
|
||||
|
||||
if (this->type != INI_KEY) {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
if (ini_string_match_si("FULLNAME", this->data, this->format)) {
|
||||
|
||||
if (confs->fullname) { return 0; }
|
||||
this->v_len = ini_string_parse(this->value, this->format); /* Remove all quotes, if any */
|
||||
confs->fullname = strndup(this->value, this->v_len);
|
||||
confs->_configs_left_--;
|
||||
|
||||
} else if (ini_string_match_si("FAVOURITEFRUIT", this->data, this->format)) {
|
||||
|
||||
if (confs->favouritefruit) { return 0; }
|
||||
this->v_len = ini_string_parse(this->value, this->format); /* Remove all quotes, if any */
|
||||
confs->favouritefruit = strndup(this->value, this->v_len);
|
||||
confs->_configs_left_--;
|
||||
|
||||
} else if (ini_string_match_si("NEEDSPEELING", this->data, this->format)) {
|
||||
|
||||
if (~confs->needspeeling & 0x80) { return 0; }
|
||||
confs->needspeeling = ini_get_bool(this->value, TRUE);
|
||||
confs->_configs_left_--;
|
||||
|
||||
} else if (ini_string_match_si("SEEDSREMOVED", this->data, this->format)) {
|
||||
|
||||
if (~confs->seedsremoved & 0x80) { return 0; }
|
||||
confs->seedsremoved = ini_get_bool(this->value, TRUE);
|
||||
confs->_configs_left_--;
|
||||
|
||||
} else if (!confs->otherfamily && ini_string_match_si("OTHERFAMILY", this->data, this->format)) {
|
||||
|
||||
if (confs->otherfamily) { return 0; }
|
||||
this->v_len = ini_array_collapse(this->value, INI_ARRAY_DELIMITER, this->format); /* Save memory (not strictly needed) */
|
||||
confs->otherfamily = make_array(&confs->otherfamily_len, this->value, this->v_len + 1, this->format);
|
||||
confs->_configs_left_--;
|
||||
|
||||
}
|
||||
|
||||
/* Optimization: stop reading the INI file when we have all we need */
|
||||
return !confs->_configs_left_;
|
||||
|
||||
}
|
||||
|
||||
static int populate_configs (struct configs * confs) {
|
||||
|
||||
/* Define the format of the configuration file */
|
||||
IniFormat config_format = {
|
||||
.delimiter_symbol = INI_ANY_SPACE,
|
||||
.case_sensitive = FALSE,
|
||||
.semicolon_marker = INI_IGNORE,
|
||||
.hash_marker = INI_IGNORE,
|
||||
.multiline_nodes = INI_NO_MULTILINE,
|
||||
.section_paths = INI_NO_SECTIONS,
|
||||
.no_single_quotes = FALSE,
|
||||
.no_double_quotes = FALSE,
|
||||
.no_spaces_in_names = TRUE,
|
||||
.implicit_is_not_empty = TRUE,
|
||||
.do_not_collapse_values = FALSE,
|
||||
.preserve_empty_quotes = FALSE,
|
||||
.no_disabled_after_space = FALSE,
|
||||
.disabled_can_be_implicit = FALSE
|
||||
};
|
||||
|
||||
*confs = (struct configs) { NULL, NULL, 0x80, 0x80, NULL, 0, CONFIGS_TO_READ };
|
||||
|
||||
if (load_ini_path("rosetta.conf", config_format, NULL, configs_member_handler, confs) & CONFINI_ERROR) {
|
||||
|
||||
fprintf(stderr, "Sorry, something went wrong :-(\n");
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
confs->needspeeling &= 0x7F;
|
||||
confs->seedsremoved &= 0x7F;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int main () {
|
||||
|
||||
struct configs confs;
|
||||
|
||||
ini_global_set_implicit_value("YES", 0);
|
||||
|
||||
if (populate_configs(&confs)) {
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* Print the configurations parsed */
|
||||
|
||||
printf(
|
||||
|
||||
"Full name: %s\n"
|
||||
"Favorite fruit: %s\n"
|
||||
"Need spelling: %s\n"
|
||||
"Seeds removed: %s\n",
|
||||
|
||||
confs.fullname,
|
||||
confs.favouritefruit,
|
||||
confs.needspeeling ? "True" : "False",
|
||||
confs.seedsremoved ? "True" : "False"
|
||||
|
||||
);
|
||||
|
||||
for (size_t idx = 0; idx < confs.otherfamily_len; idx++) {
|
||||
|
||||
printf("Other family[%d]: %s\n", idx, confs.otherfamily[idx]);
|
||||
|
||||
}
|
||||
|
||||
/* Free the allocated memory */
|
||||
|
||||
#define FREE_NON_NULL(PTR) if (PTR) { free(PTR); }
|
||||
|
||||
FREE_NON_NULL(confs.fullname);
|
||||
FREE_NON_NULL(confs.favouritefruit);
|
||||
FREE_NON_NULL(confs.otherfamily);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
|
@ -1,2 +1,48 @@
|
|||
(let [cfg (read-string (slurp "config.edn"))]
|
||||
(clojure.pprint/pprint cfg))
|
||||
(ns read-conf-file.core
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :as str])
|
||||
(:gen-class))
|
||||
|
||||
(def conf-keys ["fullname"
|
||||
"favouritefruit"
|
||||
"needspeeling"
|
||||
"seedsremoved"
|
||||
"otherfamily"])
|
||||
|
||||
(defn get-lines
|
||||
"Read file returning vec of lines."
|
||||
[file]
|
||||
(try
|
||||
(with-open [rdr (io/reader file)]
|
||||
(into [] (line-seq rdr)))
|
||||
(catch Exception e (.getMessage e))))
|
||||
|
||||
(defn parse-line
|
||||
"Parse passed line returning vec: token, vec of values."
|
||||
[line]
|
||||
(if-let [[_ k v] (re-matches #"(?i)^\s*([a-z]+)(?:\s+|=)?(.+)?$" line)]
|
||||
(let [k (str/lower-case k)]
|
||||
(if v
|
||||
[k (str/split v #",\s*")]
|
||||
[k [true]]))))
|
||||
|
||||
(defn mk-conf
|
||||
"Build configuration map from lines."
|
||||
[lines]
|
||||
(->> (map parse-line lines)
|
||||
(filter (comp not nil?))
|
||||
(reduce (fn
|
||||
[m [k v]]
|
||||
(assoc m k v)) {})))
|
||||
|
||||
(defn output
|
||||
[conf-keys conf]
|
||||
(doseq [k conf-keys]
|
||||
(let [v (get conf k)]
|
||||
(if v
|
||||
(println (format "%s = %s" k (str/join ", " v)))
|
||||
(println (format "%s = %s" k "false"))))))
|
||||
|
||||
(defn -main
|
||||
[filename]
|
||||
(output conf-keys (mk-conf (get-lines filename))))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,40 @@
|
|||
;config-file.txt
|
||||
;lisp comments works normally as it would in lisp
|
||||
#S(config-file
|
||||
:fullname "Foo Barber"
|
||||
:favoritefruit "banana"
|
||||
:needspeeling t
|
||||
:seedsremoved nil
|
||||
:otherfamily '("Rhu Barber" "Harry Barber")
|
||||
;:will "not be read"
|
||||
)
|
||||
(ql:quickload :parser-combinators)
|
||||
|
||||
(defpackage :read-config
|
||||
(:use :cl :parser-combinators))
|
||||
|
||||
(in-package :read-config)
|
||||
|
||||
(defun trim-space (string)
|
||||
(string-trim '(#\space #\tab) string))
|
||||
|
||||
(defun any-but1? (except)
|
||||
(named-seq? (<- res (many1? (except? (item) except)))
|
||||
(coerce res 'string)))
|
||||
|
||||
(defun values? ()
|
||||
(named-seq? (<- values (sepby? (any-but1? #\,) #\,))
|
||||
(mapcar 'trim-space values)))
|
||||
|
||||
(defun key-values? ()
|
||||
(named-seq? (<- key (word?))
|
||||
(opt? (many? (whitespace?)))
|
||||
(opt? #\=)
|
||||
(<- values (values?))
|
||||
(cons key (or (if (cdr values) values (car values)) t))))
|
||||
|
||||
(defun parse-line (line)
|
||||
(setf line (trim-space line))
|
||||
(if (or (string= line "") (member (char line 0) '(#\# #\;)))
|
||||
:comment
|
||||
(parse-string* (key-values?) line)))
|
||||
|
||||
(defun parse-config (stream)
|
||||
(let ((hash (make-hash-table :test 'equal)))
|
||||
(loop for line = (read-line stream nil nil)
|
||||
while line
|
||||
do (let ((parsed (parse-line line)))
|
||||
(cond ((eq parsed :comment))
|
||||
((eq parsed nil) (error "config parser error: ~a" line))
|
||||
(t (setf (gethash (car parsed) hash) (cdr parsed))))))
|
||||
hash))
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
;config-file.lisp
|
||||
(defstruct config-file :fullname :favoritefruit :needspeeling :seedsremoved :otherfamily)
|
||||
(with-open-file (in "config-file.txt")
|
||||
(defvar contents (read in))
|
||||
(format t "~a~%" contents)
|
||||
;reading the config-file into a structure gives us
|
||||
;some helper functions to access individualy each element
|
||||
(format t "Fullname: ~a~%" (config-file-fullname contents))
|
||||
(format t "Contents is a config-file? ~a~%" (config-file-p contents)))
|
||||
READ-CONFIG> (with-open-file (s "test.cfg") (parse-config s))
|
||||
#<HASH-TABLE :TEST EQUAL :COUNT 4 {100BD25B43}>
|
||||
READ-CONFIG> (maphash (lambda (k v) (print (list k v))) *)
|
||||
|
||||
("FULLNAME" "Foo Barber")
|
||||
("FAVOURITEFRUIT" "banana")
|
||||
("NEEDSPEELING" T)
|
||||
("OTHERFAMILY" ("Rhu Barber" "Harry Barber"))
|
||||
NIL
|
||||
READ-CONFIG> (gethash "SEEDSREMOVED" **)
|
||||
NIL
|
||||
NIL
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
function readconf(file)
|
||||
vars = Dict()
|
||||
for line in eachline(file)
|
||||
line = strip(line)
|
||||
if !isempty(line) && !startswith(line, '#') && !startswith(line, ';')
|
||||
fspace = searchindex(line, " ")
|
||||
if fspace == 0
|
||||
vars[Symbol(lowercase(line))] = true
|
||||
else
|
||||
vname, line = Symbol(lowercase(line[1:fspace-1])), line[fspace+1:end]
|
||||
value = ',' ∈ line ? strip.(split(line, ',')) : line
|
||||
vars[vname] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
for (vname, value) in vars
|
||||
eval(:($vname = $value))
|
||||
end
|
||||
return vars
|
||||
end
|
||||
|
||||
readconf("test.conf")
|
||||
|
||||
@show fullname favouritefruit needspeeling otherfamily
|
||||
|
|
@ -17,7 +17,7 @@ grammar ConfFile {
|
|||
[ \n || { die "Parse failed at line $*linenum" } ]
|
||||
}
|
||||
|
||||
proto token line() {{*}}
|
||||
proto token line() {*}
|
||||
|
||||
token line:misc { {} (\S+) { die "Unrecognized word: $0" } }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
Function Read-ConfigurationFile {
|
||||
[CmdletBinding()]
|
||||
[OutputType([Collections.Specialized.OrderedDictionary])]
|
||||
Param (
|
||||
[Parameter(
|
||||
Mandatory=$true,
|
||||
Position=0
|
||||
)
|
||||
]
|
||||
[Alias('LiteralPath')]
|
||||
[ValidateScript({
|
||||
Test-Path -LiteralPath $PSItem -PathType 'Leaf'
|
||||
})
|
||||
]
|
||||
[String]
|
||||
$_LiteralPath
|
||||
)
|
||||
|
||||
Begin {
|
||||
Function Houdini-Value ([String]$_Text) {
|
||||
$__Aux = $_Text.Trim()
|
||||
If ($__Aux.Length -eq 0) {
|
||||
$__Aux = $true
|
||||
} ElseIf ($__Aux.Contains(',')) {
|
||||
$__Aux = $__Aux.Split(',') |
|
||||
ForEach-Object {
|
||||
If ($PSItem.Trim().Length -ne 0) {
|
||||
$PSItem.Trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
Return $__Aux
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
$__Configuration = [Ordered]@{}
|
||||
# Config equivalent pattern
|
||||
# Select-String -Pattern '^\s*[^\s;#=]+.*\s*$' -LiteralPath '.\filename.cfg'
|
||||
Switch -Regex -File $_LiteralPath {
|
||||
|
||||
'^\s*[;#=]|^\s*$' {
|
||||
Write-Verbose -Message "v$(' '*20)ignored"
|
||||
Write-Verbose -Message $Matches[0]
|
||||
Continue
|
||||
}
|
||||
|
||||
'^([^=]+)=(.*)$' {
|
||||
Write-Verbose -Message '↓← ← ← ← ← ← ← ← ← ← equal pattern'
|
||||
Write-Verbose -Message $Matches[0]
|
||||
$__Name,$__Value = $Matches[1..2]
|
||||
$__Configuration[$__Name.Trim()] = Houdini-Value($__Value)
|
||||
Continue
|
||||
}
|
||||
|
||||
'^\s*([^\s;#=]+)(.*)(\s*)$' {
|
||||
Write-Verbose -Message '↓← ← ← ← ← ← ← ← ← ← space or tab pattern or only name'
|
||||
Write-Verbose -Message $Matches[0]
|
||||
$__Name,$__Value = $Matches[1..2]
|
||||
$__Configuration[$__Name.Trim()] = Houdini-Value($__Value)
|
||||
Continue
|
||||
}
|
||||
|
||||
}
|
||||
Return $__Configuration
|
||||
}
|
||||
}
|
||||
|
||||
Function Show-Value ([Collections.Specialized.OrderedDictionary]$_Dictionary, $_Index, $_SubIndex) {
|
||||
$__Aux = $_Index + ' = '
|
||||
If ($_Dictionary[$_Index] -eq $null) {
|
||||
$__Aux += $false
|
||||
} ElseIf ($_Dictionary[$_Index].Count -gt 1) {
|
||||
If ($_SubIndex -eq $null) {
|
||||
$__Aux += $_Dictionary[$_Index] -join ','
|
||||
} Else {
|
||||
$__Aux = $_Index + '(' + $_SubIndex + ') = '
|
||||
If ($_Dictionary[$_Index][$_SubIndex] -eq $null) {
|
||||
$__Aux += $false
|
||||
} Else {
|
||||
$__Aux += $_Dictionary[$_Index][$_SubIndex]
|
||||
}
|
||||
}
|
||||
} Else {
|
||||
$__Aux += $_Dictionary[$_Index]
|
||||
}
|
||||
Return $__Aux
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
$Configuration = Read-ConfigurationFile -LiteralPath '.\config.cfg'
|
||||
|
|
@ -0,0 +1 @@
|
|||
$Configuration
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
Show-Value $Configuration 'fullname'
|
||||
Show-Value $Configuration 'favouritefruit'
|
||||
Show-Value $Configuration 'needspeeling'
|
||||
Show-Value $Configuration 'seedsremoved'
|
||||
Show-Value $Configuration 'otherfamily'
|
||||
Show-Value $Configuration 'otherfamily' 0
|
||||
Show-Value $Configuration 'otherfamily' 1
|
||||
Show-Value $Configuration 'otherfamily' 2
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
'$Configuration[''fullname'']'
|
||||
$Configuration['fullname']
|
||||
'$Configuration.''fullname'''
|
||||
$Configuration.'fullname'
|
||||
'$Configuration.Item(''fullname'')'
|
||||
$Configuration.Item('fullname')
|
||||
'$Configuration[0]'
|
||||
$Configuration[0]
|
||||
'$Configuration.Item(0)'
|
||||
$Configuration.Item(0)
|
||||
' '
|
||||
'=== $Configuration[''otherfamily''] ==='
|
||||
$Configuration['otherfamily']
|
||||
'=== $Configuration[''otherfamily''][0] ==='
|
||||
$Configuration['otherfamily'][0]
|
||||
'=== $Configuration[''otherfamily''][1] ==='
|
||||
$Configuration['otherfamily'][1]
|
||||
' '
|
||||
'=== $Configuration.''otherfamily'' ==='
|
||||
$Configuration.'otherfamily'
|
||||
'=== $Configuration.''otherfamily''[0] ==='
|
||||
$Configuration.'otherfamily'[0]
|
||||
'=== $Configuration.''otherfamily''[1] ==='
|
||||
$Configuration.'otherfamily'[1]
|
||||
' '
|
||||
'=== $Configuration.Item(''otherfamily'') ==='
|
||||
$Configuration.Item('otherfamily')
|
||||
'=== $Configuration.Item(''otherfamily'')[0] ==='
|
||||
$Configuration.Item('otherfamily')[0]
|
||||
'=== $Configuration.Item(''otherfamily'')[1] ==='
|
||||
$Configuration.Item('otherfamily')[1]
|
||||
' '
|
||||
'=== $Configuration[3] ==='
|
||||
$Configuration[3]
|
||||
'=== $Configuration[3][0] ==='
|
||||
$Configuration[3][0]
|
||||
'=== $Configuration[3][1] ==='
|
||||
$Configuration[3][1]
|
||||
' '
|
||||
'=== $Configuration.Item(3) ==='
|
||||
$Configuration.Item(3)
|
||||
'=== $Configuration.Item(3).Item(0) ==='
|
||||
$Configuration.Item(3).Item(0)
|
||||
'=== $Configuration.Item(3).Item(1) ==='
|
||||
$Configuration.Item(3).Item(1)
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
use std::fs::File;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
use std::iter::FromIterator;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let path = String::from("file.conf");
|
||||
let cfg = config_from_file(path);
|
||||
println!("{:?}", cfg);
|
||||
}
|
||||
|
||||
fn config_from_file(path: String) -> Config {
|
||||
let path = Path::new(&path);
|
||||
let file = File::open(path).expect("File not found or cannot be opened");
|
||||
let content = BufReader::new(&file);
|
||||
let mut cfg = Config::new();
|
||||
|
||||
for line in content.lines() {
|
||||
let line = line.expect("Could not read the line");
|
||||
// Remove whitespaces at the beginning and end
|
||||
let line = line.trim();
|
||||
|
||||
// Ignore comments and empty lines
|
||||
if line.starts_with("#") || line.starts_with(";") || line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Split line into parameter name and rest tokens
|
||||
let tokens = Vec::from_iter(line.split_whitespace());
|
||||
let name = tokens.first().unwrap();
|
||||
let tokens = tokens.get(1..).unwrap();
|
||||
|
||||
// Remove the equal signs
|
||||
let tokens = tokens.iter().filter(|t| !t.starts_with("="));
|
||||
// Remove comment after the parameters
|
||||
let tokens = tokens.take_while(|t| !t.starts_with("#") && !t.starts_with(";"));
|
||||
|
||||
// Concat back the parameters into one string to split for separated parameters
|
||||
let mut parameters = String::new();
|
||||
tokens.for_each(|t| { parameters.push_str(t); parameters.push(' '); });
|
||||
// Splits the parameters and trims
|
||||
let parameters = parameters.split(',').map(|s| s.trim());
|
||||
// Converts them from Vec<&str> into Vec<String>
|
||||
let parameters: Vec<String> = parameters.map(|s| s.to_string()).collect();
|
||||
|
||||
// Setting the config parameters
|
||||
match name.to_lowercase().as_str() {
|
||||
"fullname" => cfg.full_name = parameters.get(0).cloned(),
|
||||
"favouritefruit" => cfg.favourite_fruit = parameters.get(0).cloned(),
|
||||
"needspeeling" => cfg.needs_peeling = true,
|
||||
"seedsremoved" => cfg.needs_peeling = true,
|
||||
"otherfamily" => cfg.other_family = Some(parameters),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
cfg
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Config {
|
||||
full_name: Option<String>,
|
||||
favourite_fruit: Option<String>,
|
||||
needs_peeling: bool,
|
||||
seeds_removed: bool,
|
||||
other_family: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn new() -> Config {
|
||||
Config {
|
||||
full_name: None,
|
||||
favourite_fruit: None,
|
||||
needs_peeling: false,
|
||||
seeds_removed: false,
|
||||
other_family: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue