blob: 3be4fe7bdf9872d4e83c9ebef44ba2955436e83e [file] [log] [blame] [edit]
/* Copyright 2023 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* This is a thin "composer" helper layer in the sense of the
* YAML specification: https://yaml.org/spec/1.1/current.html
*
* It is given a pre-initialised libYAML parser, and generates a
* representation graph from the event stream.
*
* There are multiple restrictions. Examples:
* - Not all event types are handled - example: YAML_ALIAS_EVENT
* - Map keys must be scalars
*/
#ifndef CAMERA_TOOLS_MCTK_YAML_TREE_H_
#define CAMERA_TOOLS_MCTK_YAML_TREE_H_
#include <linux/types.h>
#include <linux/videodev2.h>
#include <stddef.h> /* size_t */
#include <stdio.h>
#include <yaml.h>
#include <memory> /* std::unique_ptr */
#include <optional>
#include <string>
#include <utility> /* std::move, std::pair */
#include <vector>
#include "tools/mctk/selection.h"
class YamlNode;
class YamlEmpty;
class YamlScalar;
class YamlSequence;
class YamlMap;
class YamlNode {
public:
virtual ~YamlNode() {}
/* Recursively convert an input to a YAML tree, depending on what data
* is at hand:
* - a FILE (feeding it into libyaml),
* - a libyaml parser, or
* - a libyaml parser and the last read event (used internally).
*/
static std::unique_ptr<YamlNode> FromFile(FILE& file);
static std::unique_ptr<YamlNode> FromParser(yaml_parser_t& parser);
static std::unique_ptr<YamlNode> FromParserEvent(yaml_parser_t& parser,
yaml_event_t& event);
/* Recursively convert a YAML tree to a FILE. */
bool ToFile(FILE& file);
virtual bool Emit(yaml_emitter_t& emitter) = 0;
/* Check if this is a YamlEmpty node */
bool IsEmpty();
/* Syntactic sugar for lookups.
*
* If the node is a YamlSequence, integer lookups returns the n-th node.
* Otherwise a YamlEmpty node.
*
* If the node is a YamlMap, string lookups return the node with that key.
* Otherwise a YamlEmpty node.
*/
YamlNode& operator[](size_t index);
YamlNode& operator[](std::string key);
/* Getters for basic types in std::optional form.
* Returns std:nullopt if trying to read from an empty node.
*/
template <typename T>
std::optional<T> Read();
/* Parse a whole array of the same basic type, but only if the
* array size matches the expected number of elements.
*/
template <typename T>
std::optional<std::vector<T>> ReadArray(size_t expected_count);
/* Getters for batch parsing.
* These handle bare values, instead of encapsulating them in std::optional.
* The passed reference to a bool is not touched if the read succeeds.
* On failure, the bool is set to false, and an undefined value is returned.
*
* ReadInt() is a template, as it specialises on uint64_t, int32_t, and more.
*/
template <typename T>
T ReadInt(bool& ok);
template <typename T>
void ReadCArray(T* dest, size_t expected_count, bool& ok);
/* destlen is the maximum destination buffer length.
* It will always be NUL-terminated.
*/
void ReadCString(char* dest, size_t destlen, bool& ok);
/* Return a vector of nodes contained in a sequence node.
* If it is not a sequence, an empty vector is returned.
*/
std::vector<std::unique_ptr<YamlNode>>& ReadSequence();
/* Getters for common composite V4L data types */
std::optional<struct v4l2_rect> ReadRect();
void ReadSelection(V4lMcSelection& dest);
};
/*
* The empty YAML node is merely implementation specific syntactical sugar.
* It allows full-path lookups to fail gracefully if an intermediary node
* does not exist, enabling batch parsing:
*
* std::optional<__u32> value = root["key1"][42]["key2"].Read<__u32>();
*/
class YamlEmpty : public YamlNode {
public:
bool Emit(yaml_emitter_t& emitter);
};
/*
* YAML scalars are leaf nodes containing an actual value
*/
class YamlScalar : public YamlNode {
public:
explicit YamlScalar(std::string value) : value_(std::move(value)) {}
static std::unique_ptr<YamlScalar> FromEvent(yaml_event_t& event);
bool Emit(yaml_emitter_t& emitter);
template <typename T>
std::optional<T> Read();
/* The actual value stored in this node */
const std::string value_;
};
/*
* YAML sequences act like vectors/lists
*/
class YamlSequence : public YamlNode {
public:
static std::unique_ptr<YamlSequence> FromParserEvent(
yaml_parser_t& parser, yaml_event_t& start_event);
bool Emit(yaml_emitter_t& emitter);
template <typename T>
std::optional<std::vector<T>> ReadArray(size_t expected_count);
/* The actual list of nodes */
std::vector<std::unique_ptr<YamlNode>> list_;
private:
bool ParseOneListElement(yaml_parser_t& parser);
int implicit_;
yaml_sequence_style_t style_;
};
/*
* YAML mappings act like maps/dictionaries
*/
typedef std::pair<std::string, std::unique_ptr<YamlNode>> YamlMapPair;
class YamlMap : public YamlNode {
public:
static std::unique_ptr<YamlMap> FromParserEvent(yaml_parser_t& parser,
yaml_event_t& start_event);
bool Emit(yaml_emitter_t& emitter);
/* The actual map of nodes */
std::vector<YamlMapPair> map_;
private:
int implicit_;
yaml_mapping_style_t style_;
};
#endif // CAMERA_TOOLS_MCTK_YAML_TREE_H_