Program Listing for File toml.h#

Return to documentation for file (support\toml.h)

/********************************************************************************
 * Copyright (c) 2025-2026 ZF Friedrichshafen AG
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contributors:
 *   Erik Verhoeven - initial API and implementation
 ********************************************************************************/

#ifndef SDV_TOML_H
#define SDV_TOML_H

#include <charconv>
#include "../interfaces/toml.h"
#include "interface_ptr.h"
#include "local_service_access.h"
#include "interface_ptr.h"

namespace sdv::toml
{
    class CNode
    {
    public:
        CNode() = default;

        CNode(const TInterfaceAccessPtr& rptrNode);

        CNode& operator=(const TInterfaceAccessPtr& rptrNode);

        virtual bool IsValid() const;

        virtual operator bool() const;

        TInterfaceAccessPtr GetInterface();

        sdv::u8string GetName() const;

        sdv::u8string GetQualifiedPath() const;

        ENodeType GetType() const;

        uint32_t GetIndex() const;

        bool IsInline() const;

        std::string GetComment() const;

        void SetComment(const std::string& rssComment);

        sdv::any_t GetValue() const;

        std::string GetValueAsString() const;

        std::filesystem::path GetValueAsPath() const;

        bool SetValue(const sdv::any_t& ranyValue);

        bool Delete();

        virtual void Clear();

        void AutomaticFormat(bool bRemoveComments);

        sdv::u8string GetTOML() const;

    protected:
        TInterfaceAccessPtr     m_ptrNode;
        INodeInfo*              m_pNodeInfo = nullptr;
    };

    class CNodeCollection : public CNode
    {
    public:
        CNodeCollection() = default;

        CNodeCollection(const TInterfaceAccessPtr& rptrNode);

        CNodeCollection(const CNode& rNode);

        CNodeCollection& operator=(const TInterfaceAccessPtr& rptrNode);

        CNodeCollection& operator=(const CNode& rNode);

        virtual bool IsValid() const override;

        virtual operator bool() const override;

        size_t GetCount() const;

        CNode Get(size_t nIndex) const;

        CNode operator[](size_t nIndex) const;

        virtual void Clear() override;

        CNode GetDirect(const sdv::u8string& rssNode) const;

        std::string GetNodeNameByIndex(size_t nIndex) const;

        CNode InsertValue(const std::string& rssInsertBefore, const std::string& rssName, const sdv::any_t& ranyValue);

        CNode AddValue(const std::string& rssName, const sdv::any_t& ranyValue);

        CNodeCollection InsertArray(const std::string& rssInsertBefore, const std::string& rssName);

        CNodeCollection AddArray(const std::string& rssName);

        CNodeCollection InsertTable(const std::string& rssInsertBefore, const std::string& rssName, bool bFavorInline = false);

        CNodeCollection AddTable(const std::string& rssName, bool bFavorInline = false);

        CNodeCollection InsertTableArray(const std::string& rssInsertBefore, const std::string& rssName, bool bFavorInline = false);

        CNodeCollection AddTableArray(const std::string& rssName, bool bFavorInline = false);

        int InsertTOML(const std::string& rssInsertBefore, const std::string& rssTOML, bool bAllowPartial = false);

        int AddTOML(const std::string& rssTOML, bool bAllowPartial = false);

    private:
        INodeCollection*    m_pCollection = nullptr;
    };

    class CTOMLParser : public CNodeCollection
    {
    public:
        CTOMLParser(const std::string& rssConfig = "");

        bool Process(const std::string& rssConfig);

        virtual bool IsValid() const override;

        virtual operator bool() const override;

        // Ignore cppcheck warning for not using dynamic binding when being called through the destructor.
        // cppcheck-suppress virtualCallInConstructor
        virtual void Clear() override;

    private:
        TObjectPtr      m_ptrParserUtil;
        ITOMLParser*    m_pParser = nullptr;
    };

