Code View

fson / source / fson-1.1.0.0 / libs / internal / sdk / fson / Document.hh
// SPDX-License-Identifier: MIT
#pragma once

#include <memory>
#include <optional>
#include <stdexcept>
#include <vector>

#include "Comment.hh"
#include "Object.hh"

namespace fedem
{
  namespace fson
  {
    // ─────────────────────────────────────────────────────────────────────
    // Document (docs/notation/00005-fson_file.rrd)
    //
    // A complete FSON file: a root value surrounded by optional trivia.
    //   header : comments before the root value
    //   footer : comments after the root value
    // The root is never null. Since v0.9.0.0 (JSON5 superset, decision
    // A.2a) the root may be ANY value, not just an object — a bare array,
    // string, number, boolean, or null is a valid FSON file. getRoot()
    // is kept for source compatibility with object-rooted documents: it
    // throws std::out_of_range when the root is not an Object. Callers
    // that need to handle non-object roots should use getRootObject(),
    // which returns nullptr instead of throwing.
    // ─────────────────────────────────────────────────────────────────────
    class Document final
    {
      public:
        Document( );

        // Throws std::out_of_range when the root is not an Object.
        Object&       getRoot( );
        Object const& getRoot( ) const;

        // Returns nullptr when the root is not an Object.
        Object*       getRootObject( )       noexcept;
        Object const* getRootObject( ) const noexcept;

        Value&       getRootValue( )       noexcept;
        Value const& getRootValue( ) const noexcept;

        // Throws std::invalid_argument when root is null.
        void setRoot( std::unique_ptr< Value > root );

        CommentList&       getHeaderComments( )       noexcept;
        CommentList const& getHeaderComments( ) const noexcept;

        CommentList&       getFooterComments( )       noexcept;
        CommentList const& getFooterComments( ) const noexcept;

        // ── path API forwarders (see Object for path syntax) ────────────
        // All of these operate on getRootObject(): when the root is not
        // an Object, findPath()/setPath() return nullptr, containsPath()/
        // removePath() return false, the typed getters return fallback,
        // and atPath() throws std::out_of_range (same as a not-found path).
        Value*       findPath( std::string const& path )       noexcept;
        Value const* findPath( std::string const& path ) const noexcept;
        bool containsPath( std::string const& path ) const noexcept;
        Value&       atPath( std::string const& path );
        Value const& atPath( std::string const& path ) const;
        Value* setPath( std::string const& path, std::unique_ptr< Value > value );
        bool removePath( std::string const& path ) noexcept;

        // ── provenance forwarders (see Object) ──────────────────────────
        // All operate on getRootObject(); when the root is not an object,
        // isLocal() returns false, provenanceOf() returns nullopt, and
        // provenanceMap() returns an empty vector.
        bool isLocal( std::string const& path ) const noexcept;
        std::optional< Object::Provenance >
          provenanceOf( std::string const& path ) const;
        std::vector< Object::Provenance > provenanceMap( ) const;

        std::string getString ( std::string const& path, std::string const& fallback = std::string( ) ) const;
        long long   getInteger( std::string const& path, long long fallback = 0LL ) const noexcept;
        double      getDouble ( std::string const& path, double fallback = 0.0 ) const noexcept;
        bool        getBoolean( std::string const& path, bool fallback = false ) const noexcept;

      private:
        CommentList               header;
        std::unique_ptr< Value >  root;
        CommentList               footer;

        Document( Document const& ) = delete;
        Document( Document&&      ) = delete;
        Document& operator=( Document const& ) = delete;
        Document& operator=( Document&&      ) = delete;
    };

    inline Document::Document( )
    : root( std::make_unique< Object >( ) )
    {
    }

    inline Object& Document::getRoot( )
    {
      Object* object = root->asObject( );
      if( !object )
        throw std::out_of_range( "fson::Document: root is not an object" );
      return *object;
    }

