[v17,03/12] config: Add configuration retrieval helpers
diff mbox series

Message ID 20250911092945.16517-4-mzamazal@redhat.com
State New
Headers show
Series
  • Add global configuration file
Related show

Commit Message

Milan Zamazal Sept. 11, 2025, 9:29 a.m. UTC
Let's add some helpers to make accessing simple configuration values
simpler.  The helpers are used in the followup patches.

GlobalConfiguration::option ensures that no value is returned rather
than a value of YamlObject::empty.

Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
---
 .../libcamera/internal/global_configuration.h | 28 ++++++
 src/libcamera/global_configuration.cpp        | 99 +++++++++++++++++++
 2 files changed, 127 insertions(+)

Comments

Paul Elder Sept. 11, 2025, 11:01 a.m. UTC | #1
Hi Milan,

Thanks for the patch.

Quoting Milan Zamazal (2025-09-11 18:29:33)
> Let's add some helpers to make accessing simple configuration values
> simpler.  The helpers are used in the followup patches.
> 
> GlobalConfiguration::option ensures that no value is returned rather
> than a value of YamlObject::empty.
> 
> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>

Looks good to me.

Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>

> ---
>  .../libcamera/internal/global_configuration.h | 28 ++++++
>  src/libcamera/global_configuration.cpp        | 99 +++++++++++++++++++
>  2 files changed, 127 insertions(+)
> 
> diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h
> index f695498c4..8d09517ed 100644
> --- a/include/libcamera/internal/global_configuration.h
> +++ b/include/libcamera/internal/global_configuration.h
> @@ -8,6 +8,11 @@
>  #pragma once
>  
>  #include <filesystem>
> +#include <optional>
> +#include <string>
> +#include <string_view>
> +
> +#include <libcamera/base/utils.h>
>  
>  #include "libcamera/internal/yaml_parser.h"
>  
> @@ -23,6 +28,29 @@ public:
>         unsigned int version() const;
>         Configuration configuration() const;
>  
> +       template<typename T>
> +       std::optional<T> option(
> +               const std::initializer_list<std::string_view> confPath) const
> +       {
> +               const YamlObject *c = &configuration();
> +               for (auto part : confPath) {
> +                       c = &(*c)[part];
> +                       if (!*c)
> +                               return {};
> +               }
> +               return c->get<T>();
> +       }
> +
> +       std::optional<std::vector<std::string>> listOption(
> +               const std::initializer_list<std::string_view> confPath) const;
> +       std::optional<std::string> envOption(
> +               const char *const envVariable,
> +               const std::initializer_list<std::string_view> confPath) const;
> +       std::optional<std::vector<std::string>> envListOption(
> +               const char *const envVariable,
> +               const std::initializer_list<std::string_view> confPath,
> +               const std::string delimiter = ":") const;
> +
>  private:
>         bool loadFile(const std::filesystem::path &fileName);
>         void load();
> diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp
> index d02668111..592edcf30 100644
> --- a/src/libcamera/global_configuration.cpp
> +++ b/src/libcamera/global_configuration.cpp
> @@ -8,8 +8,12 @@
>  #include "libcamera/internal/global_configuration.h"
>  
>  #include <filesystem>
> +#include <memory>
> +#include <optional>
> +#include <string>
>  #include <string_view>
>  #include <sys/types.h>
> +#include <vector>
>  
>  #include <libcamera/base/file.h>
>  #include <libcamera/base/log.h>
> @@ -43,6 +47,12 @@ LOG_DEFINE_CATEGORY(Configuration)
>   * If the first found configuration file cannot be opened or parsed, an error is
>   * reported and no configuration file is used. This is to prevent libcamera from
>   * using an unintended configuration file.
> + *
> + * The configuration can be accessed using the provided helpers. Namely
> + * GlobalConfiguration::option(), GlobalConfiguration::envOption(),
> + * GlobalConfiguration::listOption(), and GlobalConfiguration::envListOption()
> + * to access individual options, or GlobalConfiguration::configuration() to
> + * access the whole configuration.
>   */
>  
>  bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)
> @@ -110,6 +120,95 @@ GlobalConfiguration::GlobalConfiguration()
>   * the underlying type.
>   */
>  
> +/**
> + * \fn std::optional<T> GlobalConfiguration::option(const std::initializer_list<std::string_view> &confPath) const
> + * \brief Return value of the configuration option identified by \a confPath
> + * \param[in] confPath Sequence of the YAML section names (excluding
> + * `configuration') leading to the requested option
> + * \return The value of the configuration item corresponding to \a confPath if
> + * it exists in the configuration file, or no value otherwise
> + */
> +
> +/**
> + * \brief Return values of the configuration option identified by \a confPath
> + * \tparam T The type of the retrieved configuration value
> + * \param[in] confPath Sequence of the YAML section names (excluding
> + * `configuration') leading to the requested list option, separated by dots
> + * \return A vector of strings or no value if not found
> + */
> +std::optional<std::vector<std::string>> GlobalConfiguration::listOption(
> +       const std::initializer_list<std::string_view> confPath) const
> +{
> +       const YamlObject *c = &configuration();
> +       for (auto part : confPath) {
> +               c = &(*c)[part];
> +               if (!*c)
> +                       return {};
> +       }
> +       return c->getList<std::string>();
> +}
> +
> +/**
> + * \brief Return value of environment variable with a fallback on the configuration file
> + * \param[in] envVariable Environment variable to get the value from
> + * \param[in] confPath The sequence of YAML section names to fall back on when
> + * \a envVariable is unavailable
> + *
> + * This helper looks first at the given environment variable and if it is
> + * defined then it returns its value (even if it is empty). Otherwise it looks
> + * for \a confPath the same way as in GlobalConfiguration::option. Only string
> + * values are supported.
> + *
> + * \note Support for using environment variables to configure libcamera behavior
> + * is provided here mostly for backward compatibility reasons. Introducing new
> + * configuration environment variables is discouraged.
> + *
> + * \return The value retrieved from the given environment if it is set,
> + * otherwise the value from the configuration file if it exists, or no value if
> + * it does not
> + */
> +std::optional<std::string> GlobalConfiguration::envOption(
> +       const char *envVariable,
> +       const std::initializer_list<std::string_view> confPath) const
> +{
> +       const char *envValue = utils::secure_getenv(envVariable);
> +       if (envValue)
> +               return std::optional{ std::string{ envValue } };
> +       return option<std::string>(confPath);
> +}
> +
> +/**
> + * \brief Return values of the configuration option from a file or environment
> + * \param[in] envVariable Environment variable to get the value from
> + * \param[in] confPath The same as in GlobalConfiguration::option
> + * \param[in] delimiter Items separator in the environment variable
> + *
> + * This helper looks first at the given environment variable and if it is
> + * defined (even if it is empty) then it splits its value by semicolons and
> + * returns the resulting list of strings. Otherwise it looks for \a confPath the
> + * same way as in GlobalConfiguration::option, value of which must be a list of
> + * strings.
> + *
> + * \note Support for using environment variables to configure libcamera behavior
> + * is provided here mostly for backward compatibility reasons. Introducing new
> + * configuration environment variables is discouraged.
> + *
> + * \return A vector of strings retrieved from the given environment option or
> + * configuration file or no value if not found; the vector may be empty
> + */
> +std::optional<std::vector<std::string>> GlobalConfiguration::envListOption(
> +       const char *const envVariable,
> +       const std::initializer_list<std::string_view> confPath,
> +       const std::string delimiter) const
> +{
> +       const char *envValue = utils::secure_getenv(envVariable);
> +       if (envValue) {
> +               auto items = utils::split(envValue, delimiter);
> +               return std::vector<std::string>(items.begin(), items.end());
> +       }
> +       return listOption(confPath);
> +}
> +
>  /**
>   * \brief Return configuration version
>   *
> -- 
> 2.51.0
>

Patch
diff mbox series

diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h
index f695498c4..8d09517ed 100644
--- a/include/libcamera/internal/global_configuration.h
+++ b/include/libcamera/internal/global_configuration.h
@@ -8,6 +8,11 @@ 
 #pragma once
 
 #include <filesystem>
+#include <optional>
+#include <string>
+#include <string_view>
+
+#include <libcamera/base/utils.h>
 
 #include "libcamera/internal/yaml_parser.h"
 
@@ -23,6 +28,29 @@  public:
 	unsigned int version() const;
 	Configuration configuration() const;
 
+	template<typename T>
+	std::optional<T> option(
+		const std::initializer_list<std::string_view> confPath) const
+	{
+		const YamlObject *c = &configuration();
+		for (auto part : confPath) {
+			c = &(*c)[part];
+			if (!*c)
+				return {};
+		}
+		return c->get<T>();
+	}
+
+	std::optional<std::vector<std::string>> listOption(
+		const std::initializer_list<std::string_view> confPath) const;
+	std::optional<std::string> envOption(
+		const char *const envVariable,
+		const std::initializer_list<std::string_view> confPath) const;
+	std::optional<std::vector<std::string>> envListOption(
+		const char *const envVariable,
+		const std::initializer_list<std::string_view> confPath,
+		const std::string delimiter = ":") const;
+
 private:
 	bool loadFile(const std::filesystem::path &fileName);
 	void load();
diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp
index d02668111..592edcf30 100644
--- a/src/libcamera/global_configuration.cpp
+++ b/src/libcamera/global_configuration.cpp
@@ -8,8 +8,12 @@ 
 #include "libcamera/internal/global_configuration.h"
 
 #include <filesystem>
+#include <memory>
+#include <optional>
+#include <string>
 #include <string_view>
 #include <sys/types.h>
+#include <vector>
 
 #include <libcamera/base/file.h>
 #include <libcamera/base/log.h>
@@ -43,6 +47,12 @@  LOG_DEFINE_CATEGORY(Configuration)
  * If the first found configuration file cannot be opened or parsed, an error is
  * reported and no configuration file is used. This is to prevent libcamera from
  * using an unintended configuration file.
+ *
+ * The configuration can be accessed using the provided helpers. Namely
+ * GlobalConfiguration::option(), GlobalConfiguration::envOption(),
+ * GlobalConfiguration::listOption(), and GlobalConfiguration::envListOption()
+ * to access individual options, or GlobalConfiguration::configuration() to
+ * access the whole configuration.
  */
 
 bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName)