    enum class ECompareFlags : uint32_t
    {
        compare_ignore_whitespace = 1,
        compare_ignore_comments = 2,
        compare_ignore_inline = 8,
        compare_ignore_all = 255,
    };

    enum class ECompareResult : int32_t
    {
        compare_identical = 0,
        compare_different = 1,
        compare_error = -1
    };

    namespace internal
    {
        ECompareResult CompareNodes(CNode& rnode1, CNode& rnode2,
            uint32_t uiCompareFlags = static_cast<uint32_t>(ECompareFlags::compare_ignore_all));
    } // namespace internal;

    ECompareResult Compare(const std::string& rssToml1, const std::string& rssToml2,
        uint32_t uiCompareFlags = static_cast<uint32_t>(ECompareFlags::compare_ignore_all));

    inline CNode::CNode(const TInterfaceAccessPtr& rptrNode)
    {
        m_pNodeInfo = rptrNode.GetInterface<INodeInfo>();
        if (!m_pNodeInfo) return;
        m_ptrNode = rptrNode;
    }

    inline CNode& CNode::operator=(const TInterfaceAccessPtr& rptrNode)
    {
        CNode::Clear();
        m_pNodeInfo = rptrNode.GetInterface<INodeInfo>();
        if (!m_pNodeInfo)
            return *this;
        m_ptrNode = rptrNode;
        return *this;
    }

    inline bool CNode::IsValid() const
    {
        return m_pNodeInfo ? true : false;
    }

    inline CNode::operator bool() const
    {
        return m_pNodeInfo ? true : false;
    }

    inline TInterfaceAccessPtr CNode::GetInterface()
    {
        return m_ptrNode;
    }

    inline sdv::u8string CNode::GetName() const
    {
        return m_pNodeInfo ? m_pNodeInfo->GetName() : sdv::u8string();
    }

    inline sdv::u8string CNode::GetQualifiedPath() const
    {
        return m_pNodeInfo ? m_pNodeInfo->GetPath(true) : sdv::u8string();
    }

    inline ENodeType CNode::GetType() const
    {
        return m_pNodeInfo ? m_pNodeInfo->GetType() : ENodeType::node_invalid;
    }

    inline uint32_t CNode::GetIndex() const
    {
        return m_pNodeInfo ? m_pNodeInfo->GetIndex() : npos;
    }

    inline bool CNode::IsInline() const
    {
        return m_pNodeInfo ? m_pNodeInfo->IsInline() : true;
    }

    inline std::string CNode::GetComment() const
    {
        if (!m_pNodeInfo) return {};
        std::string ssComment;
        switch (GetType())
        {
        case ENodeType::node_boolean:
        case ENodeType::node_integer:
        case ENodeType::node_floating_point:
        case ENodeType::node_string:
            ssComment = m_pNodeInfo->GetComment(INodeInfo::ECommentType::comment_behind);
            if (ssComment.empty())
                ssComment = m_pNodeInfo->GetComment(INodeInfo::ECommentType::comment_before);
            break;
        default:
            ssComment = m_pNodeInfo->GetComment(INodeInfo::ECommentType::comment_before);
            if (ssComment.empty())
                ssComment = m_pNodeInfo->GetComment(INodeInfo::ECommentType::comment_behind);
            break;
        }
        return ssComment;
    }

    inline void CNode::SetComment(const std::string& rssComment)
    {
        if (!m_pNodeInfo) return;
        switch (GetType())
        {
        case ENodeType::node_boolean:
        case ENodeType::node_integer:
        case ENodeType::node_floating_point:
        case ENodeType::node_string:
            m_pNodeInfo->SetComment(INodeInfo::ECommentType::comment_behind, rssComment);
            break;
        default:
            m_pNodeInfo->SetComment(INodeInfo::ECommentType::comment_before, rssComment);
            break;
        }
    }

