1 // This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
2 // the main distribution directory for license terms and copyright or visit
3 // https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
4
5 #include "caf/config_option.hpp"
6
7 #include <algorithm>
8 #include <limits>
9 #include <numeric>
10
11 #include "caf/config.hpp"
12 #include "caf/config_value.hpp"
13 #include "caf/error.hpp"
14 #include "caf/expected.hpp"
15 #include "caf/optional.hpp"
16
17 using std::move;
18 using std::string;
19
20 namespace caf {
21
22 // -- constructors, destructors, and assignment operators ----------------------
23
config_option(string_view category,string_view name,string_view description,const meta_state * meta,void * value)24 config_option::config_option(string_view category, string_view name,
25 string_view description, const meta_state* meta,
26 void* value)
27 : meta_(meta),
28 value_(value) {
29 using std::copy;
30 using std::accumulate;
31 auto comma = name.find(',');
32 auto long_name = name.substr(0, comma);
33 auto short_names = comma == string_view::npos ? string_view{}
34 : name.substr(comma + 1);
35 auto total_size = [](std::initializer_list<string_view> xs) {
36 return (xs.size() - 1) // one separator between all fields
37 + accumulate(xs.begin(), xs.end(), size_t{0},
38 [](size_t x, string_view sv) { return x + sv.size(); });
39 };
40 auto ts = total_size({category, long_name, short_names, description});
41 CAF_ASSERT(ts <= std::numeric_limits<uint16_t>::max());
42 buf_size_ = static_cast<uint16_t>(ts);
43 buf_.reset(new char[ts]);
44 // fill the buffer with "<category>.<long-name>,<short-name>,<descriptions>"
45 auto first = buf_.get();
46 auto i = first;
47 auto pos = [&] {
48 return static_cast<uint16_t>(std::distance(first, i));
49 };
50 // <category>.
51 i = copy(category.begin(), category.end(), i);
52 category_separator_ = pos();
53 *i++ = '.';
54 // <long-name>,
55 i = copy(long_name.begin(), long_name.end(), i);
56 long_name_separator_ = pos();
57 *i++ = ',';
58 // <short-names>,
59 i = copy(short_names.begin(), short_names.end(), i);
60 short_names_separator_ = pos();
61 *i++ = ',';
62 // <description>
63 i = copy(description.begin(), description.end(), i);
64 CAF_ASSERT(pos() == buf_size_);
65 }
66
config_option(const config_option & other)67 config_option::config_option(const config_option& other)
68 : category_separator_{other.category_separator_},
69 long_name_separator_{other.long_name_separator_},
70 short_names_separator_{other.short_names_separator_},
71 buf_size_{other.buf_size_},
72 meta_{other.meta_},
73 value_{other.value_} {
74 buf_.reset(new char[buf_size_]);
75 std::copy_n(other.buf_.get(), buf_size_, buf_.get());
76 }
77
operator =(const config_option & other)78 config_option& config_option::operator=(const config_option& other) {
79 config_option tmp{other};
80 swap(*this, tmp);
81 return *this;
82 }
83
swap(config_option & first,config_option & second)84 void swap(config_option& first, config_option& second) noexcept {
85 using std::swap;
86 swap(first.buf_, second.buf_);
87 swap(first.category_separator_, second.category_separator_);
88 swap(first.long_name_separator_, second.long_name_separator_);
89 swap(first.short_names_separator_, second.short_names_separator_);
90 swap(first.buf_size_, second.buf_size_);
91 swap(first.meta_, second.meta_);
92 swap(first.value_, second.value_);
93 }
94
95 // -- properties ---------------------------------------------------------------
96
category() const97 string_view config_option::category() const noexcept {
98 return buf_slice(buf_[0] == '?' ? 1 : 0, category_separator_);
99 }
100
long_name() const101 string_view config_option::long_name() const noexcept {
102 return buf_slice(category_separator_ + 1, long_name_separator_);
103 }
104
short_names() const105 string_view config_option::short_names() const noexcept {
106 return buf_slice(long_name_separator_ + 1, short_names_separator_);
107 }
108
description() const109 string_view config_option::description() const noexcept {
110 return buf_slice(short_names_separator_ + 1, buf_size_);
111 }
112
full_name() const113 string_view config_option::full_name() const noexcept {
114 return buf_slice(buf_[0] == '?' ? 1 : 0, long_name_separator_);
115 }
116
sync(config_value & x) const117 error config_option::sync(config_value& x) const {
118 return meta_->sync(value_, x);
119 }
120
store(const config_value & x) const121 error config_option::store(const config_value& x) const {
122 auto cpy = x;
123 return sync(cpy);
124 }
125
type_name() const126 string_view config_option::type_name() const noexcept {
127 return meta_->type_name;
128 }
129
is_flag() const130 bool config_option::is_flag() const noexcept {
131 return type_name() == "bool";
132 }
133
has_flat_cli_name() const134 bool config_option::has_flat_cli_name() const noexcept {
135 return buf_[0] == '?' || category() == "global";
136 }
137
parse(string_view input) const138 expected<config_value> config_option::parse(string_view input) const {
139 config_value val{input};
140 if (auto err = sync(val))
141 return {std::move(err)};
142 else
143 return {std::move(val)};
144 }
145
get() const146 optional<config_value> config_option::get() const {
147 if (value_ != nullptr && meta_->get != nullptr)
148 return meta_->get(value_);
149 return none;
150 }
151
buf_slice(size_t from,size_t to) const152 string_view config_option::buf_slice(size_t from, size_t to) const noexcept {
153 CAF_ASSERT(from <= to);
154 return {buf_.get() + from, to - from};
155 }
156
157 } // namespace caf
158