Program Listing for File Env.ipp

Return to documentation for file (uconfig/format/impl/Env.ipp)

#pragma once

#include <cstdlib>
#include <limits>
#include <sstream>

namespace uconfig {

template <typename T>
std::optional<T> EnvFormat::Parse(const source_type*, const std::string& path) const
{
    const char* env_var = std::getenv(path.c_str());
    if (!env_var) {
        return std::nullopt;
    }
    return FromString<T>(env_var);
}

template <typename T>
void EnvFormat::Emit(dest_type* dest, const std::string& path, const T& value) const
{
    dest->emplace(std::make_pair(path, ToString<T>(value)));
}

std::string EnvFormat::VectorElementPath(const std::string& vector_path, std::size_t index) const noexcept
{
    return vector_path + "_" + std::to_string(index);
}

template <typename T>
std::optional<T> EnvFormat::FromString(const std::string& str)
{
    T result;

    std::istringstream ss(str);
    ss >> result;
    if (ss.fail() || ToString(result) != str) {
        return std::nullopt;
    }

    return result;
}

template <typename T>
std::string EnvFormat::ToString(const T& value)
{
    std::ostringstream ss;
    // by default stream print with .3 digits accuracy, so 123456.0 -> "1.234e+05" -> 123400;
    // but full precision (max_digits10) leads to: 11.0/10.0 -> 1.1000000000000001
    // [https://coliru.stacked-crooked.com/a/1681ce5326697585]
    if (std::numeric_limits<T>::max_digits10 > 0) {
        ss.precision(std::numeric_limits<T>::max_digits10 - 1);
    }

    ss << value;
    if (ss.fail()) {
        throw std::runtime_error("failed to print value");
    }
    return ss.str();
}

template <>
inline std::optional<std::string> EnvFormat::FromString<std::string>(const std::string& str)
{
    return str;
}

template <>
inline std::string EnvFormat::ToString<std::string>(const std::string& value)
{
    return value;
}

} // namespace uconfig