    inline sdv::any_t CNode::GetValue() const
    {
        return m_pNodeInfo ? m_pNodeInfo->GetValue() : sdv::any_t();
    }

    inline std::string CNode::GetValueAsString() const
    {
        return m_pNodeInfo ? m_pNodeInfo->GetValue().get<std::string>() : std::string();
    }

    inline std::filesystem::path CNode::GetValueAsPath() const
    {
        return m_pNodeInfo ? m_pNodeInfo->GetValue().get<std::filesystem::path>() : std::filesystem::path();
    }

    inline bool CNode::SetValue(const sdv::any_t& ranyValue)
    {
        INodeUpdate* pNodeUpdate = m_ptrNode.GetInterface<INodeUpdate>();
        if (!pNodeUpdate) return false;
        return pNodeUpdate->ChangeValue(ranyValue);
    }

    inline bool CNode::Delete()
    {
        INodeUpdate* pNodeUpdate = m_ptrNode.GetInterface<INodeUpdate>();
        if (!pNodeUpdate) return false;
        bool bRet = pNodeUpdate->DeleteNode();
        Clear();    // Not valid any more.
        return bRet;
    }

    inline void CNode::Clear()
    {
        m_ptrNode = nullptr;
        m_pNodeInfo = nullptr;
    }

    inline void CNode::AutomaticFormat(bool bRemoveComments)
    {
        if (m_pNodeInfo) m_pNodeInfo->AutomaticFormat(bRemoveComments);
    }

    inline sdv::u8string CNode::GetTOML() const
    {
        return m_pNodeInfo ? m_pNodeInfo->GetTOML() : sdv::u8string();
    }

    inline CNodeCollection::CNodeCollection(const TInterfaceAccessPtr& rptrNode) : CNode(rptrNode)
    {
        m_pCollection = rptrNode.GetInterface<INodeCollection>();
        if (!m_pCollection) CNode::Clear();
    }

    inline CNodeCollection::CNodeCollection(const CNode& rNode) : CNode(rNode)
    {
        m_pCollection = m_ptrNode.GetInterface<INodeCollection>();
        if (!m_pCollection) CNode::Clear();
    }

    inline CNodeCollection& CNodeCollection::operator=(const TInterfaceAccessPtr& rptrNode)
    {
        CNode::operator=(rptrNode);
        m_pCollection = rptrNode.GetInterface<INodeCollection>();
        if (!m_pCollection) CNode::Clear();
        return *this;
    }

    inline CNodeCollection& CNodeCollection::operator=(const CNode& rNode)
    {
        CNode::operator=(rNode);
        m_pCollection = m_ptrNode.GetInterface<INodeCollection>();
        if (!m_pCollection) CNode::Clear();
        return *this;
    }

    inline bool CNodeCollection::IsValid() const
    {
        return m_pCollection ? true : false;
    }

    inline CNodeCollection::operator bool() const
    {
        return m_pCollection ? true : false;
    }

    inline size_t CNodeCollection::GetCount() const
    {
        return m_pCollection ? m_pCollection->GetCount() : 0;
    }

    inline CNode CNodeCollection::Get(size_t nIndex) const
    {
        return m_pCollection ? CNode(m_pCollection->GetNode(static_cast<uint32_t>(nIndex))) : CNode();
    }

    inline CNode CNodeCollection::operator[](size_t nIndex) const
    {
        return m_pCollection ? CNode(m_pCollection->GetNode(static_cast<uint32_t>(nIndex))) : CNode();
    }

    inline void CNodeCollection::Clear()
    {
        CNode::Clear();
        m_pCollection = nullptr;
    }

    inline CNode CNodeCollection::GetDirect(const sdv::u8string& rssNode) const
    {
        return m_pCollection ? CNode(m_pCollection->GetNodeDirect(rssNode)) : CNode();
    }

