68 using element_type = std::conditional_t<std_basic_string<T>, T, range_value_t_or_self_t<T>>;
69 using set_hash_type = std::conditional_t<std_basic_string<element_type>,
string_hash, std::hash<element_type>>;
71 static constexpr std::string_view delimiter{U8(
",")};
72 static constexpr std::string_view optional_pattern{U8(
"[]")};
73 static constexpr std::string_view keyword_pattern{U8(
"``")};
79 option() : base_{make_base_option()} {
81 if constexpr (std::is_enum_v<element_type>) {
82 for (
auto&& [name, value] : meta::runtime::get_enum_names<element_type>(
true)) {
83 valid_value_strs_.emplace_back(name);
84 valid_values_.emplace(value);
86 }
else if constexpr (std::same_as<T, bool>) {
87 valid_value_strs_.emplace_back(meta::true_string);
88 valid_value_strs_.emplace_back(meta::false_string);
89 valid_values_.emplace(
true);
90 valid_values_.emplace(
false);
91 set_default_value(
false);
95 [[nodiscard]] abi::string bound_name()
const {
96 return base_.bound_name();
99 option& set_bound_name(std::string_view name) {
100 static_cast<void>(base_.set_bound_name(name));
105 [[nodiscard]] abi::string description()
const {
106 return base_.description();
109 option& set_description(std::string_view description) {
110 static_cast<void>(base_.set_description(description));
115 [[nodiscard]] std::span<const abi::string> aliases()
const {
116 return base_.aliases();
119 option& add_aliases(std::span<const abi::string> aliases) {
120 static_cast<void>(base_.add_aliases(aliases));
125 [[nodiscard]] std::optional<abi::string> default_value_str()
const {
126 return default_value_str_;
129 [[nodiscard]] std::span<const abi::string> valid_value_strs() const noexcept {
130 return valid_value_strs_;
133 [[nodiscard]] abi::string name_hints()
const {
134 auto hints = base_.name_hints();
137 if (default_value_str()) {
138 hints.insert(hints.begin(), optional_pattern.front());
139 hints.push_back(optional_pattern.back());
145 [[nodiscard]] abi::string value_hints()
const {
148 if (valid_value_strs().empty()) {
149 hints.push_back(keyword_pattern.front());
150 hints.append(type_id.friendly_name());
151 hints.push_back(keyword_pattern.back());
153 auto joint = join_with(valid_value_strs_, delimiter);
155 hints.assign(joint.begin(), joint.end());
161 [[nodiscard]]
static bool check_target_type(meta::fingerprint
id)
noexcept {
162 return id == type_id;
165 [[nodiscard]]
bool parse_value_and_cache(std::string_view value) {
166 thread_local bool success =
true;
168 if constexpr (!std_basic_string<T> && extendable_contiguous_range<T>) {
169 parse_range_value_and_cache(value, success);
171 auto parsed_value = from_element_string(value);
173 if ((success = parsed_value.has_value())) {
174 validate_range(*parsed_value, success);
178 raise_error(U8(
"Invalid value."));
183 parsed_value.swap(cached_value_);
188 if (validation_result result; !(validate(value, result), result.success)) {
189 success = result.success;
191 raise_error(result.error);
198 void set_target_from_cache(
void* target) {
199 if (target && (cached_value_ || default_value_)) {
200 auto value = cached_value_ ? *cached_value_ : *default_value_;
203 if constexpr (
requires(T obj) { std::swap(obj, obj); }) {
204 std::swap(*
static_cast<T*
>(target), value);
205 }
else if constexpr (
requires(T obj) { T{}.swap(obj); }) {
206 value.swap(*
static_cast<T*
>(target));
208 *
static_cast<T*
>(target) = value;
213 void validate(std::string_view value, validation_result& result)
const {
214 base_.validate(value, result);
217 void raise_error(std::string_view message)
const {
218 base_.raise_error(message);
221 void on_validation(
const validation_handler& handler)
const {
222 base_.on_validation(handler);
225 void on_error(
const output_handler& handler)
const {
226 base_.on_error(handler);
229 template <std::convertible_to<std::
string_view>... Args>
230 option& add_aliases(Args&&... args) {
231 add_aliases(std::array{abi::string{std::string_view{std::forward<Args>(args)}}...});
236 template <
typename U>
237 requires std::constructible_from<T, U>
238 option& set_default_value(U&& value) {
239 default_value_.emplace(std::forward<U>(value));
242 if constexpr (!std_basic_string<T> && extendable_contiguous_range<T>) {
243 auto strs = (*default_value_) | std::views::transform(&option::to_element_string);
244 auto joint = join_with(strs, delimiter);
246 default_value_str_.emplace(joint.begin(), joint.end());
248 default_value_str_.emplace(to_element_string(*default_value_));
254 option& set_valid_values(std::span<const element_type> values) {
255 decltype(valid_values_){values.begin(), values.end()}.swap(valid_values_);
257 auto adapter = valid_values_ | std::views::transform(&option::to_element_string);
259 valid_value_strs_.assign(adapter.begin(), adapter.end());
264 template <
typename... Args>
265 requires(std::constructible_from<element_type, Args> && ...)
266 option& set_valid_values(Args&&... args) {
267 set_valid_values(std::array{element_type{std::forward<Args>(args)}...});
272 abstract::option as_abstract() {
273 return abstract::option{std::move(*
this)};
277 static abi::string to_element_string(
const element_type& item) {
278 if constexpr (std::convertible_to<element_type, std::string_view>) {
279 return abi::string{std::string_view{item}};
280 }
else if constexpr (std::is_enum_v<element_type> || std::same_as<element_type, bool>) {
281 return abi::to_abi_string(meta::runtime::to_string(item));
282 }
else if constexpr (std::is_arithmetic_v<element_type>) {
283 return abi::to_abi_string(to_string(item));
289 static std::optional<element_type> from_element_string(std::string_view str) {
290 if constexpr (std_basic_string<element_type>) {
291 return element_type{str};
292 }
else if constexpr (std::is_enum_v<element_type>) {
293 return meta::runtime::from_string<element_type>(str);
294 }
else if constexpr (std::same_as<element_type, bool>) {
295 return meta::runtime::from_string<bool>(str);
296 }
else if constexpr (std::is_arithmetic_v<element_type>) {
297 return from_string<element_type>(str);
303 void validate_range(
const element_type& value,
bool& success)
const {
304 if (success = valid_values_.empty() ?
true : valid_values_.contains(value); !success) {
305 auto joint = join_with(valid_value_strs_, delimiter);
307 raise_error(U8(
"The value was out of range."));
308 raise_error(U8(
"One of the following values is allowed:"));
309 raise_error(abi::string{joint.begin(), joint.end()});
313 void parse_range_value_and_cache(std::string_view value,
bool& success) {
314 auto validator = [&](
const auto& inner) {
315 if ((success = inner.has_value())) {
316 validate_range(*inner, success);
318 raise_error(U8(
"Invalid value."));
324 if constexpr (!std_basic_string<T> && extendable_contiguous_range<T>) {
325 auto parts = std::views::split(value, delimiter) | std::views::transform([](
const auto& inner) {
328 auto iter = inner | std::views::common;
330 return from_element_string(abi::string{iter.begin(), iter.end()});
331 }) | std::views::take_while(validator)
332 | std::views::transform([](
const auto& inner) {
return *inner; }) | std::views::common;
335 cached_value_.emplace();
339 std::ranges::copy(parts, std::back_inserter(*cached_value_));
340 cached_value_->shrink_to_fit();
342 cached_value_.reset();
347 abstract::option base_;
348 std::optional<T> cached_value_;
349 std::optional<T> default_value_;
350 std::optional<abi::string> default_value_str_;
351 abi::vector<abi::string> valid_value_strs_;
352 std::unordered_set<element_type, set_hash_type, std::equal_to<>> valid_values_;