| 186 | |
| 187 | template <typename T> |
| 188 | Expected<Value<T>> readValue( const std::vector<std::string>& path ) |
| 189 | { |
| 190 | if ( path.empty() ) |
| 191 | return unexpected( "readValue: Empty path not allowed here." ); |
| 192 | |
| 193 | auto groupEx = findGroup( { path.data(), path.size() - 1 } ); |
| 194 | if ( !groupEx ) |
| 195 | return unexpected( groupEx.error() ); |
| 196 | const auto& group = **groupEx; |
| 197 | |
| 198 | auto iter = group.elems.find( path.back() ); |
| 199 | if ( iter == group.elems.end() ) |
| 200 | return unexpected( fmt::format( "No such entry: `{}`. Known entries are: {}.", path.back(), listKeys( group ) ) ); |
| 201 | |
| 202 | auto entryEx = iter->second.getAs<TestEngine::ValueEntry>( path.back() ); |
| 203 | if ( !entryEx ) |
| 204 | return unexpected( entryEx.error() ); |
| 205 | const auto& entry = **entryEx; |
| 206 | |
| 207 | if constexpr ( std::is_same_v<T, std::string> ) |
| 208 | { |
| 209 | if ( auto val = std::get_if<TestEngine::ValueEntry::Value<T>>( &entry.value ) ) |
| 210 | { |
| 211 | Value<T> ret; |
| 212 | ret.value = val->value; |
| 213 | ret.allowedValues = val->allowedValues; |
| 214 | return ret; |
| 215 | } |
| 216 | |
| 217 | return unexpected( "This isn't a string." ); |
| 218 | } |
| 219 | else |
| 220 | { |
| 221 | // Try to read with the wrong signedness first. |
| 222 | if constexpr ( std::is_same_v<T, std::int64_t> ) |
| 223 | { |
| 224 | if ( auto val = std::get_if<TestEngine::ValueEntry::Value<std::uint64_t>>( &entry.value ) ) |
| 225 | { |
| 226 | // Allow if the value is not too large. |
| 227 | // We don't check if the max bound is too large, because it be too large by default if not specified. |
| 228 | |
| 229 | if ( val->value > std::uint64_t( std::numeric_limits<std::int64_t>::max() ) ) |
| 230 | return unexpected( "Attempt to read an uint64_t value as an int64_t, but the value is too large to fit into the target type. Read as uint64_t instead." ); |
| 231 | |
| 232 | Value<T> ret; |
| 233 | ret.value = std::int64_t( val->value ); |
| 234 | ret.min = std::int64_t( std::min( val->min, std::uint64_t( std::numeric_limits<std::int64_t>::max() ) ) ); |
| 235 | ret.max = std::int64_t( std::min( val->max, std::uint64_t( std::numeric_limits<std::int64_t>::max() ) ) ); |
| 236 | return ret; |
| 237 | } |
| 238 | } |
| 239 | else if constexpr ( std::is_same_v<T, std::uint64_t> ) |
| 240 | { |
| 241 | if ( auto val = std::get_if<TestEngine::ValueEntry::Value<std::int64_t>>( &entry.value ) ) |
| 242 | { |
| 243 | // Allow if the value is nonnegative, and the min bound is also nonnegative. |
| 244 | |
| 245 | if ( val->value < 0 || val->min < 0 ) |