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