|
1 | 1 | #pragma once |
2 | 2 |
|
| 3 | +#include <string> |
| 4 | +#include <tuple> |
| 5 | +#include <type_traits> |
| 6 | +#include <unordered_map> |
| 7 | +#include <utility> |
| 8 | +#include <vector> |
| 9 | + |
3 | 10 | #include "error.h" |
4 | 11 | #include "optional.h" |
5 | 12 |
|
@@ -56,26 +63,98 @@ struct ConfigMetadata { |
56 | 63 | : name(n), value(std::move(v)), origin(orig), error(std::move(err)) {} |
57 | 64 | }; |
58 | 65 |
|
59 | | -// Return a vector containing the configuration values chosen from one of the |
60 | | -// specified `from_env`, `from_user`, and `fallback`. This function defines |
61 | | -// the relative precedence among configuration values, from lower to higher, |
62 | | -// originating from the default configuration, programmatic configuration, |
63 | | -// and environment. |
64 | | -template <typename Value, typename DefaultValue> |
65 | | -std::vector<std::pair<ConfigMetadata::Origin, Value>> pick( |
| 66 | +// Return the chosen configuration value from one of the specified `from_env`, |
| 67 | +// `from_user`, and `fallback`. This function defines the relative precedence |
| 68 | +// among configuration values, from lower to higher, originating from the |
| 69 | +// default configuration, programmatic configuration, and environment. |
| 70 | +// |
| 71 | +// This overload directly populates both telemetry_configs[config_name] and |
| 72 | +// metadata[config_name] using the stringified value, avoiding duplicate |
| 73 | +// string computations. Both telemetry_configs and config_name are required. |
| 74 | +// Returns the chosen value directly (no origin needed since it's in metadata). |
| 75 | +template <typename Value, typename DefaultValue, typename Stringifier = std::nullptr_t> |
| 76 | +Value pick( |
66 | 77 | const Optional<Value>& from_env, const Optional<Value>& from_user, |
67 | | - DefaultValue fallback) { |
68 | | - std::vector<std::pair<ConfigMetadata::Origin, Value>> result; |
69 | | - result.emplace_back(ConfigMetadata::Origin::DEFAULT, fallback); |
70 | | - |
71 | | - if (from_user) { |
72 | | - result.emplace_back(ConfigMetadata::Origin::CODE, *from_user); |
73 | | - } |
74 | | - if (from_env) { |
75 | | - result.emplace_back(ConfigMetadata::Origin::ENVIRONMENT_VARIABLE, |
76 | | - *from_env); |
77 | | - } |
78 | | - return result; |
| 78 | + DefaultValue fallback, |
| 79 | + std::unordered_map<ConfigName, std::vector<ConfigMetadata>>* telemetry_configs, |
| 80 | + std::unordered_map<ConfigName, ConfigMetadata>* metadata, |
| 81 | + ConfigName config_name, |
| 82 | + Stringifier to_string_fn = nullptr) { |
| 83 | + |
| 84 | + auto stringify = [&](const Value& v) -> std::string { |
| 85 | + if constexpr (!std::is_same_v<Stringifier, std::nullptr_t>) { |
| 86 | + return to_string_fn(v); // use provided function |
| 87 | + } else if constexpr (std::is_constructible_v<std::string, Value>) { |
| 88 | + return std::string(v); // default behaviour (works for string-like types) |
| 89 | + } else { |
| 90 | + static_assert(!std::is_same_v<Value, Value>, |
| 91 | + "Non-string types require a stringifier function"); |
| 92 | + return ""; // unreachable |
| 93 | + } |
| 94 | + }; |
| 95 | + |
| 96 | + std::vector<ConfigMetadata> telemetry_entries; |
| 97 | + Value chosen_value = fallback; |
| 98 | + |
| 99 | + auto add_entry = [&](ConfigMetadata::Origin origin, const Value& val) { |
| 100 | + std::string val_str = stringify(val); |
| 101 | + telemetry_entries.emplace_back(ConfigMetadata{config_name, val_str, origin}); |
| 102 | + chosen_value = val; |
| 103 | + }; |
| 104 | + |
| 105 | + add_entry(ConfigMetadata::Origin::DEFAULT, fallback); |
| 106 | + |
| 107 | + if (from_user) { |
| 108 | + add_entry(ConfigMetadata::Origin::CODE, *from_user); |
| 109 | + } |
| 110 | + |
| 111 | + if (from_env) { |
| 112 | + add_entry(ConfigMetadata::Origin::ENVIRONMENT_VARIABLE, *from_env); |
| 113 | + } |
| 114 | + |
| 115 | + (*telemetry_configs)[config_name] = std::move(telemetry_entries); |
| 116 | + (*metadata)[config_name] = (*telemetry_configs)[config_name].back(); |
| 117 | + |
| 118 | + return chosen_value; |
| 119 | +} |
| 120 | + |
| 121 | +// Overload without telemetry - for simpler use cases where telemetry |
| 122 | +// population is not needed. |
| 123 | +template <typename Value, typename DefaultValue, typename Stringifier = std::nullptr_t> |
| 124 | +std::vector<std::tuple<ConfigMetadata::Origin, Value, std::string>> pick( |
| 125 | + const Optional<Value>& from_env, const Optional<Value>& from_user, |
| 126 | + DefaultValue fallback, Stringifier to_string_fn = nullptr) { |
| 127 | + |
| 128 | + auto stringify = [&](const Value& v) -> std::string { |
| 129 | + if constexpr (!std::is_same_v<Stringifier, std::nullptr_t>) { |
| 130 | + return to_string_fn(v); // use provided function |
| 131 | + } else if constexpr (std::is_constructible_v<std::string, Value>) { |
| 132 | + return std::string(v); // default behaviour (works for string-like types) |
| 133 | + } else { |
| 134 | + static_assert(!std::is_same_v<Value, Value>, |
| 135 | + "Non-string types require a stringifier function"); |
| 136 | + return ""; // unreachable |
| 137 | + } |
| 138 | + }; |
| 139 | + |
| 140 | + std::vector<std::tuple<ConfigMetadata::Origin, Value, std::string>> result; |
| 141 | + |
| 142 | + auto add_entry = [&](ConfigMetadata::Origin origin, const Value& val) { |
| 143 | + std::string val_str = stringify(val); |
| 144 | + result.emplace_back(origin, val, val_str); |
| 145 | + }; |
| 146 | + |
| 147 | + add_entry(ConfigMetadata::Origin::DEFAULT, fallback); |
| 148 | + |
| 149 | + if (from_user) { |
| 150 | + add_entry(ConfigMetadata::Origin::CODE, *from_user); |
| 151 | + } |
| 152 | + |
| 153 | + if (from_env) { |
| 154 | + add_entry(ConfigMetadata::Origin::ENVIRONMENT_VARIABLE, *from_env); |
| 155 | + } |
| 156 | + |
| 157 | + return result; |
79 | 158 | } |
80 | 159 |
|
81 | 160 | } // namespace tracing |
|
0 commit comments