blob: ba56b4dad2868d62746778f1fa7023d854b93d2e [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iomanip>
#include <limits>
#include <string>
#include <sstream>
#include <utility>
#include <variant>
#include "utils/GeneralUtils.h"
#include "utils/StringUtils.h"
#pragma once
namespace org::apache::nifi::minifi::expression {
/**
* Represents an expression value, which can be one of multiple types or NULL (represented as variant with monostate as null)
*/
class Value {
public:
/// Construct a default (NULL) value
Value() = default;
/// Construct with the specified value
explicit Value(std::string val) :value_{std::move(val)} {}
explicit Value(bool val) :value_{val} {}
explicit Value(uint64_t val) :value_{val} {}
explicit Value(int64_t val) :value_{val} {}
explicit Value(long double val) :value_{val} {}
[[nodiscard]] bool isNull() const { return holds_alternative<std::monostate>(value_); }
[[nodiscard]] bool isDecimal() const {
return std::visit(utils::overloaded{
[](long double) { return true; },
[](const std::string& string_val) {
return string_val.find('.') != string_val.npos ||
string_val.find('e') != string_val.npos ||
string_val.find('E') != string_val.npos;
},
[](const auto&) { return false; }
}, value_);
}
void setSignedLong(int64_t val) { value_ = val; }
void setUnsignedLong(uint64_t val) { value_ = val; }
void setLongDouble(long double val) { value_ = val; }
void setBoolean(bool val) { value_ = val; }
void setString(std::string val) { value_ = std::move(val); }
[[nodiscard]] std::string asString() const {
return std::visit<std::string>(utils::overloaded{
[](const std::string& str) { return str; },
[](bool b) -> std::string { return b ? "true" : "false"; },
[](int64_t i) { return std::to_string(i); },
[](uint64_t i) { return std::to_string(i); },
[](long double d) {
std::stringstream ss;
ss << std::fixed << std::setprecision(std::numeric_limits<double>::digits10)
<< d;
auto result = ss.str();
result.erase(result.find_last_not_of('0') + 1, std::string::npos);
if (result.find('.') == result.length() - 1) {
result.erase(result.length() - 1, std::string::npos);
}
return result;
},
[](std::monostate) { return std::string{}; }
}, value_);
}
[[nodiscard]] uint64_t asUnsignedLong() const {
return std::visit<uint64_t>(utils::overloaded{
[](uint64_t i) { return i; },
[](int64_t i) { return gsl::narrow_cast<uint64_t>(i); },
[](long double d) { return static_cast<uint64_t>(d); },
[](const std::string& str) -> uint64_t {
const auto stoull_ = [](const std::string& s) { return std::stoull(s); };
return strParse<uint64_t>(stoull_, 0, "Value::asUnsignedLong", str);
},
[](auto) { return uint64_t{0}; }
}, value_);
}
[[nodiscard]] int64_t asSignedLong() const {
return std::visit<int64_t>(utils::overloaded{
[](int64_t i) { return i; },
[](uint64_t i) { return gsl::narrow_cast<int64_t>(i); },
[](long double d) { return static_cast<int64_t>(d); },
[](const std::string& str) -> int64_t {
const auto stoll_ = [](const std::string& s) { return std::stoll(s); };
return strParse<int64_t>(stoll_, 0, "Value::asSignedLong", str);
},
[](auto) { return int64_t{0}; }
}, value_);
}
[[nodiscard]] long double asLongDouble() const {
return std::visit<long double>(utils::overloaded{
[](long double d) { return d; },
[](int64_t i) { return static_cast<long double>(i); },
[](uint64_t i) { return static_cast<long double>(i); },
[](const std::string& str) -> long double {
const auto stold_ = [](const std::string& s) { return std::stold(s); };
return strParse<long double>(stold_, 0.0, "Value::asLongDouble", str);
},
[](auto) -> long double { return 0.0; }
}, value_);
}
[[nodiscard]] bool asBoolean() const {
return std::visit(utils::overloaded{
[](bool b) { return b; },
[](int64_t i) { return i != 0; },
[](uint64_t i) { return i != 0; },
[](long double d) { return d != 0.0; },
[](const std::string& str) { return utils::string::toBool(str).value_or(false); },
[](auto) { return false; }
}, value_);
}
private:
template<typename T>
static T strParse(std::regular_invocable<std::string> auto const& conversion_function, T default_value, std::string_view context, const std::string& value) {
if (value.empty()) return default_value;
try {
return std::invoke(conversion_function, value);
} catch (const std::invalid_argument&) {
throw std::invalid_argument{utils::string::join_pack(context, " failed to parse \"", value, "\": invalid argument")};
} catch (const std::out_of_range&) {
throw std::out_of_range{utils::string::join_pack(context, " failed to parse \"", value, "\": out of range")};
}
}
std::variant<std::monostate, bool, uint64_t, int64_t, long double, std::string> value_;
};
} // namespace org::apache::nifi::minifi::expression