    inline Object const& Document::getRoot( ) const
    {
      Object const* object = root->asObject( );
      if( !object )
        throw std::out_of_range( "fson::Document: root is not an object" );
      return *object;
    }

    inline Object* Document::getRootObject( ) noexcept
    {
      return root->asObject( );
    }

    inline Object const* Document::getRootObject( ) const noexcept
    {
      return root->asObject( );
    }

    inline Value& Document::getRootValue( ) noexcept
    {
      return *root;
    }

    inline Value const& Document::getRootValue( ) const noexcept
    {
      return *root;
    }

    inline void Document::setRoot( std::unique_ptr< Value > root )
    {
      if( !root )
        throw std::invalid_argument( "fson::Document: root must not be null" );
      this->root = std::move( root );
    }

    inline CommentList& Document::getHeaderComments( ) noexcept
    {
      return header;
    }

    inline CommentList const& Document::getHeaderComments( ) const noexcept
    {
      return header;
    }

    inline CommentList& Document::getFooterComments( ) noexcept
    {
      return footer;
    }

    inline CommentList const& Document::getFooterComments( ) const noexcept
    {
      return footer;
    }

    inline Value* Document::findPath( std::string const& path ) noexcept
    {
      Object* object = getRootObject( );
      return object ? object->findPath( path ) : nullptr;
    }

    inline Value const* Document::findPath( std::string const& path ) const noexcept
    {
      Object const* object = getRootObject( );
      return object ? object->findPath( path ) : nullptr;
    }

    inline bool Document::containsPath( std::string const& path ) const noexcept
    {
      Object const* object = getRootObject( );
      return object && object->containsPath( path );
    }

    inline Value& Document::atPath( std::string const& path )
    {
      Object* object = getRootObject( );
      if( !object )
        throw std::out_of_range( "fson::Document: root is not an object" );
      return object->atPath( path );
    }

    inline Value const& Document::atPath( std::string const& path ) const
    {
      Object const* object = getRootObject( );
      if( !object )
        throw std::out_of_range( "fson::Document: root is not an object" );
      return object->atPath( path );
    }

    inline Value* Document::setPath( std::string const& path,
                                     std::unique_ptr< Value > value )
    {
      Object* object = getRootObject( );
      return object ? object->setPath( path, std::move( value ) ) : nullptr;
    }

    inline bool Document::removePath( std::string const& path ) noexcept
    {
      Object* object = getRootObject( );
      return object && object->removePath( path );
    }

    inline bool Document::isLocal( std::string const& path ) const noexcept
    {
      Object const* object = getRootObject( );
      return object && object->isLocal( path );
    }

    inline std::optional< Object::Provenance >
      Document::provenanceOf( std::string const& path ) const
    {
      Object const* object = getRootObject( );
      return object ? object->provenanceOf( path ) : std::nullopt;
    }

    inline std::vector< Object::Provenance > Document::provenanceMap( ) const
    {
      Object const* object = getRootObject( );
      return object ? object->provenanceMap( ) : std::vector< Object::Provenance >( );
    }

    inline std::string Document::getString( std::string const& path,
                                            std::string const& fallback ) const
    {
      Object const* object = getRootObject( );
      return object ? object->getString( path, fallback ) : fallback;
    }

    inline long long Document::getInteger( std::string const& path,
                                           long long fallback ) const noexcept
    {
      Object const* object = getRootObject( );
      return object ? object->getInteger( path, fallback ) : fallback;
    }

    inline double Document::getDouble( std::string const& path,
                                       double fallback ) const noexcept
    {
      Object const* object = getRootObject( );
      return object ? object->getDouble( path, fallback ) : fallback;
    }

    inline bool Document::getBoolean( std::string const& path,
                                      bool fallback ) const noexcept
    {
      Object const* object = getRootObject( );
      return object ? object->getBoolean( path, fallback ) : fallback;
    }
  }  // end namespace fson
}  // end namespace fedem