Files
external_libcamera/include/libcamera/internal/yaml_parser.h
Stefan Klug 6b67094cd2 libcamera: yaml-parser: Differentiate between empty and empty string
When accessing a nonexistent key on a dict the YamlObject returns an
empty element. This element can happily be cast to a string which is
unexpected. For example the following statement:

yamlDict["nonexistent"].get<string>("default")

is expected to return "default" but actually returns "". Fix this by
introducing an empty type to distinguish between an empty YamlObject and
a YamlObject of type value containing an empty string. For completeness
add an isEmpty() function and an explicit cast to bool to be able to
test for that type.

Extend the tests accordingly.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
2024-09-23 13:03:35 +02:00

245 lines
4.3 KiB
C++

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2022, Google Inc.
*
* libcamera YAML parsing helper
*/
#pragma once
#include <iterator>
#include <map>
#include <optional>
#include <stdint.h>
#include <string>
#include <vector>
#include <libcamera/base/class.h>
#include <libcamera/geometry.h>
namespace libcamera {
class File;
class YamlParserContext;
class YamlObject
{
private:
struct Value {
Value(std::string &&k, std::unique_ptr<YamlObject> &&v)
: key(std::move(k)), value(std::move(v))
{
}
std::string key;
std::unique_ptr<YamlObject> value;
};
using Container = std::vector<Value>;
using ListContainer = std::vector<std::unique_ptr<YamlObject>>;
public:
#ifndef __DOXYGEN__
template<typename Derived>
class Iterator
{
public:
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
Iterator(typename Container::const_iterator it)
: it_(it)
{
}
Derived &operator++()
{
++it_;
return *static_cast<Derived *>(this);
}
Derived operator++(int)
{
Derived it = *static_cast<Derived *>(this);
it_++;
return it;
}
friend bool operator==(const Iterator &a, const Iterator &b)
{
return a.it_ == b.it_;
}
friend bool operator!=(const Iterator &a, const Iterator &b)
{
return a.it_ != b.it_;
}
protected:
Container::const_iterator it_;
};
template<typename Iterator>
class Adapter
{
public:
Adapter(const Container &container)
: container_(container)
{
}
Iterator begin() const
{
return Iterator{ container_.begin() };
}
Iterator end() const
{
return Iterator{ container_.end() };
}
protected:
const Container &container_;
};
class ListIterator : public Iterator<ListIterator>
{
public:
using value_type = const YamlObject &;
using pointer = const YamlObject *;
using reference = value_type;
value_type operator*() const
{
return *it_->value.get();
}
pointer operator->() const
{
return it_->value.get();
}
};
class DictIterator : public Iterator<DictIterator>
{
public:
using value_type = std::pair<const std::string &, const YamlObject &>;
using pointer = value_type *;
using reference = value_type &;
value_type operator*() const
{
return { it_->key, *it_->value.get() };
}
};
class DictAdapter : public Adapter<DictIterator>
{
public:
using key_type = std::string;
};
class ListAdapter : public Adapter<ListIterator>
{
};
#endif /* __DOXYGEN__ */
YamlObject();
~YamlObject();
bool isValue() const
{
return type_ == Type::Value;
}
bool isList() const
{
return type_ == Type::List;
}
bool isDictionary() const
{
return type_ == Type::Dictionary;
}
bool isEmpty() const
{
return type_ == Type::Empty;
}
explicit operator bool() const
{
return type_ != Type::Empty;
}
std::size_t size() const;
template<typename T>
std::optional<T> get() const
{
return Getter<T>{}.get(*this);
}
template<typename T, typename U>
T get(U &&defaultValue) const
{
return get<T>().value_or(std::forward<U>(defaultValue));
}
#ifndef __DOXYGEN__
template<typename T,
std::enable_if_t<
std::is_same_v<bool, T> ||
std::is_same_v<float, T> ||
std::is_same_v<double, T> ||
std::is_same_v<int8_t, T> ||
std::is_same_v<uint8_t, T> ||
std::is_same_v<int16_t, T> ||
std::is_same_v<uint16_t, T> ||
std::is_same_v<int32_t, T> ||
std::is_same_v<uint32_t, T> ||
std::is_same_v<std::string, T> ||
std::is_same_v<Size, T>> * = nullptr>
#else
template<typename T>
#endif
std::optional<std::vector<T>> getList() const;
DictAdapter asDict() const { return DictAdapter{ list_ }; }
ListAdapter asList() const { return ListAdapter{ list_ }; }
const YamlObject &operator[](std::size_t index) const;
bool contains(const std::string &key) const;
const YamlObject &operator[](const std::string &key) const;
private:
LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
template<typename T>
friend struct Getter;
friend class YamlParserContext;
enum class Type {
Dictionary,
List,
Value,
Empty,
};
template<typename T>
struct Getter {
std::optional<T> get(const YamlObject &obj) const;
};
Type type_;
std::string value_;
Container list_;
std::map<std::string, YamlObject *> dictionary_;
};
class YamlParser final
{
public:
static std::unique_ptr<YamlObject> parse(File &file);
};
} /* namespace libcamera */