@@ -110,6 +120,95 @@  GlobalConfiguration::GlobalConfiguration()
  * the underlying type.
  */
 
+/**
+ * \fn std::optional<T> GlobalConfiguration::option(const std::initializer_list<std::string_view> &confPath) const
+ * \brief Return value of the configuration option identified by \a confPath
+ * \param[in] confPath Sequence of the YAML section names (excluding
+ * `configuration') leading to the requested option
+ * \return The value of the configuration item corresponding to \a confPath if
+ * it exists in the configuration file, or no value otherwise
+ */
+
+/**
+ * \brief Return values of the configuration option identified by \a confPath
+ * \tparam T The type of the retrieved configuration value
+ * \param[in] confPath Sequence of the YAML section names (excluding
+ * `configuration') leading to the requested list option, separated by dots
+ * \return A vector of strings or no value if not found
+ */
+std::optional<std::vector<std::string>> GlobalConfiguration::listOption(
+	const std::initializer_list<std::string_view> confPath) const
+{
+	const YamlObject *c = &configuration();
+	for (auto part : confPath) {
+		c = &(*c)[part];
+		if (!*c)
+			return {};
+	}
+	return c->getList<std::string>();
+}
+
+/**
+ * \brief Return value of environment variable with a fallback on the configuration file
+ * \param[in] envVariable Environment variable to get the value from
+ * \param[in] confPath The sequence of YAML section names to fall back on when
+ * \a envVariable is unavailable
+ *
+ * This helper looks first at the given environment variable and if it is
+ * defined then it returns its value (even if it is empty). Otherwise it looks
+ * for \a confPath the same way as in GlobalConfiguration::option. Only string
+ * values are supported.
+ *
+ * \note Support for using environment variables to configure libcamera behavior
+ * is provided here mostly for backward compatibility reasons. Introducing new
+ * configuration environment variables is discouraged.
+ *
+ * \return The value retrieved from the given environment if it is set,
+ * otherwise the value from the configuration file if it exists, or no value if
+ * it does not
+ */
+std::optional<std::string> GlobalConfiguration::envOption(
+	const char *envVariable,
+	const std::initializer_list<std::string_view> confPath) const
+{
+	const char *envValue = utils::secure_getenv(envVariable);
+	if (envValue)
+		return std::optional{ std::string{ envValue } };
+	return option<std::string>(confPath);
+}
+
+/**
+ * \brief Return values of the configuration option from a file or environment
+ * \param[in] envVariable Environment variable to get the value from
+ * \param[in] confPath The same as in GlobalConfiguration::option
+ * \param[in] delimiter Items separator in the environment variable
+ *
+ * This helper looks first at the given environment variable and if it is
+ * defined (even if it is empty) then it splits its value by semicolons and
+ * returns the resulting list of strings. Otherwise it looks for \a confPath the
+ * same way as in GlobalConfiguration::option, value of which must be a list of
+ * strings.
+ *
+ * \note Support for using environment variables to configure libcamera behavior
+ * is provided here mostly for backward compatibility reasons. Introducing new
+ * configuration environment variables is discouraged.
+ *
+ * \return A vector of strings retrieved from the given environment option or
+ * configuration file or no value if not found; the vector may be empty
+ */
+std::optional<std::vector<std::string>> GlobalConfiguration::envListOption(
+	const char *const envVariable,
+	const std::initializer_list<std::string_view> confPath,
+	const std::string delimiter) const
+{
+	const char *envValue = utils::secure_getenv(envVariable);
+	if (envValue) {
+		auto items = utils::split(envValue, delimiter);
+		return std::vector<std::string>(items.begin(), items.end());
+	}
+	return listOption(confPath);
+}
+
 /**
  * \brief Return configuration version
  *