/* streamer-tools OBS Camera Plugin - minimal JSON reader Copyright (C) 2026 CyberCoveLLC 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 #include #include #include 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 v); static Value makeObject(std::map 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 &elements() const { return array_; } private: Type type_ = Type::Invalid; bool bool_ = false; double number_ = 0.0; std::string string_; std::vector array_; std::map 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