Files
obs-streamer-tools-plugin/core/include/stplugin/json.h
T

100 lines
3.6 KiB
C++
Raw Normal View History

/*
streamer-tools OBS Camera Plugin - minimal JSON reader
Copyright (C) 2026 CyberCoveLLC <jknapp85@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
// A deliberately small, strict, allocation-bounded JSON reader.
//
// Why hand-rolled rather than vendoring nlohmann/json: the only JSON this
// plugin ever parses is two small, fixed-shape responses from its own
// server (apps/server/src/obs/plugin.routes.ts in the streamer-tools repo),
// and the parser has to build unmodified on three platforms with no package
// manager step in CI. The scope is small enough to test exhaustively --
// including the malformed inputs a compromised or misconfigured endpoint
// could return, which is the case that must not crash or hang OBS.
//
// Properties this parser guarantees, all covered by core/tests/test_json.cpp:
// - never throws; every failure is reported as Value::invalid()
// - bounded recursion (kMaxDepth) so nesting cannot blow the stack
// - trailing garbage after the top-level value is an error
// - accessors on a wrong-typed value return the caller's default rather
// than aborting, so callers can be written without type interrogation
#include <cstdint>
#include <map>
#include <string>
#include <vector>
namespace stplugin {
namespace json {
/// Maximum nesting depth accepted by parse(). Any deeper input is rejected
/// as invalid rather than recursed into.
constexpr int kMaxDepth = 32;
class Value {
public:
enum class Type { Invalid, Null, Bool, Number, String, Array, Object };
Value() = default;
static Value invalid() { return Value(); }
static Value makeNull();
static Value makeBool(bool v);
static Value makeNumber(double v);
static Value makeString(std::string v);
static Value makeArray(std::vector<Value> v);
static Value makeObject(std::map<std::string, Value> v);
Type type() const { return type_; }
bool valid() const { return type_ != Type::Invalid; }
bool isNull() const { return type_ == Type::Null; }
bool isBool() const { return type_ == Type::Bool; }
bool isNumber() const { return type_ == Type::Number; }
bool isString() const { return type_ == Type::String; }
bool isArray() const { return type_ == Type::Array; }
bool isObject() const { return type_ == Type::Object; }
/// Object member lookup. Returns invalid() for a missing key or when this
/// value is not an object.
const Value &operator[](const std::string &key) const;
/// Array element access. Returns invalid() when out of range or when this
/// value is not an array.
const Value &at(std::size_t index) const;
std::size_t size() const;
/// Typed accessors. Each returns `fallback` when this value is missing or
/// of the wrong type, so callers never have to check first.
std::string asString(const std::string &fallback = std::string()) const;
bool asBool(bool fallback = false) const;
double asNumber(double fallback = 0.0) const;
const std::vector<Value> &elements() const { return array_; }
private:
Type type_ = Type::Invalid;
bool bool_ = false;
double number_ = 0.0;
std::string string_;
std::vector<Value> array_;
std::map<std::string, Value> object_;
};
/// Parse a complete JSON document. Returns Value::invalid() on any syntax
/// error, on trailing non-whitespace content, or on excessive nesting.
/// Never throws.
Value parse(const std::string &text);
} // namespace json
} // namespace stplugin