    inline std::string CNodeCollection::GetNodeNameByIndex(size_t nIndex) const
    {
        if (!m_pCollection) return {};
        if (nIndex >= m_pCollection->GetCount()) return {};
        if (GetType() == ENodeType::node_array) return "[" + std::to_string(nIndex) + "]";
        TInterfaceAccessPtr ptrNode = m_pCollection->GetNode(static_cast<uint32_t>(nIndex));
        const auto* pAccess = ptrNode.GetInterface<INodeInfo>();
        if (!pAccess) return {};
        return pAccess->GetName();
    }

    inline CNode CNodeCollection::InsertValue(const std::string& rssInsertBefore, const std::string& rssName,
        const sdv::any_t& ranyValue)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return {};
        return CNode(pInsert->InsertValue(rssInsertBefore, rssName, ranyValue));
    }

    inline CNode CNodeCollection::AddValue(const std::string& rssName, const sdv::any_t& ranyValue)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return {};
        return CNode(pInsert->InsertValue("", rssName, ranyValue));
    }

    inline CNodeCollection CNodeCollection::InsertArray(const std::string& rssInsertBefore, const std::string& rssName)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return {};
        return CNodeCollection(pInsert->InsertArray(rssInsertBefore, rssName));
    }

    inline CNodeCollection CNodeCollection::AddArray(const std::string& rssName)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return {};
        return CNodeCollection(pInsert->InsertArray("", rssName));
    }

    inline CNodeCollection CNodeCollection::InsertTable(const std::string& rssInsertBefore, const std::string& rssName,
        bool bFavorInline /*= false*/)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return {};
        return CNodeCollection(pInsert->InsertTable(rssInsertBefore, rssName, bFavorInline ? EInsertPreference::prefer_inline :
                EInsertPreference::prefer_standard));
    }

    inline CNodeCollection CNodeCollection::AddTable(const std::string& rssName, bool bFavorInline /*= false*/)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return {};
        return CNodeCollection(pInsert->InsertTable("", rssName, bFavorInline ? EInsertPreference::prefer_inline :
                EInsertPreference::prefer_standard));
    }

    inline CNodeCollection CNodeCollection::InsertTableArray(const std::string& rssInsertBefore, const std::string& rssName,
        bool bFavorInline /*= false*/)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return {};
        return CNodeCollection(pInsert->InsertTableArray(rssInsertBefore, rssName, bFavorInline ? EInsertPreference::prefer_inline :
                EInsertPreference::prefer_standard));
    }

    inline CNodeCollection CNodeCollection::AddTableArray(const std::string& rssName, bool bFavorInline /*= false*/)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return {};
        return CNodeCollection(pInsert->InsertTableArray("", rssName, bFavorInline ? EInsertPreference::prefer_inline :
                EInsertPreference::prefer_standard));
    }

    inline int CNodeCollection::InsertTOML(const std::string& rssInsertBefore, const std::string& rssTOML,
        bool bAllowPartial /*= false*/)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return 0;
        INodeCollectionInsert::EInsertResult eRet = pInsert->InsertTOML(rssInsertBefore, rssTOML, !bAllowPartial);
        switch (eRet)
        {
        case INodeCollectionInsert::EInsertResult::insert_success:
            return 1;
        case INodeCollectionInsert::EInsertResult::insert_partly_success:
            return -1;
        case INodeCollectionInsert::EInsertResult::insert_fail:
        default:
            return 0;
        }
    }

    inline int CNodeCollection::AddTOML(const std::string& rssTOML, bool bAllowPartial /*= false*/)
    {
        INodeCollectionInsert* pInsert = m_ptrNode.GetInterface<INodeCollectionInsert>();
        if (!pInsert) return 0;
        INodeCollectionInsert::EInsertResult eRet = pInsert->InsertTOML("", rssTOML, !bAllowPartial);
        switch (eRet)
        {
        case INodeCollectionInsert::EInsertResult::insert_success:
            return 1;
        case INodeCollectionInsert::EInsertResult::insert_partly_success:
            return -1;
        case INodeCollectionInsert::EInsertResult::insert_fail:
        default:
            return 0;
        }
    }

    inline CTOMLParser::CTOMLParser(const std::string& rssConfig)
    {
        Process(rssConfig);
    }

    inline bool CTOMLParser::Process(const std::string& rssConfig)
    {
        Clear();
        m_ptrParserUtil = sdv::core::CreateUtility("TOMLParserUtility");
        m_pParser = m_ptrParserUtil.GetInterface<ITOMLParser>();
        if (m_pParser)
        {
            try
            {
                m_pParser->Process(rssConfig);
                CNodeCollection::operator=(m_ptrParserUtil);
            }
            catch (const sdv::toml::XTOMLParseException&)
            {
                Clear();
                return false;
            }
        }
        return IsValid();
    }

    inline bool CTOMLParser::IsValid() const
    {
        return m_pParser ? true : false;
    }

    inline CTOMLParser::operator bool() const
    {
        return m_pParser ? true : false;
    }

    inline void CTOMLParser::Clear()
    {
        m_pParser = nullptr;
        m_ptrParserUtil.Clear();
    }

    namespace internal
    {
        inline ECompareResult CompareNodes(CNode& rnode1, CNode& rnode2,
            uint32_t uiCompareFlags /*= static_cast<uint32_t>(ECompareFlags::compare_ignore_all)*/)
        {
            if (!rnode1 || !rnode2) return ECompareResult::compare_error;

            // Format the nodes when ignore whitespace and/or comments
            bool bIgnoreComments = uiCompareFlags & static_cast<uint32_t>(ECompareFlags::compare_ignore_comments);
            bool bIgnoreWhitespace = bIgnoreComments ||
                (uiCompareFlags & static_cast<uint32_t>(ECompareFlags::compare_ignore_whitespace));
            if (bIgnoreWhitespace)
            {
                INodeInfo* pNodeInfo1 = rnode1.GetInterface().GetInterface<INodeInfo>();
                if (!pNodeInfo1) return ECompareResult::compare_error;
                pNodeInfo1->AutomaticFormat(bIgnoreComments);
                INodeInfo* pNodeInfo2 = rnode2.GetInterface().GetInterface<INodeInfo>();
                if (!pNodeInfo2) return ECompareResult::compare_error;
                pNodeInfo2->AutomaticFormat(bIgnoreComments);
            }

            // Convert to standard if ignoring inline
            INodeCollectionConvert* pConvert1 = rnode1.GetInterface().GetInterface<INodeCollectionConvert>();
            INodeCollectionConvert* pConvert2 = rnode2.GetInterface().GetInterface<INodeCollectionConvert>();
            if (pConvert1 && pConvert2 && uiCompareFlags & static_cast<uint32_t>(ECompareFlags::compare_ignore_inline))
            {
                // Making inline nodes as standard, might only change the upper node.
                // Making standard nodes inline, will have all child nodes be made inline as well, because inline nodes can only
                // have inline nodes.
                pConvert1->MakeInline();
                pConvert2->MakeInline();
            }

            // Generate the TOMLs and compare
            return rnode1.GetTOML() == rnode2.GetTOML() ? ECompareResult::compare_identical : ECompareResult::compare_different;
        }
    } // namespace internal

    inline ECompareResult Compare(const std::string& rssToml1, const std::string& rssToml2,
        uint32_t uiCompareFlags /*= static_cast<uint32_t>(ECompareFlags::compare_ignore_all)*/)
    {
        try
        {
            CTOMLParser parser1(rssToml1);
            CTOMLParser parser2(rssToml2);
            return internal::CompareNodes(parser1, parser2, uiCompareFlags);
        }
        catch (const sdv::toml::XTOMLParseException&)
        {
            return ECompareResult::compare_error;
        }
    }

}

#endif // !defined SDV_TOML_H