Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
68292c2b
Commit
68292c2b
authored
Oct 12, 2015
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement and test parse_config function
parent
7dc0fd2e
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
207 additions
and
86 deletions
+207
-86
libcaf_core/caf/detail/parse_ini.hpp
libcaf_core/caf/detail/parse_ini.hpp
+4
-2
libcaf_core/caf/parse_config.hpp
libcaf_core/caf/parse_config.hpp
+16
-3
libcaf_core/src/parse_config.cpp
libcaf_core/src/parse_config.cpp
+46
-40
libcaf_core/src/parse_ini.cpp
libcaf_core/src/parse_ini.cpp
+5
-3
libcaf_core/test/parse_ini.cpp
libcaf_core/test/parse_ini.cpp
+136
-38
No files found.
libcaf_core/caf/detail/parse_ini.hpp
View file @
68292c2b
...
...
@@ -22,6 +22,7 @@
#include <istream>
#include "caf/optional.hpp"
#include "caf/parse_config.hpp"
namespace
caf
{
...
...
@@ -32,8 +33,9 @@ namespace detail {
/// @param raw_data Input stream of INI formatted text.
/// @param errors Output stream for parser errors.
/// @param consumer Callback consuming generated key-value pairs.
void
parse_ini
(
std
::
istream
&
raw_data
,
std
::
ostream
&
errors
,
config_consumer
consumer
);
void
parse_ini
(
std
::
istream
&
raw_data
,
config_consumer
consumer
,
optional
<
std
::
ostream
&>
errors
=
none
);
}
// namespace detail
}
// namespace caf
...
...
libcaf_core/caf/parse_config.hpp
View file @
68292c2b
...
...
@@ -21,15 +21,16 @@
#define CAF_PARSER_CONFIG_HPP
#include <string>
#include <istream>
#include <algorithm>
#include "caf/variant.hpp"
#include "caf/optional.hpp"
namespace
caf
{
/// Denotes the format of a configuration file.
enum
class
config_format
{
auto_detect
,
ini
};
...
...
@@ -39,9 +40,21 @@ using config_value = variant<std::string, double, int64_t, bool>;
/// Denotes a callback for config parser implementations.
using
config_consumer
=
std
::
function
<
void
(
std
::
string
,
config_value
)
>
;
/// Parse `file_name` using given file format `cf`.
/// Read configuration from `input_stream` using given `format`.
/// @param input_stream ASCII-formatted configuration.
/// @param format Configuration format such as INI.
/// @param errors Output streams for error messages.
void
parse_config
(
std
::
istream
&
input_stream
,
config_format
format
,
optional
<
std
::
ostream
&>
errors
=
none
);
/// Read configuration from `file_name` using given `format` or try to
/// deduce file format automatically if `cf == none`.
/// @param file_name Path to configuration file.
/// @param cf Forces the parser to use a specific file format unless `none`.
/// @param errors Output streams for error messages.
void
parse_config
(
const
std
::
string
&
file_name
,
config_format
cf
=
config_format
::
auto_detect
);
optional
<
config_format
>
cf
=
none
,
optional
<
std
::
ostream
&>
errors
=
none
);
}
// namespace caf
...
...
libcaf_core/src/parse_config.cpp
View file @
68292c2b
...
...
@@ -19,59 +19,65 @@
#include "caf/parse_config.hpp"
#include <vector>
#include <fstream>
#include <iostream>
#include <sstream>
#include <
vector
>
#include <
iostream
>
#include "caf/actor.hpp"
#include "caf/atom.hpp"
#include "caf/detail/optional_message_visitor.hpp"
#include "caf/detail/parse_ini.hpp"
#include "caf/send.hpp"
#include "caf/actor.hpp"
#include "caf/experimental/whereis.hpp"
#include "caf/detail/parse_ini.hpp"
#include "caf/detail/optional_message_visitor.hpp"
namespace
caf
{
class
message_visitor
:
public
static_visitor
<
message
>
{
public:
template
<
class
T
>
message
operator
()(
T
&
value
)
const
{
return
make_message
(
std
::
move
(
value
));
}
};
void
parse_config
(
std
::
istream
&
input
,
config_format
format
,
optional
<
std
::
ostream
&>
errors
)
{
if
(
!
input
)
return
;
auto
cs
=
experimental
::
whereis
(
atom
(
"ConfigServ"
));
auto
consume
=
[
&
](
std
::
string
key
,
config_value
value
)
{
message_visitor
mv
;
anon_send
(
cs
,
put_atom
::
value
,
std
::
move
(
key
),
apply_visitor
(
mv
,
value
));
};
switch
(
format
)
{
case
config_format
:
:
ini
:
detail
::
parse_ini
(
input
,
consume
,
errors
);
}
}
void
parse_config
(
const
std
::
string
&
file_name
,
config_format
cf
)
{
if
(
cf
==
config_format
::
auto_detect
)
{
optional
<
config_format
>
format
,
optional
<
std
::
ostream
&>
errors
)
{
if
(
!
format
)
{
// try to detect file format according to file extension
if
(
file_name
.
size
()
<
5
)
{
if
(
file_name
.
size
()
<
5
)
std
::
cerr
<<
"filename is to short"
<<
std
::
endl
;
}
else
if
(
file_name
.
compare
(
file_name
.
size
()
-
4
,
4
,
".ini"
))
{
parse_config
(
file_name
,
config_format
::
ini
);
}
else
{
std
::
cerr
<<
"unknown config file format"
<<
std
::
endl
;
}
else
if
(
file_name
.
compare
(
file_name
.
size
()
-
4
,
4
,
".ini"
))
parse_config
(
file_name
,
config_format
::
ini
,
errors
);
else
if
(
errors
)
*
errors
<<
"error: unknown config file format"
<<
std
::
endl
;
return
;
}
std
::
ifstream
raw_data
(
file_name
);
std
::
stringstream
error_stream
;
struct
consumer
{
actor
config_server_
;
consumer
()
{
config_server_
=
experimental
::
whereis
(
atom
(
"ConfigServ"
));
}
consumer
(
const
consumer
&
)
=
default
;
consumer
&
operator
=
(
const
consumer
&
)
=
default
;
void
operator
()(
const
std
::
string
&
,
config_value
)
const
{
// send message to config server
}
};
consumer
cons
;
switch
(
cf
)
{
case
config_format
:
:
ini
:
detail
::
parse_ini
(
raw_data
,
error_stream
,
cons
);
break
;
default:
std
::
cerr
<<
"no other format is supported"
<<
std
::
endl
;
break
;
std
::
ifstream
input
{
file_name
};
if
(
!
input
)
{
if
(
errors
)
*
errors
<<
"error: unable to open "
<<
file_name
<<
std
::
endl
;
return
;
}
raw_data
.
close
(
);
parse_config
(
input
,
*
format
,
errors
);
}
}
// namespace caf
libcaf_core/src/parse_ini.cpp
View file @
68292c2b
...
...
@@ -26,13 +26,15 @@
namespace
caf
{
void
detail
::
parse_ini
(
std
::
istream
&
input
,
std
::
ostream
&
errors
,
config_consumer
consumer
)
{
void
detail
::
parse_ini
(
std
::
istream
&
input
,
config_consumer
consumer
,
optional
<
std
::
ostream
&>
errors
)
{
std
::
string
group
;
std
::
string
line
;
size_t
ln
=
0
;
// line number
auto
print
=
[
&
](
const
char
*
category
,
const
char
*
str
)
{
errors
<<
category
<<
" in line "
<<
ln
<<
": "
<<
str
<<
std
::
endl
;
if
(
errors
)
*
errors
<<
category
<<
" in line "
<<
ln
<<
": "
<<
str
<<
std
::
endl
;
};
auto
print_error
=
[
&
](
const
char
*
str
)
{
print
(
"error"
,
str
);
...
...
libcaf_core/test/parse_ini.cpp
View file @
68292c2b
...
...
@@ -27,6 +27,8 @@
#include "caf/all.hpp"
#include "caf/experimental/whereis.hpp"
#include "caf/detail/parse_ini.hpp"
#include "caf/detail/safe_equal.hpp"
...
...
@@ -79,24 +81,72 @@ hop=--"hiho"
)__"
;
struct
fixture
{
void
load
(
const
char
*
str
)
{
auto
f
=
[
&
](
std
::
string
key
,
config_value
value
)
{
values
.
emplace
(
std
::
move
(
key
),
std
::
move
(
value
));
};
~
fixture
()
{
shutdown
();
}
template
<
class
F
>
void
load_impl
(
F
loader
,
const
char
*
str
)
{
std
::
stringstream
ss
;
std
::
stringstream
err
;
ss
<<
str
;
detail
::
parse_ini
(
ss
,
err
,
f
);
loader
(
ss
,
err
);
split
(
errors
,
err
.
str
(),
is_any_of
(
"
\n
"
),
token_compress_on
);
}
void
load_to_config_server
(
const
char
*
str
)
{
config_server
=
experimental
::
whereis
(
atom
(
"ConfigServ"
));;
auto
f
=
[
&
](
std
::
istream
&
in
,
std
::
ostream
&
out
)
{
parse_config
(
in
,
config_format
::
ini
,
out
);
};
load_impl
(
f
,
str
);
}
void
load
(
const
char
*
str
)
{
auto
consume
=
[
&
](
std
::
string
key
,
config_value
value
)
{
values
.
emplace
(
std
::
move
(
key
),
std
::
move
(
value
));
};
auto
f
=
[
&
](
std
::
istream
&
in
,
std
::
ostream
&
out
)
{
detail
::
parse_ini
(
in
,
consume
,
out
);
};
load_impl
(
f
,
str
);
}
bool
has_error
(
const
char
*
err
)
{
return
std
::
any_of
(
errors
.
begin
(),
errors
.
end
(),
[
=
](
const
std
::
string
&
str
)
{
return
str
==
err
;
});
}
template
<
class
T
>
bool
config_server_has
(
const
char
*
key
,
const
T
&
what
)
{
using
type
=
typename
std
::
conditional
<
std
::
is_convertible
<
T
,
std
::
string
>::
value
,
std
::
string
,
typename
std
::
conditional
<
std
::
is_integral
<
T
>::
value
&&
!
std
::
is_same
<
T
,
bool
>::
value
,
int64_t
,
T
>::
type
>::
type
;
bool
result
=
false
;
scoped_actor
self
;
self
->
sync_send
(
config_server
,
get_atom
::
value
,
key
).
await
(
[
&
](
ok_atom
,
std
::
string
&
,
message
&
msg
)
{
msg
.
apply
(
[
&
](
type
&
val
)
{
result
=
detail
::
safe_equal
(
what
,
val
);
}
);
}
);
return
result
;
}
template
<
class
T
>
bool
value_is
(
const
char
*
key
,
const
T
&
what
)
{
if
(
config_server
!=
invalid_actor
)
return
config_server_has
(
key
,
what
);
auto
&
cv
=
values
[
key
];
using
type
=
typename
std
::
conditional
<
...
...
@@ -112,7 +162,63 @@ struct fixture {
return
ptr
!=
nullptr
&&
detail
::
safe_equal
(
*
ptr
,
what
);
}
size_t
num_values
()
{
if
(
config_server
!=
invalid_actor
)
{
size_t
result
=
0
;
scoped_actor
self
;
self
->
sync_send
(
config_server
,
get_atom
::
value
,
"*"
).
await
(
[
&
](
ok_atom
,
std
::
vector
<
std
::
pair
<
std
::
string
,
message
>>&
msgs
)
{
result
=
msgs
.
size
();
}
);
return
result
;
}
return
values
.
size
();
}
void
check_case1
()
{
CAF_CHECK
(
errors
.
empty
());
CAF_CHECK
(
num_values
()
==
6
);
CAF_CHECK
(
value_is
(
"nexus.port"
,
4242
));
CAF_CHECK
(
value_is
(
"nexus.host"
,
"127.0.0.1"
));
CAF_CHECK
(
value_is
(
"scheduler.policy"
,
"work-sharing"
));
CAF_CHECK
(
value_is
(
"scheduler.max-threads"
,
2
));
CAF_CHECK
(
value_is
(
"middleman.automatic-connections"
,
true
));
CAF_CHECK
(
value_is
(
"cash.greeting"
,
"Hi there, this is
\"
CASH!
\"\n
~
\\
~ use at your own risk ~
\\
~"
));
}
void
check_case2
()
{
CAF_CHECK
(
errors
.
empty
());
CAF_CHECK
(
num_values
()
==
5
);
CAF_CHECK
(
value_is
(
"test.foo"
,
-
0xff
));
CAF_CHECK
(
value_is
(
"test.bar"
,
034
));
CAF_CHECK
(
value_is
(
"test.baz"
,
-
0.23
));
CAF_CHECK
(
value_is
(
"test.buzz"
,
1E-34
));
CAF_CHECK
(
value_is
(
"test.bazz"
,
10931
));
}
void
check_case3
()
{
CAF_CHECK
(
has_error
(
"error in line 2: missing ] at end of line"
));
CAF_CHECK
(
has_error
(
"error in line 3: value outside of a group"
));
CAF_CHECK
(
has_error
(
"error in line 6: no '=' found"
));
CAF_CHECK
(
has_error
(
"error in line 7: line starting with '='"
));
CAF_CHECK
(
has_error
(
"error in line 8: line ends with '='"
));
CAF_CHECK
(
has_error
(
"error in line 9: stray '
\"
'"
));
CAF_CHECK
(
has_error
(
"error in line 10: string not terminated by '
\"
'"
));
CAF_CHECK
(
has_error
(
"warning in line 12: trailing quotation mark escaped"
));
CAF_CHECK
(
has_error
(
"error in line 13: '-' is not a number"
));
CAF_CHECK
(
has_error
(
"error in line 14: invalid hex value"
));
CAF_CHECK
(
has_error
(
"error in line 15: invalid binary value"
));
CAF_CHECK
(
has_error
(
"error in line 16: invalid oct value"
));
CAF_CHECK
(
has_error
(
"error in line 17: invalid value"
));
CAF_CHECK
(
num_values
()
==
2
);
CAF_CHECK
(
value_is
(
"test.some-int"
,
42
));
CAF_CHECK
(
value_is
(
"test.some-string"
,
"hi there!"
));
}
std
::
map
<
std
::
string
,
config_value
>
values
;
actor
config_server
;
std
::
vector
<
std
::
string
>
errors
;
};
...
...
@@ -120,48 +226,40 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE
(
parse_ini_tests
,
fixture
)
CAF_TEST
(
simple_ini
)
{
load
(
case1
);
CAF_CHECK
(
errors
.
empty
());
CAF_CHECK
(
values
.
count
(
"nexus.port"
)
>
0
);
CAF_CHECK
(
value_is
(
"nexus.port"
,
4242
));
CAF_CHECK
(
value_is
(
"nexus.host"
,
"127.0.0.1"
));
CAF_CHECK
(
value_is
(
"scheduler.policy"
,
"work-sharing"
));
CAF_CHECK
(
value_is
(
"scheduler.max-threads"
,
2
));
CAF_CHECK
(
value_is
(
"middleman.automatic-connections"
,
true
));
CAF_CHECK
(
values
.
count
(
"cash.greeting"
)
>
0
);
CAF_CHECK
(
value_is
(
"cash.greeting"
,
"Hi there, this is
\"
CASH!
\"\n
~
\\
~ use at your own risk ~
\\
~"
));
check_case1
();
}
CAF_TEST
(
numbers
)
{
load
(
case2
);
CAF_CHECK
(
join
(
errors
,
"
\n
"
)
==
""
);
CAF_CHECK
(
value_is
(
"test.foo"
,
-
0xff
));
CAF_CHECK
(
value_is
(
"test.bar"
,
034
));
CAF_CHECK
(
value_is
(
"test.baz"
,
-
0.23
));
CAF_CHECK
(
value_is
(
"test.buzz"
,
1E-34
));
CAF_CHECK
(
value_is
(
"test.bazz"
,
10931
));
check_case2
();
}
CAF_TEST
(
errors
)
{
load
(
case3
);
CAF_CHECK
(
has_error
(
"error in line 2: missing ] at end of line"
));
CAF_CHECK
(
has_error
(
"error in line 3: value outside of a group"
));
CAF_CHECK
(
has_error
(
"error in line 6: no '=' found"
));
CAF_CHECK
(
has_error
(
"error in line 7: line starting with '='"
));
CAF_CHECK
(
has_error
(
"error in line 8: line ends with '='"
));
CAF_CHECK
(
has_error
(
"error in line 9: stray '
\"
'"
));
CAF_CHECK
(
has_error
(
"error in line 10: string not terminated by '
\"
'"
));
CAF_CHECK
(
has_error
(
"warning in line 12: trailing quotation mark escaped"
));
CAF_CHECK
(
has_error
(
"error in line 13: '-' is not a number"
));
CAF_CHECK
(
has_error
(
"error in line 14: invalid hex value"
));
CAF_CHECK
(
has_error
(
"error in line 15: invalid binary value"
));
CAF_CHECK
(
has_error
(
"error in line 16: invalid oct value"
));
CAF_CHECK
(
has_error
(
"error in line 17: invalid value"
));
CAF_CHECK
(
values
.
size
()
==
2
);
CAF_CHECK
(
value_is
(
"test.some-int"
,
42
));
CAF_CHECK
(
value_is
(
"test.some-string"
,
"hi there!"
));
load_to_config_server
(
case3
);
check_case3
();
}
CAF_TEST
(
simple_ini_via_config_server
)
{
load_to_config_server
(
case1
);
CAF_CHECK
(
values
.
empty
());
check_case1
();
}
CAF_TEST
(
numbers_via_config_server
)
{
load_to_config_server
(
case2
);
CAF_CHECK
(
values
.
empty
());
check_case2
();
}
CAF_TEST
(
errors_via_config_server
)
{
load_to_config_server
(
case3
);
CAF_CHECK
(
values
.
empty
());
check_case3
();
}
CAF_TEST_FIXTURE_SCOPE_END
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment