diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2023-05-05 11:09:01 +0300 |
---|---|---|
committer | Daniil Cherednik <dan.cherednik@gmail.com> | 2023-05-05 11:09:01 +0300 |
commit | b5a989b16cafa8a3b3bc076f1097a0eda6f48c06 (patch) | |
tree | 4da744117a5aab37758921fa43b95a3068e5aec1 /library/cpp/yaml/fyamlcpp | |
parent | fc1cffcfa7f0497a1f97b384a24bcbf23362f3be (diff) | |
download | ydb-b5a989b16cafa8a3b3bc076f1097a0eda6f48c06.tar.gz |
Ydb stable 23-1-2623.1.26
x-stable-origin-commit: 22184a7e157553d447f17a2dffc4ea2d32dfd74d
Diffstat (limited to 'library/cpp/yaml/fyamlcpp')
-rw-r--r-- | library/cpp/yaml/fyamlcpp/CMakeLists.darwin.txt | 18 | ||||
-rw-r--r-- | library/cpp/yaml/fyamlcpp/CMakeLists.linux-aarch64.txt | 19 | ||||
-rw-r--r-- | library/cpp/yaml/fyamlcpp/CMakeLists.linux.txt | 19 | ||||
-rw-r--r-- | library/cpp/yaml/fyamlcpp/CMakeLists.txt | 15 | ||||
-rw-r--r-- | library/cpp/yaml/fyamlcpp/fyamlcpp.cpp | 969 | ||||
-rw-r--r-- | library/cpp/yaml/fyamlcpp/fyamlcpp.h | 636 | ||||
-rw-r--r-- | library/cpp/yaml/fyamlcpp/fyamlcpp_ut.cpp | 156 | ||||
-rw-r--r-- | library/cpp/yaml/fyamlcpp/libfyaml_ut.cpp | 1838 |
8 files changed, 3670 insertions, 0 deletions
diff --git a/library/cpp/yaml/fyamlcpp/CMakeLists.darwin.txt b/library/cpp/yaml/fyamlcpp/CMakeLists.darwin.txt new file mode 100644 index 0000000000..4db87e9d0d --- /dev/null +++ b/library/cpp/yaml/fyamlcpp/CMakeLists.darwin.txt @@ -0,0 +1,18 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yaml-fyamlcpp) +target_link_libraries(cpp-yaml-fyamlcpp PUBLIC + contrib-libs-cxxsupp + yutil + contrib-libs-libfyaml +) +target_sources(cpp-yaml-fyamlcpp PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yaml/fyamlcpp/fyamlcpp.cpp +) diff --git a/library/cpp/yaml/fyamlcpp/CMakeLists.linux-aarch64.txt b/library/cpp/yaml/fyamlcpp/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..d13ed8d7f1 --- /dev/null +++ b/library/cpp/yaml/fyamlcpp/CMakeLists.linux-aarch64.txt @@ -0,0 +1,19 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yaml-fyamlcpp) +target_link_libraries(cpp-yaml-fyamlcpp PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + contrib-libs-libfyaml +) +target_sources(cpp-yaml-fyamlcpp PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yaml/fyamlcpp/fyamlcpp.cpp +) diff --git a/library/cpp/yaml/fyamlcpp/CMakeLists.linux.txt b/library/cpp/yaml/fyamlcpp/CMakeLists.linux.txt new file mode 100644 index 0000000000..d13ed8d7f1 --- /dev/null +++ b/library/cpp/yaml/fyamlcpp/CMakeLists.linux.txt @@ -0,0 +1,19 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(cpp-yaml-fyamlcpp) +target_link_libraries(cpp-yaml-fyamlcpp PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + yutil + contrib-libs-libfyaml +) +target_sources(cpp-yaml-fyamlcpp PRIVATE + ${CMAKE_SOURCE_DIR}/library/cpp/yaml/fyamlcpp/fyamlcpp.cpp +) diff --git a/library/cpp/yaml/fyamlcpp/CMakeLists.txt b/library/cpp/yaml/fyamlcpp/CMakeLists.txt new file mode 100644 index 0000000000..3e0811fb22 --- /dev/null +++ b/library/cpp/yaml/fyamlcpp/CMakeLists.txt @@ -0,0 +1,15 @@ + +# This file was gererated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND UNIX AND NOT APPLE AND NOT ANDROID) + include(CMakeLists.linux-aarch64.txt) +elseif (APPLE) + include(CMakeLists.darwin.txt) +elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND UNIX AND NOT APPLE AND NOT ANDROID) + include(CMakeLists.linux.txt) +endif() diff --git a/library/cpp/yaml/fyamlcpp/fyamlcpp.cpp b/library/cpp/yaml/fyamlcpp/fyamlcpp.cpp new file mode 100644 index 0000000000..b8d8b17596 --- /dev/null +++ b/library/cpp/yaml/fyamlcpp/fyamlcpp.cpp @@ -0,0 +1,969 @@ +#include "fyamlcpp.h" + +#include <contrib/libs/libfyaml/include/libfyaml.h> + +#include <util/digest/murmur.h> + +namespace NFyaml { + +#define ENSURE_NODE_NOT_EMPTY(NODE) Y_ENSURE_EX(NODE, TFyamlEx() << "Expected non-empty Node") +#define ENSURE_DOCUMENT_NOT_EMPTY(NODE) Y_ENSURE_EX(NODE, TFyamlEx() << "Expected non-empty Document") + +const char* zstr = ""; + +enum class EErrorType { + Debug = FYET_DEBUG, + Info = FYET_INFO, + Notice = FYET_NOTICE, + Warning = FYET_WARNING, + Error = FYET_ERROR, + Max = FYET_MAX, +}; + +enum class EErrorModule { + Unknown = FYEM_UNKNOWN, + Atom = FYEM_ATOM, + Scan = FYEM_SCAN, + Parse = FYEM_PARSE, + Doc = FYEM_DOC, + Build = FYEM_BUILD, + Internal = FYEM_INTERNAL, + System = FYEM_SYSTEM, + Max = FYEM_MAX, +}; + +enum class EParseCfgFlags { + Quiet = FYPCF_QUIET, + CollectDiag = FYPCF_COLLECT_DIAG, + ResolveDocument = FYPCF_RESOLVE_DOCUMENT, + DisableMmapOpt = FYPCF_DISABLE_MMAP_OPT, + DisableRecycling = FYPCF_DISABLE_RECYCLING, + ParseComments = FYPCF_PARSE_COMMENTS, + DisableDepth_limit = FYPCF_DISABLE_DEPTH_LIMIT, + DisableAccelerators = FYPCF_DISABLE_ACCELERATORS, + DisableBuffering = FYPCF_DISABLE_BUFFERING, + DefaultVersionAuto = FYPCF_DEFAULT_VERSION_AUTO, + DefaultVersion1_1 = FYPCF_DEFAULT_VERSION_1_1, + DefaultVersion1_2 = FYPCF_DEFAULT_VERSION_1_2, + DefaultVersion1_3 = FYPCF_DEFAULT_VERSION_1_3, + SloppyFlowIndentation = FYPCF_SLOPPY_FLOW_INDENTATION, + PreferRecursive = FYPCF_PREFER_RECURSIVE, + JsonAuto = FYPCF_JSON_AUTO, + JsonNone = FYPCF_JSON_NONE, + JsonForce = FYPCF_JSON_FORCE, + YpathAliases = FYPCF_YPATH_ALIASES, + AllowDuplicateKeys = FYPCF_ALLOW_DUPLICATE_KEYS, +}; + +enum class EEventType { + None = FYET_NONE, + StreamStart = FYET_STREAM_START, + StreamEnd = FYET_STREAM_END, + DocumentStart = FYET_DOCUMENT_START, + DocumentEnd = FYET_DOCUMENT_END, + MappingStart = FYET_MAPPING_START, + MappingEnd = FYET_MAPPING_END, + SequenceStart = FYET_SEQUENCE_START, + SequenceEnd = FYET_SEQUENCE_END, + Scalar = FYET_SCALAR, + Alias = FYET_ALIAS, +}; + +enum class EScalarStyle { + Any = FYSS_ANY, + Plain = FYSS_PLAIN, + SingleQuoted = FYSS_SINGLE_QUOTED, + DoubleQuoted = FYSS_DOUBLE_QUOTED, + Literal = FYSS_LITERAL, + Folded = FYSS_FOLDED, + Max = FYSS_MAX, +}; + +enum class EEmitterWriteType { + DocumentIndicator = fyewt_document_indicator, + TagDirective = fyewt_tag_directive, + VersionDirective = fyewt_version_directive, + Indent = fyewt_indent, + Indicator = fyewt_indicator, + Whitespace = fyewt_whitespace, + PlainScalar = fyewt_plain_scalar, + SingleQuotedScalar = fyewt_single_quoted_scalar, + DoubleQuotedScalar = fyewt_double_quoted_scalar, + LiteralScalar = fyewt_literal_scalar, + FoldedScalar = fyewt_folded_scalar, + Anchor = fyewt_anchor, + Tag = fyewt_tag, + Linebreak = fyewt_linebreak, + Alias = fyewt_alias, + TerminatingZero = fyewt_terminating_zero, + PlainScalarKey = fyewt_plain_scalar_key, + SingleQuotedScalarKey = fyewt_single_quoted_scalar_key, + DoubleQuotedScalarKey = fyewt_double_quoted_scalar_key, + Comment = fyewt_comment, +}; + +enum class ECommentPlacement { + Top = fycp_top, + Right = fycp_right, + Bottom = fycp_bottom, +}; + +enum EEmitterCfgFlags { + SortKeys = FYECF_SORT_KEYS, + OutputComments = FYECF_OUTPUT_COMMENTS, + StripLabels = FYECF_STRIP_LABELS, + StripTags = FYECF_STRIP_TAGS, + StripDoc = FYECF_STRIP_DOC, + NoEndingNewline = FYECF_NO_ENDING_NEWLINE, + StripEmptyKv = FYECF_STRIP_EMPTY_KV, + IndentDefault = FYECF_INDENT_DEFAULT, + Indent1 = FYECF_INDENT_1, + Indent2 = FYECF_INDENT_2, + Indent3 = FYECF_INDENT_3, + Indent4 = FYECF_INDENT_4, + Indent5 = FYECF_INDENT_5, + Indent6 = FYECF_INDENT_6, + Indent7 = FYECF_INDENT_7, + Indent8 = FYECF_INDENT_8, + Indent9 = FYECF_INDENT_9, + WidthDefault = FYECF_WIDTH_DEFAULT, + Width80 = FYECF_WIDTH_80, + Width132 = FYECF_WIDTH_132, + WidthInf = FYECF_WIDTH_INF, + ModeOriginal = FYECF_MODE_ORIGINAL, + ModeBlock = FYECF_MODE_BLOCK, + ModeFlow = FYECF_MODE_FLOW, + ModeFlowOneline = FYECF_MODE_FLOW_ONELINE, + ModeJson = FYECF_MODE_JSON, + ModeJsonTp = FYECF_MODE_JSON_TP, + ModeJsonOneline = FYECF_MODE_JSON_ONELINE, + ModeDejson = FYECF_MODE_DEJSON, + ModePretty = FYECF_MODE_PRETTY, + DocStartMarkAuto = FYECF_DOC_START_MARK_AUTO, + DocStartMarkOff = FYECF_DOC_START_MARK_OFF, + DocStartMarkOn = FYECF_DOC_START_MARK_ON, + DocEndMarkAuto = FYECF_DOC_END_MARK_AUTO, + DocEndMarkOff = FYECF_DOC_END_MARK_OFF, + DocEndMarkOn = FYECF_DOC_END_MARK_ON, + VersionDirAuto = FYECF_VERSION_DIR_AUTO, + VersionDirOff = FYECF_VERSION_DIR_OFF, + VersionDirOn = FYECF_VERSION_DIR_ON, + TagDirAuto = FYECF_TAG_DIR_AUTO, + TagDirOff = FYECF_TAG_DIR_OFF, + TagDirOn = FYECF_TAG_DIR_ON, + + Default = FYECF_DEFAULT, +}; + +enum class ENodeStyle { + Any = FYNS_ANY, + Flow = FYNS_FLOW, + Block = FYNS_BLOCK, + Plain = FYNS_PLAIN, + SingleQuoted = FYNS_SINGLE_QUOTED, + DoubleQuoted = FYNS_DOUBLE_QUOTED, + Literal = FYNS_LITERAL, + Folded = FYNS_FOLDED, + Alias = FYNS_ALIAS, +}; + +enum class ENodeWalkFlags { + DontFollow = FYNWF_DONT_FOLLOW, + Follow = FYNWF_FOLLOW, + PtrYaml = FYNWF_PTR_YAML, + PtrJson = FYNWF_PTR_JSON, + PtrReljson = FYNWF_PTR_RELJSON, + PtrYpath = FYNWF_PTR_YPATH, + UriEncoded = FYNWF_URI_ENCODED, + MaxdepthDefault = FYNWF_MAXDEPTH_DEFAULT, + MarkerDefault = FYNWF_MARKER_DEFAULT, + PtrDefault = FYNWF_PTR_DEFAULT, +}; + +enum class EPathParseCfgFlags { + Quiet = FYPPCF_QUIET, + DisableRecycling = FYPPCF_DISABLE_RECYCLING, + DisableAccelerators = FYPPCF_DISABLE_ACCELERATORS, +}; + +enum class EPathExecCfgFlags { + Quiet = FYPXCF_QUIET, + DisableRecycling = FYPXCF_DISABLE_RECYCLING, + DisableAccelerators = FYPXCF_DISABLE_ACCELERATORS, +}; + +enum class ETokenType { + /* non-content token types */ + None = FYTT_NONE, + StreamStart = FYTT_STREAM_START, + StreamEnd = FYTT_STREAM_END, + VersionDirective = FYTT_VERSION_DIRECTIVE, + TagDirective = FYTT_TAG_DIRECTIVE, + DocumentStart = FYTT_DOCUMENT_START, + DocumentEnd = FYTT_DOCUMENT_END, + /* content token types */ + BlockSequenceStart = FYTT_BLOCK_SEQUENCE_START, + BlockMappingStart = FYTT_BLOCK_MAPPING_START, + BlockEnd = FYTT_BLOCK_END, + FlowSequenceStart = FYTT_FLOW_SEQUENCE_START, + FlowSequenceEnd = FYTT_FLOW_SEQUENCE_END, + FlowMappingStart = FYTT_FLOW_MAPPING_START, + FlowMappingEnd = FYTT_FLOW_MAPPING_END, + BlockEntry = FYTT_BLOCK_ENTRY, + FlowEntry = FYTT_FLOW_ENTRY, + Key = FYTT_KEY, + Value = FYTT_VALUE, + Alias = FYTT_ALIAS, + Anchor = FYTT_ANCHOR, + Tag = FYTT_TAG, + Scalar = FYTT_SCALAR, + + /* special error reporting */ + Input_marker = FYTT_INPUT_MARKER, + + /* path expression tokens */ + PeSlash = FYTT_PE_SLASH, + PeRoot = FYTT_PE_ROOT, + PeThis = FYTT_PE_THIS, + PeParent = FYTT_PE_PARENT, + PeMapKey = FYTT_PE_MAP_KEY, + PeSeqIndex = FYTT_PE_SEQ_INDEX, + PeSeqSlice = FYTT_PE_SEQ_SLICE, + PeScalarFilter = FYTT_PE_SCALAR_FILTER, + PeCollectionFilter = FYTT_PE_COLLECTION_FILTER, + PeSeqFilter = FYTT_PE_SEQ_FILTER, + PeMapFilter = FYTT_PE_MAP_FILTER, + PeUniqueFilter = FYTT_PE_UNIQUE_FILTER, + PeEveryChild = FYTT_PE_EVERY_CHILD, + PeEveryChildR = FYTT_PE_EVERY_CHILD_R, + PeAlias = FYTT_PE_ALIAS, + PeSibling = FYTT_PE_SIBLING, + PeComma = FYTT_PE_COMMA, + PeBarbar = FYTT_PE_BARBAR, + PeAmpamp = FYTT_PE_AMPAMP, + PeLparen = FYTT_PE_LPAREN, + PeRparen = FYTT_PE_RPAREN, + + /* comparison operators */ + PeEqeq = FYTT_PE_EQEQ, + PeNoteq = FYTT_PE_NOTEQ, + PeLt = FYTT_PE_LT, + PeGt = FYTT_PE_GT, + PeLte = FYTT_PE_LTE, + PeGte = FYTT_PE_GTE, + + /* scalar expression tokens */ + SePlus = FYTT_SE_PLUS, + SeMinus = FYTT_SE_MINUS, + SeMult = FYTT_SE_MULT, + SeDiv = FYTT_SE_DIV, + + PeMethod = FYTT_PE_METHOD, + SeMethod = FYTT_SE_METHOD, +}; + +enum class EComposerReturn { + OkContinue = FYCR_OK_CONTINUE, + OkStop = FYCR_OK_STOP, + OkStartSkip = FYCR_OK_START_SKIP, + OkStopSkip = FYCR_OK_STOP_SKIP, + Error = FYCR_ERROR, +}; + +TDocumentIterator::TDocumentIterator(fy_document_iterator* iterator) + : Iterator_(iterator, fy_document_iterator_destroy) +{} + +TNode TNodeRef::CreateReference() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return TNode(fy_node_create_reference(Node_)); +} + +TNode TNodeRef::Copy() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return TNode(fy_node_copy(fy_node_document(Node_), Node_)); +} + +TNode TNodeRef::Copy(TDocument& to) const { + ENSURE_NODE_NOT_EMPTY(Node_); + auto* fromDoc = fy_node_document(Node_); + auto& fromUserdata = *reinterpret_cast<THashSet<TSimpleSharedPtr<TString>, TStringPtrHashT>*>(fy_document_get_userdata(fromDoc)); + auto& toUserdata = *reinterpret_cast<THashSet<TSimpleSharedPtr<TString>, TStringPtrHashT>*>(fy_document_get_userdata(to.Document_.get())); + toUserdata.insert(fromUserdata.begin(), fromUserdata.end()); + return TNode(fy_node_copy(to.Document_.get(), Node_)); +} + +TString TNodeRef::Path() const { + ENSURE_NODE_NOT_EMPTY(Node_); + char* path = fy_node_get_path(Node_); + + if (path) { + TString str(path); + free(path); + return str; + } + + return {}; +} + +ENodeType TNodeRef::Type() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return static_cast<ENodeType>(fy_node_get_type(Node_)); +} + +bool TNodeRef::IsAlias() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return fy_node_is_alias(Node_); +} + +TNodeRef TNodeRef::ResolveAlias() const { + ENSURE_NODE_NOT_EMPTY(Node_); + Y_VERIFY_DEBUG(IsAlias()); + return TNodeRef(fy_node_resolve_alias(Node_)); +} + +TString TNodeRef::Scalar() const { + ENSURE_NODE_NOT_EMPTY(Node_); + Y_ENSURE_EX(fy_node_is_scalar(Node_), TFyamlEx() << "Node is not Scalar: " << Path()); + size_t size; + const char* text = fy_node_get_scalar(Node_, &size); + return TString(text, size); +} + +TMapping TNodeRef::Map() const { + ENSURE_NODE_NOT_EMPTY(Node_); + Y_ENSURE_EX(fy_node_is_mapping(Node_), TFyamlEx() << "Node is not Mapping: " << Path()); + return TMapping(*this); +} + +TSequence TNodeRef::Sequence() const { + ENSURE_NODE_NOT_EMPTY(Node_); + Y_ENSURE_EX(fy_node_is_sequence(Node_), TFyamlEx() << "Node is not Sequence: " << Path()); + return TSequence(*this); +} + +void TNodeRef::Insert(const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Node_); + NDetail::RethrowOnError(fy_node_insert(Node_, node.Node_), Node_); +} + +std::optional<TString> TNodeRef::Tag() const { + ENSURE_NODE_NOT_EMPTY(Node_); + size_t len = 0; + const char* tag = fy_node_get_tag(Node_, &len); + + if (tag) { + return TString(tag, len); + } + + return std::nullopt; +} + +void TNodeRef::SetTag(const TString& tag) { + ENSURE_NODE_NOT_EMPTY(Node_); + auto* str = new TString(std::move(tag)); + auto* data = new NDetail::TUserDataHolder(UserData(), str); + SetUserData(data); + NDetail::RethrowOnError(fy_node_set_tag(Node_, str->c_str(), str->length()), Node_); +} + +bool TNodeRef::RemoveTag() { + ENSURE_NODE_NOT_EMPTY(Node_); + bool ret = fy_node_remove_tag(Node_); + ClearUserData(); + return ret; +} + +bool TNodeRef::HasAnchor() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return fy_node_get_anchor(Node_) != nullptr; +} + +void TNodeRef::SetAnchor(const TString& anchor) { + auto* str = new TString(anchor); + auto* data = new NDetail::TUserDataHolder(UserData(), str); + SetUserData(data); + NDetail::RethrowOnError(fy_node_set_anchor(Node_, str->c_str(), str->length()), Node_); +} + +bool TNodeRef::DeepEqual(const TNodeRef& other) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(other.Node_); + return fy_node_compare(Node_, other.Node_); +} + +std::unique_ptr<char, void(*)(char*)> TNodeRef::EmitToCharArray() const { + std::unique_ptr<char, void(*)(char*)> res( + fy_emit_node_to_string( + Node_, + (fy_emitter_cfg_flags)(FYECF_DEFAULT)), &NDetail::FreeChar); + return res; +} + +void TNodeRef::SetUserData(NDetail::IBasicUserData* data) { + ENSURE_NODE_NOT_EMPTY(Node_); + fy_node_set_meta(Node_, data); +} + +NDetail::IBasicUserData* TNodeRef::UserData() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return reinterpret_cast<NDetail::IBasicUserData* >(fy_node_get_meta(Node_)); +} + +void TNodeRef::ClearUserData() { + ENSURE_NODE_NOT_EMPTY(Node_); + fy_node_clear_meta(Node_); +} + +TNode& TNode::operator=(fy_node* node) { + Node_.reset(node, fy_node_free); + return *this; +} + +TNode::TNode(fy_node* node) + : Node_(node, fy_node_free) +{} + +TNodeRef TNodePairRef::Key() const { + ENSURE_NODE_NOT_EMPTY(Pair_); + return TNodeRef(fy_node_pair_key(Pair_)); +} + +void TNodePairRef::SetKey(const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Pair_); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_pair_set_key(Pair_, node.Node_), Pair_); +} + +TNodeRef TNodePairRef::Value() const { + ENSURE_NODE_NOT_EMPTY(Pair_); + return TNodeRef(fy_node_pair_value(Pair_)); +} + +void TNodePairRef::SetValue(const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Pair_); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_pair_set_value(Pair_, node.Node_), Pair_); +} + +TMappingIterator::TMappingIterator(const TNodeRef& node, bool end) + : Node_(node) +{ + if (!end) { + NodePair_ = TNodePairRef(fy_node_mapping_iterate(Node_.Node_, reinterpret_cast<void**>(&NodePair_.Pair_))); + } +} + +TMappingIterator& TMappingIterator::operator++() { + NodePair_ = TNodePairRef(fy_node_mapping_iterate(Node_.Node_, reinterpret_cast<void**>(&NodePair_.Pair_))); + return *this; +} + +TReverseMappingIterator::TReverseMappingIterator(const TNodeRef& node, bool end) + : Node_(node) +{ + if (!end) { + NodePair_ = TNodePairRef(fy_node_mapping_reverse_iterate(Node_.Node_, reinterpret_cast<void**>(&NodePair_.Pair_))); + } +} + +TReverseMappingIterator& TReverseMappingIterator::operator++() { + NodePair_ = TNodePairRef(fy_node_mapping_reverse_iterate(Node_.Node_, reinterpret_cast<void**>(&NodePair_.Pair_))); + return *this; +} + +size_t TMapping::size() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return fy_node_mapping_item_count(Node_); +} + +size_t TMapping::empty() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return fy_node_mapping_is_empty(Node_); +} + +TNodePairRef TMapping::at(int index) const { + ENSURE_NODE_NOT_EMPTY(Node_); + auto res = fy_node_mapping_get_by_index(Node_, index); + Y_ENSURE_EX(res, TFyamlEx() << "No such child: " << Path() << "/" << index); + return TNodePairRef(res); +} + +TNodePairRef TMapping::operator[](int index) const { + ENSURE_NODE_NOT_EMPTY(Node_); + return TNodePairRef(fy_node_mapping_get_by_index(Node_, index)); +} + +TNodeRef TMapping::at(const TString& index) const { + ENSURE_NODE_NOT_EMPTY(Node_); + auto res = fy_node_mapping_lookup_by_string(Node_, index.data(), index.size()); + Y_ENSURE_EX(res, TFyamlEx() << "No such child: " << Path() << "/" << index); + return TNodeRef(res); +} + +TNodePairRef TMapping::pair_at(const TString& index) const { + ENSURE_NODE_NOT_EMPTY(Node_); + auto res = fy_node_mapping_lookup_pair_by_string(Node_, index.data(), index.size()); + Y_ENSURE_EX(res, TFyamlEx() << "No such child: " << Path() << "/" << index); + return TNodePairRef(res); +} + +TNodePairRef TMapping::pair_at_opt(const TString& index) const { + ENSURE_NODE_NOT_EMPTY(Node_); + return TNodePairRef(fy_node_mapping_lookup_pair_by_string(Node_, index.data(), index.size())); +} + +TNodeRef TMapping::operator[](const TString& index) const { + ENSURE_NODE_NOT_EMPTY(Node_); + return TNodeRef(fy_node_mapping_lookup_by_string(Node_, index.data(), index.size())); +} + +TNodeRef TMapping::operator[](const char* str) const { + ENSURE_NODE_NOT_EMPTY(Node_); + TString index(str); + return TNodeRef(fy_node_mapping_lookup_by_string(Node_, index.data(), index.size())); +} + +void TMapping::Append(const TNodeRef& key, const TNodeRef& value) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(key); + ENSURE_NODE_NOT_EMPTY(value); + NDetail::RethrowOnError(fy_node_mapping_append(Node_, key.Node_, value.Node_), Node_); +} + +void TMapping::Prepend(const TNodeRef& key, const TNodeRef& value) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(key); + ENSURE_NODE_NOT_EMPTY(value); + NDetail::RethrowOnError(fy_node_mapping_prepend(Node_, key.Node_, value.Node_), Node_); +} + +void TMapping::Remove(const TNodePairRef& toRemove) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(toRemove); + NDetail::RethrowOnError(fy_node_mapping_remove(Node_, toRemove.Pair_), Node_); +} + +TMappingIterator TMapping::Remove(const TMappingIterator& toRemove) { + ENSURE_NODE_NOT_EMPTY(Node_); + Y_VERIFY_DEBUG(Node_ == toRemove.Node_); + TMappingIterator ret = toRemove; + ++ret; + fy_node_mapping_remove(Node_, toRemove.NodePair_.Pair_); + return ret; +} + +void TMapping::Remove(const TNodeRef& key) { + ENSURE_NODE_NOT_EMPTY(Node_); + fy_node_free(fy_node_mapping_remove_by_key(Node_, key.Node_)); +} + +TSequenceIterator::TSequenceIterator(const TNodeRef& node, bool end) + : Node_(node) +{ + if (!end) { + IterNode_ = TNodeRef(fy_node_sequence_iterate(Node_.Node_, &Iter_)); + } +} + +TSequenceIterator& TSequenceIterator::operator++() { + IterNode_ = TNodeRef(fy_node_sequence_iterate(Node_.Node_, &Iter_)); + return *this; +} + +void TSequenceIterator::InsertBefore(const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(IterNode_); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_sequence_insert_before(Node_.Node_, IterNode_.Node_, node.Node_), Node_.Node_); +} + +void TSequenceIterator::InsertAfter(const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(IterNode_); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_sequence_insert_after(Node_.Node_, IterNode_.Node_, node.Node_), Node_.Node_); +} + +TReverseSequenceIterator::TReverseSequenceIterator(const TNodeRef& node, bool end) + : Node_(node) +{ + if (!end) { + IterNode_ = TNodeRef(fy_node_sequence_reverse_iterate(Node_.Node_, &Iter_)); + } +} + +TReverseSequenceIterator& TReverseSequenceIterator::operator++() { + IterNode_ = TNodeRef(fy_node_sequence_reverse_iterate(Node_.Node_, &Iter_)); + return *this; +} + +void TReverseSequenceIterator::InsertBefore(const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(IterNode_); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_sequence_insert_after(Node_.Node_, IterNode_.Node_, node.Node_), Node_.Node_); +} + +void TReverseSequenceIterator::InsertAfter(const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(IterNode_); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_sequence_insert_before(Node_.Node_, IterNode_.Node_, node.Node_), Node_.Node_); +} + +size_t TSequence::size() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return fy_node_sequence_item_count(Node_); +} + +size_t TSequence::empty() const { + ENSURE_NODE_NOT_EMPTY(Node_); + return fy_node_sequence_is_empty(Node_); +} + +TNodeRef TSequence::at(int index) const { + ENSURE_NODE_NOT_EMPTY(Node_); + auto res = fy_node_sequence_get_by_index(Node_, index); + Y_ENSURE_EX(res, TFyamlEx() << "No such index: " << Path() << "/" << index); + return TNodeRef(res); +} + +TNodeRef TSequence::operator[](int index) const { + ENSURE_NODE_NOT_EMPTY(Node_); + return TNodeRef(fy_node_sequence_get_by_index(Node_, index)); +} + +void TSequence::Append(const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_sequence_append(Node_, node.Node_), Node_); +} + +void TSequence::Prepend(const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_sequence_prepend(Node_, node.Node_), Node_); +} + +void TSequence::InsertBefore(const TNodeRef& mark, const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(mark); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_sequence_insert_before(Node_, mark.Node_, node.Node_), Node_); +} + +void TSequence::InsertAfter(const TNodeRef& mark, const TNodeRef& node) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(mark); + ENSURE_NODE_NOT_EMPTY(node); + NDetail::RethrowOnError(fy_node_sequence_insert_after(Node_, mark.Node_, node.Node_), Node_); +} + +TNode TSequence::Remove(const TNodeRef& toRemove) { + ENSURE_NODE_NOT_EMPTY(Node_); + ENSURE_NODE_NOT_EMPTY(toRemove.Node_); + return TNode(fy_node_sequence_remove(Node_, toRemove.Node_)); +} + +TSequenceIterator TSequence::Remove(const TSequenceIterator& toRemove) { + ENSURE_NODE_NOT_EMPTY(Node_); + Y_VERIFY_DEBUG(Node_ == toRemove.Node_); + ENSURE_NODE_NOT_EMPTY(toRemove.IterNode_); + TSequenceIterator ret = toRemove; + ++ret; + fy_node_sequence_remove(Node_, toRemove.IterNode_.Node_); + fy_node_free(toRemove.IterNode_.Node_); // TODO add extract + return ret; +} + +TReverseSequenceIterator TSequence::Remove(const TReverseSequenceIterator& toRemove) { + ENSURE_NODE_NOT_EMPTY(Node_); + Y_VERIFY_DEBUG(Node_ == toRemove.Node_); + ENSURE_NODE_NOT_EMPTY(toRemove.IterNode_); + TReverseSequenceIterator ret = toRemove; + ++ret; + fy_node_sequence_remove(Node_, toRemove.IterNode_.Node_); + fy_node_free(toRemove.IterNode_.Node_); // TODO add extract + return ret; +} + +TDocumentNodeIterator::TDocumentNodeIterator(TNodeRef&& node) + : Node_(node) +{ + if (node) { + Iterator_ = {fy_document_iterator_create(), fy_document_iterator_destroy}; + fy_document_iterator_node_start(Iterator_.get(), node.Node_); + } +} + +TDocumentNodeIterator& TDocumentNodeIterator::operator++() { + Node_ = fy_document_iterator_node_next(Iterator_.get()); + return *this; +} + +TDocument::TDocument(TString str, fy_document* doc, fy_diag* diag) + : Document_(doc, fy_document_destroy) + , Diag_(diag, fy_diag_destroy) +{ + auto* userdata = new THashSet<TSimpleSharedPtr<TString>, TStringPtrHashT>({MakeSimpleShared<TString>(std::move(str))}); + fy_document_set_userdata(doc, userdata); + fy_document_register_on_destroy(doc, &DestroyDocumentStrings); + RegisterUserDataCleanup(); +} + +TDocument::TDocument(fy_document* doc, fy_diag* diag) + : Document_(doc, fy_document_destroy) + , Diag_(diag, fy_diag_destroy) +{ + RegisterUserDataCleanup(); +} + + +TDocument TDocument::Parse(TString str) { + const char* cstr = str.empty() ? zstr : str.cbegin(); + fy_diag_cfg dcfg; + fy_diag_cfg_default(&dcfg); + std::unique_ptr<fy_diag, void(*)(fy_diag*)> diag(fy_diag_create(&dcfg), fy_diag_destroy); + fy_diag_set_collect_errors(diag.get(), true); + fy_parse_cfg cfg{ + "", + // FYPCF_PARSE_COMMENTS, + FYPCF_QUIET, + nullptr, + diag.get() + }; + fy_document* doc = fy_document_build_from_string(&cfg, cstr, FY_NT); + if (!doc) { + fy_diag_error* err; + void *iter = nullptr; + while ((err = fy_diag_errors_iterate(diag.get(), &iter)) != nullptr) { + ythrow yexception() << err->file << ":" << err->line << ":" << err->column << " " << err->msg; + } + } + return TDocument(std::move(str), doc, diag.release()); +} + +TDocument TDocument::Clone() const { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + fy_document* doc = fy_document_clone(Document_.get()); + fy_document_set_userdata( + doc, + new THashSet<TSimpleSharedPtr<TString>, TStringPtrHashT>( + *reinterpret_cast<THashSet<TSimpleSharedPtr<TString>, TStringPtrHashT>*>(fy_document_get_userdata(Document_.get())) + ) + ); + fy_document_register_on_destroy(doc, &DestroyDocumentStrings); + return TDocument(doc, fy_document_get_diag(doc)); +} + +void TDocument::InsertAt(const char* path, const TNodeRef& node) { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + NDetail::RethrowOnError(fy_document_insert_at(Document_.get(), path, FY_NT, node.Node_), Diag_.get()); +} + +TNodeRef TDocument::Buildf(const char* content) { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + return fy_node_build_from_string(Document_.get(), content, strlen(content)); +} + +void TDocument::Resolve() { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + if (fy_document_resolve(Document_.get()) != 0) { + fy_diag_error* err; + void *iter = nullptr; + while ((err = fy_diag_errors_iterate(Diag_.get(), &iter)) != nullptr) { + ythrow yexception() << err->line << ":" << err->column << " " << err->msg; + } + } +} + +bool TDocument::HasDirectives() { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + return fy_document_has_directives(Document_.get()); +} + +bool TDocument::HasExplicitDocumentStart() { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + return fy_document_has_explicit_document_start(Document_.get()); +} + +bool TDocument::HasExplicitDocumentEnd() { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + return fy_document_has_explicit_document_end(Document_.get()); +} + +void TDocument::SetParent(const TDocument& doc) { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + ENSURE_DOCUMENT_NOT_EMPTY(doc.Document_); + NDetail::RethrowOnError(fy_document_set_parent(doc.Document_.get(), Document_.release()), Diag_.get()); +} + +TNodeRef TDocument::Root() { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + return fy_document_root(Document_.get()); +} + +void TDocument::SetRoot(const TNodeRef& node) { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + ENSURE_NODE_NOT_EMPTY(node.Node_); + NDetail::RethrowOnError(fy_document_set_root(Document_.get(), node.Node_), Diag_.get()); +} + +TNodeRef TDocument::CreateAlias(const TString& name) { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + return TNodeRef(fy_node_create_alias_copy(Document_.get(), name.c_str(), name.length())); +} + +std::unique_ptr<char, void(*)(char*)> TDocument::EmitToCharArray() const { + std::unique_ptr<char, void(*)(char*)> res( + fy_emit_document_to_string( + Document_.get(), + (fy_emitter_cfg_flags)(FYECF_DEFAULT | FYECF_OUTPUT_COMMENTS)), &NDetail::FreeChar); + return res; +} + +bool TDocument::RegisterUserDataCleanup() { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + return fy_document_register_meta(Document_.get(), &DestroyUserData, nullptr) == 0; +} + +void TDocument::UnregisterUserDataCleanup() { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + fy_document_unregister_meta(Document_.get()); +} + +TMark TDocument::BeginMark() const { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + auto* fyds = fy_document_get_document_state(Document_.get()); + auto* mark = fy_document_state_start_mark(fyds); + return TMark{ + mark->input_pos, + mark->line, + mark->column, + }; +} + +TMark TDocument::EndMark() const { + ENSURE_DOCUMENT_NOT_EMPTY(Document_); + auto* fyds = fy_document_get_document_state(Document_.get()); + auto* mark = fy_document_state_end_mark(fyds); + return TMark{ + mark->input_pos, + mark->line, + mark->column, + }; +} + +std::unique_ptr<char, void(*)(char*)> TJsonEmitter::EmitToCharArray() const { + std::unique_ptr<char, void(*)(char*)> res( + fy_emit_node_to_string( + Node_.Node_, + (fy_emitter_cfg_flags)(FYECF_DEFAULT | FYECF_SORT_KEYS | FYECF_MODE_JSON_TP)), &NDetail::FreeChar); + return res; +} + +TParser::TParser(TString rawStream, fy_parser* parser, fy_diag* diag) + : RawDocumentStream_(std::move(rawStream)) + , Parser_(parser, fy_parser_destroy) + , Diag_(diag, fy_diag_destroy) +{} + +TParser TParser::Create(TString str) +{ + const char* stream = str.empty() ? zstr : str.cbegin(); + fy_diag_cfg dcfg; + fy_diag_cfg_default(&dcfg); + std::unique_ptr<fy_diag, void(*)(fy_diag*)> diag(fy_diag_create(&dcfg), fy_diag_destroy); + fy_diag_set_collect_errors(diag.get(), true); + fy_parse_cfg cfg{ + "", + // FYPCF_PARSE_COMMENTS, + FYPCF_QUIET, + nullptr, + diag.get() + }; + auto* parser = fy_parser_create(&cfg); + if (!parser) { + fy_diag_error* err; + void *iter = nullptr; + while ((err = fy_diag_errors_iterate(diag.get(), &iter)) != nullptr) { + ythrow yexception() << err->file << ":" << err->line << ":" << err->column << " " << err->msg; + } + } + + fy_parser_set_string(parser, stream, -1); + + return TParser(std::move(str), parser, diag.release()); +} + +std::optional<TDocument> TParser::NextDocument() { + auto* doc = fy_parse_load_document(Parser_.get()); + if (!doc) { + return std::nullopt; + } + + return TDocument(RawDocumentStream_, doc, fy_document_get_diag(doc)); +} + +namespace NDetail { + +void RethrowError(fy_diag* diag) { + void *iter = nullptr; + fy_diag_error* err; + TStringStream ss; + while ((err = fy_diag_errors_iterate(diag, &iter)) != nullptr) { + ss << err->line << ":" << err->column << " " << err->msg << "\n"; + } + ythrow yexception() << ss.Str(); +} + +void RethrowOnError(bool isError, fy_node* node) { + if (!isError) { + return; + } + + std::unique_ptr<fy_diag, void(*)(fy_diag*)> diag(fy_document_get_diag(fy_node_document(node)), fy_diag_unref); + RethrowError(diag.get()); +} + +void RethrowOnError(bool isError, fy_node_pair* pair) { + if (!isError) { + return; + } + + std::unique_ptr<fy_diag, void(*)(fy_diag*)> diag(fy_document_get_diag(fy_node_document(fy_node_pair_key(pair))), fy_diag_unref); + RethrowError(diag.get()); +} + +void RethrowOnError(bool isError, fy_diag* diag) { + if (!isError) { + return; + } + + RethrowError(diag); +} + + +void FreeChar(char* mem) { + free(mem); +} + +} // namespace NDetail + +} // namespace NFyaml + +template <> +void Out<NFyaml::TDocument>(IOutputStream& out, const NFyaml::TDocument& value) { + out << value.EmitToCharArray().get(); +} + +template <> +void Out<NFyaml::TNodeRef>(IOutputStream& out, const NFyaml::TNodeRef& value) { + out << value.EmitToCharArray().get(); +} + +template <> +void Out<NFyaml::TJsonEmitter>(IOutputStream& out, const NFyaml::TJsonEmitter& value) { + out << value.EmitToCharArray().get(); +} diff --git a/library/cpp/yaml/fyamlcpp/fyamlcpp.h b/library/cpp/yaml/fyamlcpp/fyamlcpp.h new file mode 100644 index 0000000000..bcf9362d73 --- /dev/null +++ b/library/cpp/yaml/fyamlcpp/fyamlcpp.h @@ -0,0 +1,636 @@ +#pragma once + +#include <util/generic/yexception.h> +#include <util/system/compiler.h> +#include <util/system/yassert.h> +#include <util/stream/str.h> +#include <util/generic/hash_set.h> + +#include <memory> +#include <optional> + +struct fy_parser; +struct fy_node; +struct fy_document; +struct fy_diag; +struct fy_document_iterator; +struct fy_node_pair; + +namespace NFyaml { + +struct TStringPtrHashT { + size_t operator()(const TSimpleSharedPtr<TString>& str) const { + return (size_t)str.Get(); + } +}; + +struct TFyamlEx : public yexception {}; + +enum class ENodeType { + Scalar, + Sequence, + Mapping, +}; + +namespace NDetail { + +class IBasicUserData { +public: + virtual ~IBasicUserData() = default; +}; + +template <class T> +class TUserDataHolder : public IBasicUserData { +public: + TUserDataHolder(IBasicUserData* next, T* data) + : Next_(next) + , Data_(data) + {} + +private: + std::unique_ptr<IBasicUserData> Next_ = nullptr; + std::unique_ptr<T> Data_ = nullptr; +}; + +void RethrowError(fy_diag* diag); + +void RethrowOnError(bool isError, fy_node* node); + +void RethrowOnError(bool isError, fy_node_pair* pair); + +void RethrowOnError(bool isError, fy_diag* diag); + +void FreeChar(char* mem); + +} // namespace NDetail + +class TNodeRef; +class TDocumentIterator; +class TDocument; +class TNode; +class TMappingIterator; +class TReverseMappingIterator; +class TMapping; +class TSequenceIterator; +class TReverseSequenceIterator; +class TSequence; +class TJsonEmitter; +class TParser; +struct TMark; + +class TDocumentIterator { + friend class TDocument; +public: + TDocumentIterator(fy_document_iterator* iterator = nullptr); + +protected: + std::unique_ptr<fy_document_iterator, void(*)(fy_document_iterator*)> Iterator_; +}; + +class TNodeRef { + friend class TDocument; + friend class TDocumentNodeIterator; + friend class TNode; + friend class TMapping; + friend class TMappingIterator; + friend class TReverseMappingIterator; + friend class TNodePairRef; + friend class TSequence; + friend class TSequenceIterator; + friend class TReverseSequenceIterator; + friend class TJsonEmitter; + + TNodeRef(fy_node* node) + : Node_(node) + {} + +public: + TNodeRef() {}; + + TNodeRef(const TNodeRef& other) { Node_ = other.Node_; } + + TNodeRef& operator=(const TNodeRef& other) { Node_ = other.Node_; return *this; } + + TNodeRef& operator=(fy_node* node) { Node_ = node; return *this; } + + bool operator==(const TNodeRef& other) const { return Node_ == other.Node_; } + + explicit operator bool() const { return Node_ != nullptr; } + + TString Path() const; + + ENodeType Type() const; + + TNode Copy() const; + + TNode Copy(TDocument& to) const; + + bool IsAlias() const; + + TNodeRef ResolveAlias() const; + + TNode CreateReference() const; + + TSequence Sequence() const; + + TMapping Map() const; + + TString Scalar() const; + + void Insert(const TNodeRef& node); + + bool Empty() const { return Node_ == nullptr; } + + std::optional<TString> Tag() const; + + void SetTag(const TString& tag); + + bool RemoveTag(); + + bool HasAnchor() const; + + void SetAnchor(const TString& anchor); + + bool DeepEqual(const TNodeRef& other); + + std::unique_ptr<char, void(*)(char*)> EmitToCharArray() const; + +protected: + fy_node* Node_ = nullptr; + + void SetUserData(NDetail::IBasicUserData* data); + NDetail::IBasicUserData* UserData() const; + void ClearUserData(); +}; + +class TNode { + friend class TDocument; + friend class TDocumentNodeIterator; + friend class TNodeRef; + friend class TSequence; + + TNode& operator=(fy_node* node); +public: + TNode(fy_node* node = nullptr); + + bool operator==(const TNode& other) const { return Node_ == other.Node_; } + + explicit operator bool() { return Node_ != nullptr; } + + TNodeRef Ref() { return TNodeRef(Node_.get()); } + +private: + std::shared_ptr<fy_node> Node_; +}; + +class TNodePairRef { + friend class TMappingIterator; + friend class TReverseMappingIterator; + friend class TMapping; +public: + TNodePairRef(fy_node_pair* pair = nullptr) + : Pair_(pair) + {} + + bool operator==(const TNodePairRef& other) const { return Pair_ == other.Pair_; } + + explicit operator bool() const { return Pair_ != nullptr; } + + TNodeRef Key() const; + + void SetKey(const TNodeRef& node); + + TNodeRef Value() const; + + void SetValue(const TNodeRef& node); + +private: + fy_node_pair* Pair_ = nullptr; +}; + +class TMappingIterator { + friend class TMapping; +public: + TMappingIterator(const TNodeRef& node, bool end = false); + + TMappingIterator(const TMappingIterator& other) { + Node_ = other.Node_; + NodePair_ = other.NodePair_; + } + + TMappingIterator& operator=(const TMappingIterator& other) { + Node_ = other.Node_; + NodePair_ = other.NodePair_; + return *this; + } + + TMappingIterator& operator++(); + + const TNodePairRef* operator->() const { return &NodePair_; } + + TMappingIterator operator++(int) { + TMappingIterator retval = *this; + ++(*this); + return retval; + } + + bool operator==(TMappingIterator other) const { return Node_ == other.Node_ && NodePair_ == other.NodePair_; } + + const TNodePairRef& operator*() const { return NodePair_; } + +private: + TNodeRef Node_; + TNodePairRef NodePair_; +}; + +class TReverseMappingIterator { + friend class TMapping; +public: + TReverseMappingIterator(const TNodeRef& node, bool end = false); + + TReverseMappingIterator(const TReverseMappingIterator& other) { + Node_ = other.Node_; + NodePair_ = other.NodePair_; + } + + TReverseMappingIterator& operator=(const TReverseMappingIterator& other) { + Node_ = other.Node_; + NodePair_ = other.NodePair_; + return *this; + } + + TReverseMappingIterator& operator++(); + + const TNodePairRef* operator->() const { return &NodePair_; } + + TReverseMappingIterator operator++(int) { + TReverseMappingIterator retval = *this; + ++(*this); + return retval; + } + + bool operator==(TReverseMappingIterator other) const { return Node_ == other.Node_ && NodePair_ == other.NodePair_; } + + bool operator!=(TReverseMappingIterator other) const { return !(*this == other); } + + const TNodePairRef& operator*() const { return NodePair_; } + +private: + TNodeRef Node_; + TNodePairRef NodePair_; +}; + +class TMapping : public TNodeRef { +public: + explicit TMapping(const TNodeRef& node) + : TNodeRef(node) + { + Y_VERIFY_DEBUG(Type() == ENodeType::Mapping); + } + + TMappingIterator begin() const { + return TMappingIterator(Node_); + } + + TMappingIterator end() const { + return TMappingIterator(Node_, true); + } + + TReverseMappingIterator rbegin() const { + return TReverseMappingIterator(Node_); + } + + TReverseMappingIterator rend() const { + return TReverseMappingIterator(Node_, true); + } + + size_t size() const; + + size_t empty() const; + + TNodePairRef at(int index) const; + + TNodePairRef operator[](int index) const; + + TNodeRef at(const TString& index) const; + + TNodePairRef pair_at(const TString& index) const; + + TNodePairRef pair_at_opt(const TString& index) const; + + TNodeRef operator[](const TString& index) const; + + TNodeRef operator[](const char* str) const; + + void Append(const TNodeRef& key, const TNodeRef& value); + + void Prepend(const TNodeRef& key, const TNodeRef& value); + + void Remove(const TNodePairRef& toRemove); + + TMappingIterator Remove(const TMappingIterator& toRemove); + + void Remove(const TNodeRef& key); +}; + +class TSequenceIterator { + friend class TSequence; +public: + TSequenceIterator(const TNodeRef& node, bool end = false); + + TSequenceIterator(const TSequenceIterator& other) { + Node_ = other.Node_; + IterNode_ = other.IterNode_; + Iter_ = other.Iter_; + } + + TSequenceIterator& operator=(const TSequenceIterator& other) { + Node_ = other.Node_; + IterNode_ = other.IterNode_; + Iter_ = other.Iter_; + return *this; + } + + TSequenceIterator& operator++(); + + const TNodeRef* operator->() const { + return &IterNode_; + } + + TSequenceIterator operator++(int) { + TSequenceIterator retval = *this; + ++(*this); + return retval; + } + + bool operator==(TSequenceIterator other) const { return Node_ == other.Node_ && Iter_ == other.Iter_; } + + bool operator!=(TSequenceIterator other) const { return !(*this == other); } + + const TNodeRef& operator*() const { return IterNode_; } + + void InsertBefore(const TNodeRef& node); + + void InsertAfter(const TNodeRef& node); + +private: + TNodeRef Node_; + TNodeRef IterNode_; + void* Iter_ = nullptr; +}; + +class TReverseSequenceIterator { + friend class TSequence; +public: + TReverseSequenceIterator(const TNodeRef& node, bool end = false); + + TReverseSequenceIterator(const TReverseSequenceIterator& other) { + Node_ = other.Node_; + IterNode_ = other.IterNode_; + Iter_ = other.Iter_; + } + + TReverseSequenceIterator& operator=(const TReverseSequenceIterator& other) { + Node_ = other.Node_; + IterNode_ = other.IterNode_; + Iter_ = other.Iter_; + return *this; + } + + TReverseSequenceIterator& operator++(); + + const TNodeRef* operator->() const { + return &IterNode_; + } + + TReverseSequenceIterator operator++(int) { + TReverseSequenceIterator retval = *this; + ++(*this); + return retval; + } + + bool operator==(TReverseSequenceIterator other) const { return Node_ == other.Node_ && Iter_ == other.Iter_; } + + bool operator!=(TReverseSequenceIterator other) const { return !(*this == other); } + + const TNodeRef& operator*() const { return IterNode_; } + + void InsertBefore(const TNodeRef& node); + + void InsertAfter(const TNodeRef& node); + +private: + TNodeRef Node_; + TNodeRef IterNode_; + void* Iter_ = nullptr; +}; + +class TSequence : public TNodeRef { +public: + explicit TSequence(const TNodeRef& node) + : TNodeRef(node) + { + Y_VERIFY_DEBUG(Type() == ENodeType::Sequence); + } + + TSequenceIterator begin() const { + return TSequenceIterator(Node_); + } + + TSequenceIterator end() const { + return TSequenceIterator(Node_, true); + } + + TReverseSequenceIterator rbegin() const { + return TReverseSequenceIterator(Node_); + } + + TReverseSequenceIterator rend() const { + return TReverseSequenceIterator(Node_, true); + } + + size_t size() const; + + size_t empty() const; + + TNodeRef at(int index) const; + + TNodeRef operator[](int index) const; + + void Append(const TNodeRef& node); + + void Prepend(const TNodeRef& node); + + void InsertBefore(const TNodeRef& mark, const TNodeRef& node); + + void InsertAfter(const TNodeRef& mark, const TNodeRef& node); + + TNode Remove(const TNodeRef& toRemove); + + TSequenceIterator Remove(const TSequenceIterator& toRemove); + + TReverseSequenceIterator Remove(const TReverseSequenceIterator& toRemove); +}; + +class TDocumentNodeIterator + : public TDocumentIterator +{ +public: + TDocumentNodeIterator(TNodeRef&& node); + + TDocumentNodeIterator(const TDocumentNodeIterator& other) + : TDocumentIterator(other.Iterator_.get()) + , Node_(other.Node_) + {} + + TDocumentNodeIterator& operator=(const TDocumentNodeIterator& other) { + Iterator_.reset(other.Iterator_.get()); + Node_ = other.Node_; + return *this; + } + + TDocumentNodeIterator& operator++(); + + TNodeRef* operator->() { + return &Node_; + } + + TDocumentNodeIterator operator++(int) { + TDocumentNodeIterator retval = *this; + ++(*this); + return retval; + } + + bool operator==(TDocumentNodeIterator other) const { return Node_ == other.Node_; } + + bool operator!=(TDocumentNodeIterator other) const { return !(*this == other); } + + TNodeRef& operator*() { return Node_; } + +private: + TNodeRef Node_; +}; + +class TDocument { + friend class TNode; + friend class TNodeRef; + friend class TJsonEmitter; + friend class TParser; + friend class TMapping; + + TDocument(TString str, fy_document* doc = nullptr, fy_diag* diag = nullptr); + TDocument(fy_document* doc = nullptr, fy_diag* diag = nullptr); + +public: + TDocument(TDocument&& other) + : Document_(std::move(other.Document_)) + , Diag_(std::move(other.Diag_)) + {} + + static TDocument Parse(TString cstr); + + TDocument Clone() const; + + template <class... Args> + size_t Scanf(const char* fmt, Args&& ...args) { + Y_VERIFY_DEBUG(Document_); + return fy_document_scanf(Document_.get(), fmt, std::forward<Args>(args)...); + } + + void InsertAt(const char* path, const TNodeRef& node); + + template <class... Args> + TNodeRef Buildf(const char* fmt, Args&& ...args) { + Y_VERIFY_DEBUG(Document_); + return fy_node_buildf(Document_.get(), fmt, std::forward<Args>(args)...); + } + + TNodeRef Buildf(const char* content); + + void Resolve(); + + bool HasDirectives(); + + bool HasExplicitDocumentStart(); + + bool HasExplicitDocumentEnd(); + + void SetParent(const TDocument& doc); + + TNodeRef Root(); + + void SetRoot(const TNodeRef& node); + + TDocumentNodeIterator begin() { + auto it = TDocumentNodeIterator(Root()); + ++it; + return it; + } + + TDocumentNodeIterator end() { + return TDocumentNodeIterator(TNodeRef(nullptr)); + } + + TNodeRef CreateAlias(const TString& name); + + std::unique_ptr<char, void(*)(char*)> EmitToCharArray() const; + + TMark BeginMark() const; + + TMark EndMark() const; + +private: + std::unique_ptr<fy_document, void(*)(fy_document*)> Document_; + std::unique_ptr<fy_diag, void(*)(fy_diag*)> Diag_; + + static void DestroyUserData(fy_node *fyn, void *meta, void *user) { + Y_UNUSED(fyn); + Y_UNUSED(user); + if (meta) { + auto* data = reinterpret_cast<NDetail::IBasicUserData*>(meta); + delete data; + } + } + + static void DestroyDocumentStrings(fy_document *fyd, void *user) { + Y_UNUSED(fyd); + if (user) { + auto* data = reinterpret_cast<THashSet<TSimpleSharedPtr<TString>, TStringPtrHashT>*>(user); + delete data; + } + } + + bool RegisterUserDataCleanup(); + void UnregisterUserDataCleanup(); +}; + +class TJsonEmitter { +public: + TJsonEmitter(TDocument& doc) : Node_(doc.Root()) {} + TJsonEmitter(const TNodeRef& node) : Node_(node) {} + + std::unique_ptr<char, void(*)(char*)> EmitToCharArray() const; + +private: + const TNodeRef Node_; +}; + +class TParser { + TParser(TString rawStream, fy_parser* doc, fy_diag* diag); +public: + static TParser Create(TString str); + + std::optional<TDocument> NextDocument(); +private: + TString RawDocumentStream_; + std::unique_ptr<fy_parser, void(*)(fy_parser*)> Parser_; + std::unique_ptr<fy_diag, void(*)(fy_diag*)> Diag_; +}; + +struct TMark { + size_t InputPos; + int Line; + int Column; +}; + +} // namesapce NFyaml diff --git a/library/cpp/yaml/fyamlcpp/fyamlcpp_ut.cpp b/library/cpp/yaml/fyamlcpp/fyamlcpp_ut.cpp new file mode 100644 index 0000000000..ffe1d4368c --- /dev/null +++ b/library/cpp/yaml/fyamlcpp/fyamlcpp_ut.cpp @@ -0,0 +1,156 @@ +#include "fyamlcpp.h" + +#include <contrib/libs/libfyaml/include/libfyaml.h> + +#include <library/cpp/testing/unittest/registar.h> + +#include <util/stream/str.h> + +Y_UNIT_TEST_SUITE(FYamlCpp) { + Y_UNIT_TEST(EnumEquals) { + UNIT_ASSERT_EQUAL((int)NFyaml::ENodeType::Scalar, FYNT_SCALAR); + UNIT_ASSERT_EQUAL((int)NFyaml::ENodeType::Sequence, FYNT_SEQUENCE); + UNIT_ASSERT_EQUAL((int)NFyaml::ENodeType::Mapping, FYNT_MAPPING); + } + + Y_UNIT_TEST(ErrorHandling) { + { + const char *yaml = R"( +config: a +config: b +)"; + UNIT_ASSERT_EXCEPTION_CONTAINS( + NFyaml::TDocument::Parse(yaml), + yexception, + "3:1 duplicate key"); + } + + { + const char *yaml = R"( +anchor: *does_not_exists +)"; + auto doc = NFyaml::TDocument::Parse(yaml); + UNIT_ASSERT_EXCEPTION_CONTAINS( + doc.Resolve(), + yexception, + "2:10 invalid alias"); + } + } + + Y_UNIT_TEST(Out) { + const char *yaml = R"( +test: output +)"; + + auto doc = NFyaml::TDocument::Parse(yaml); + + TStringStream ss; + + ss << doc; + + UNIT_ASSERT_VALUES_EQUAL(ss.Str(), "test: output\n"); + } + + Y_UNIT_TEST(Parser) { + const char *yaml = R"( +test: a +--- +test: b +)"; + auto parser = NFyaml::TParser::Create(yaml); + + TStringStream ss; + + auto docOpt = parser.NextDocument(); + UNIT_ASSERT(docOpt); + ss << *docOpt; + UNIT_ASSERT_VALUES_EQUAL(ss.Str(), "test: a\n"); + auto beginMark = docOpt->BeginMark(); + UNIT_ASSERT_VALUES_EQUAL(beginMark.InputPos, 1); + auto endMark = docOpt->EndMark(); + UNIT_ASSERT_VALUES_EQUAL(endMark.InputPos, 12); + UNIT_ASSERT_VALUES_EQUAL(TStringBuf(yaml).SubStr(beginMark.InputPos, endMark.InputPos - 4), ss.Str()); + + ss.clear(); + + auto docOpt2 = parser.NextDocument(); + UNIT_ASSERT(docOpt2); + ss << *docOpt2; + UNIT_ASSERT_VALUES_EQUAL(ss.Str(), "---\ntest: b\n"); + beginMark = docOpt2->BeginMark(); + UNIT_ASSERT_VALUES_EQUAL(beginMark.InputPos, 9); + endMark = docOpt2->EndMark(); + UNIT_ASSERT_VALUES_EQUAL(endMark.InputPos, 21); + UNIT_ASSERT_VALUES_EQUAL(TStringBuf(yaml).SubStr(beginMark.InputPos, endMark.InputPos), ss.Str()); + + auto docOpt3 = parser.NextDocument(); + UNIT_ASSERT(!docOpt3); + } + + Y_UNIT_TEST(Leak) { + std::optional<NFyaml::TDocument> doc; + + { + std::optional<NFyaml::TDocument> item1; + std::optional<NFyaml::TDocument> item2; + { + const TString items = R"( +item: + x: 1 + y: 2 +--- +test: + a: noop + b: + - 1 + - 2 + - 3 +--- +x: b +)"; + auto parser = NFyaml::TParser::Create(items); + + item1.emplace(*parser.NextDocument()); + item2.emplace(*parser.NextDocument()); + parser.NextDocument(); + parser.NextDocument(); + } + + { + const TString config = R"( +test: a +--- +test: [] +--- +x: b +)"; + auto parser = NFyaml::TParser::Create(config); + + parser.NextDocument(); + doc.emplace(*parser.NextDocument()); + parser.NextDocument(); + parser.NextDocument(); + } + + { + auto item1NodeRef = item1->Root().Map().at("item"); + auto item2NodeRef = item2->Root().Map().at("test"); + auto docNodeRef = doc->Root().Map().at("test"); + auto node1 = item1NodeRef.Copy(*doc); + auto node2 = item2NodeRef.Copy(*doc); + docNodeRef.Sequence().Append(node1.Ref()); + docNodeRef.Sequence().Append(node2.Ref()); + item1.reset(); + item2.reset(); + } + } + + auto seq = doc->Root().Map().at("test").Sequence(); + UNIT_ASSERT_VALUES_EQUAL(seq[0].Map().at("x").Scalar(), "1"); + UNIT_ASSERT_VALUES_EQUAL(seq[0].Map().at("y").Scalar(), "2"); + UNIT_ASSERT_VALUES_EQUAL(seq[1].Map().at("a").Scalar(), "noop"); + UNIT_ASSERT_VALUES_EQUAL(seq[1].Map().at("b").Sequence().at(0).Scalar(), "1"); + UNIT_ASSERT_VALUES_EQUAL(seq[1].Map().at("b").Sequence().at(1).Scalar(), "2"); + UNIT_ASSERT_VALUES_EQUAL(seq[1].Map().at("b").Sequence().at(2).Scalar(), "3"); + } +} diff --git a/library/cpp/yaml/fyamlcpp/libfyaml_ut.cpp b/library/cpp/yaml/fyamlcpp/libfyaml_ut.cpp new file mode 100644 index 0000000000..d5e28cf98e --- /dev/null +++ b/library/cpp/yaml/fyamlcpp/libfyaml_ut.cpp @@ -0,0 +1,1838 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <limits.h> + +#include <span> + +#include <contrib/libs/libfyaml/include/libfyaml.h> +#include <library/cpp/testing/unittest/registar.h> + +/* +These tests are ported from https://github.com/pantoniou/libfyaml/blob/master/test/libfyaml-test-core.c +To check windows compatibility and avoid possible internal patch issues +*/ + +Y_UNIT_TEST_SUITE(LibFyamlCore) { + +Y_UNIT_TEST(doc_build_simple) { + struct fy_document *fyd; + + /* setup */ + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* cleanup */ + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_build_parse_check) { + struct fy_document *fyd; + char *buf; + + /* build document (with comments, newlines etc) */ + fyd = fy_document_build_from_string(NULL, "#comment\n[ 42, \n 12 ] # comment\n", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* convert to string */ + buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* compare with expected result */ + UNIT_ASSERT_VALUES_EQUAL(buf, TString("[42, 12]\n")); + + free(buf); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_build_scalar) { + struct fy_document *fyd; + + /* build document */ + fyd = fy_document_build_from_string(NULL, "plain scalar # comment", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* compare with expected result */ + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_document_root(fyd)), TString("plain scalar")); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_build_sequence) { + struct fy_document *fyd; + struct fy_node *fyn; + int count; + void *iter; + + /* build document */ + fyd = fy_document_build_from_string(NULL, "[ 10, 11, foo ] # comment", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* check for correct count value */ + count = fy_node_sequence_item_count(fy_document_root(fyd)); + UNIT_ASSERT_EQUAL(count, 3); + + /* try forward iterator first */ + iter = NULL; + + /* 0 */ + fyn = fy_node_sequence_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("10")); + + /* 1 */ + fyn = fy_node_sequence_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("11")); + + /* 2 */ + fyn = fy_node_sequence_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("foo")); + + /* final, iterator must return NULL */ + fyn = fy_node_sequence_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_EQUAL(fyn, NULL); + + /* reverse iterator */ + iter = NULL; + + /* 2 */ + fyn = fy_node_sequence_reverse_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("foo")); + + /* 1 */ + fyn = fy_node_sequence_reverse_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("11")); + + /* 0 */ + fyn = fy_node_sequence_reverse_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("10")); + + /* final, iterator must return NULL */ + fyn = fy_node_sequence_reverse_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_EQUAL(fyn, NULL); + + /* do forward index based accesses */ + + /* 0 */ + fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), 0); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("10")); + + /* 1 */ + fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), 1); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("11")); + + /* 2 */ + fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), 2); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("foo")); + + /* 3, it must not exist */ + fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), 3); + UNIT_ASSERT_EQUAL(fyn, NULL); + + /* do backward index based accesses */ + + /* 2 */ + fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), -1); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("foo")); + + /* 1 */ + fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), -2); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("11")); + + /* 0 */ + fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), -3); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn), TString("10")); + + /* -1, it must not exist */ + fyn = fy_node_sequence_get_by_index(fy_document_root(fyd), -4); + UNIT_ASSERT_EQUAL(fyn, NULL); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_build_mapping) { + struct fy_document *fyd; + struct fy_node_pair *fynp; + int count; + void *iter; + + fyd = fy_document_build_from_string(NULL, "{ foo: 10, bar : 20, baz: [100, 101], [frob, 1]: boo }", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* check for correct count value */ + count = fy_node_mapping_item_count(fy_document_root(fyd)); + UNIT_ASSERT_EQUAL(count, 4); + + /* forward iterator first */ + iter = NULL; + + /* 0 */ + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_key(fynp)), TString("foo")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), TString("10")); + + /* 1 */ + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_key(fynp)), TString("bar")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), TString("20")); + + /* 2 */ + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_key(fynp)), TString("baz")); + UNIT_ASSERT_EQUAL(fy_node_sequence_item_count(fy_node_pair_value(fynp)), 2); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_value(fynp), 0)), TString("100")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_value(fynp), 1)), TString("101")); + + /* 3 */ + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_EQUAL(fy_node_sequence_item_count(fy_node_pair_key(fynp)), 2); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_key(fynp), 0)), TString("frob")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_key(fynp), 1)), TString("1")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), TString("boo")); + + /* 4, it must not exist */ + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_EQUAL(fynp, NULL); + + /* reverse iterator */ + iter = NULL; + + /* 3 */ + fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_EQUAL(fy_node_sequence_item_count(fy_node_pair_key(fynp)), 2); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_key(fynp), 0)), TString("frob")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_key(fynp), 1)), TString("1")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), TString("boo")); + + /* 2 */ + fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_key(fynp)), TString("baz")); + UNIT_ASSERT_EQUAL(fy_node_sequence_item_count(fy_node_pair_value(fynp)), 2); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_value(fynp), 0)), TString("100")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_sequence_get_by_index(fy_node_pair_value(fynp), 1)), TString("101")); + + /* 1 */ + fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_key(fynp)), TString("bar")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), TString("20")); + + /* 0 */ + fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_key(fynp)), TString("foo")); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), TString("10")); + + /* -1, it must not exist */ + fynp = fy_node_mapping_reverse_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_EQUAL(fynp, NULL); + + /* key lookups (note how only the contents are compared) */ + UNIT_ASSERT(fy_node_compare_string(fy_node_mapping_lookup_by_string(fy_document_root(fyd), "foo", FY_NT), "10", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_mapping_lookup_by_string(fy_document_root(fyd), "bar", FY_NT), "20", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_mapping_lookup_by_string(fy_document_root(fyd), "baz", FY_NT), "- 100\n- 101", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_mapping_lookup_by_string(fy_document_root(fyd), "- 'frob'\n- \"\x31\"", FY_NT), "boo", FY_NT) == true); + + fy_document_destroy(fyd); + fyd = NULL; +} + +Y_UNIT_TEST(doc_path_access) { + struct fy_document *fyd; + struct fy_node *fyn; + + /* build document */ + fyd = fy_document_build_from_string(NULL, "{ " + "foo: 10, bar : 20, baz:{ frob: boo }, " + "frooz: [ seq1, { key: value} ], \"zero\\0zero\" : 0, " + "{ key2: value2 }: { key3: value3 } " + "}", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* check that getting root node works */ + fyn = fy_node_by_path(fy_document_root(fyd), "/", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fy_document_root(fyd)); + + /* check access to scalars */ + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW), "10", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "bar", FY_NT, FYNWF_DONT_FOLLOW), "20", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "baz/frob", FY_NT, FYNWF_DONT_FOLLOW), "boo", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/frooz/0", FY_NT, FYNWF_DONT_FOLLOW), "seq1", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/frooz/1/key", FY_NT, FYNWF_DONT_FOLLOW), "value", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "\"zero\\0zero\"", FY_NT, FYNWF_DONT_FOLLOW), "0", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/{ key2: value2 }/key3", FY_NT, FYNWF_DONT_FOLLOW), "value3", FY_NT) == true); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_path_node) { + struct fy_document *fyd; + char *path; + + /* build document */ + fyd = fy_document_build_from_string(NULL, "{ " + "foo: 10, bar : 20, baz:{ frob: boo }, " + "frooz: [ seq1, { key: value} ], \"zero\\0zero\" : 0, " + "{ key2: value2 }: { key3: value3 } " + "}", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + path = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/", FY_NT, FYNWF_DONT_FOLLOW)); + UNIT_ASSERT_VALUES_EQUAL(path, TString("/")); + free(path); + + path = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/frooz", FY_NT, FYNWF_DONT_FOLLOW)); + UNIT_ASSERT_VALUES_EQUAL(path, TString("/frooz")); + free(path); + + path = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/frooz/0", FY_NT, FYNWF_DONT_FOLLOW)); + UNIT_ASSERT_VALUES_EQUAL(path, TString("/frooz/0")); + free(path); + + path = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/{ key2: value2 }/key3", FY_NT, FYNWF_DONT_FOLLOW)); + UNIT_ASSERT_VALUES_EQUAL(path, TString("/{key2: value2}/key3")); + free(path); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_path_parent) { + struct fy_document *fyd; + struct fy_node *fyn_root, *fyn_foo, *fyn_bar, *fyn_baz, *fyn_frob, *fyn_ten; + struct fy_node *fyn_deep, *fyn_deeper; + char *path; + + /* build document */ + fyd = fy_document_build_from_string(NULL, "{ " + "foo: 10, bar : [ ten, 20 ], baz:{ frob: boo, deep: { deeper: yet } }, " + "}", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn_root = fy_document_root(fyd); + UNIT_ASSERT_UNEQUAL(fyn_root, NULL); + + fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_foo, NULL); + + fyn_bar = fy_node_by_path(fyn_root, "/bar", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_bar, NULL); + + fyn_baz = fy_node_by_path(fyn_root, "/baz", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_baz, NULL); + + fyn_frob = fy_node_by_path(fyn_root, "/baz/frob", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_frob, NULL); + + fyn_ten = fy_node_by_path(fyn_root, "/bar/0", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_ten, NULL); + + fyn_deep = fy_node_by_path(fyn_root, "/baz/deep", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_deep, NULL); + + fyn_deeper = fy_node_by_path(fyn_root, "/baz/deep/deeper", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_deeper, NULL); + + /* check parent paths of foo, frob, ten */ + path = fy_node_get_parent_address(fyn_foo); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("foo")); + free(path); + + path = fy_node_get_parent_address(fyn_frob); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("frob")); + free(path); + + path = fy_node_get_parent_address(fyn_ten); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("0")); + free(path); + + /* check relative paths to root */ + path = fy_node_get_path_relative_to(NULL, fyn_foo); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("foo")); + free(path); + + path = fy_node_get_path_relative_to(fyn_root, fyn_foo); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("foo")); + free(path); + + path = fy_node_get_path_relative_to(fyn_root, fyn_frob); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("baz/frob")); + free(path); + + path = fy_node_get_path_relative_to(fyn_root, fyn_ten); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("bar/0")); + free(path); + + /* check relative paths to other parents */ + path = fy_node_get_path_relative_to(fyn_baz, fyn_frob); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("frob")); + free(path); + + path = fy_node_get_path_relative_to(fyn_bar, fyn_ten); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("0")); + free(path); + + path = fy_node_get_path_relative_to(fyn_baz, fyn_deeper); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("deep/deeper")); + free(path); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_short_path) { + struct fy_document *fyd; + struct fy_node *fyn_root, *fyn_foo, *fyn_notfoo, *fyn_bar, *fyn_baz; + const char *str; + + /* build document */ + fyd = fy_document_build_from_string(NULL, + "--- &r\n" + " foo: &f\n" + " bar: [ 0, two, baz: what ]\n" + " frob: true\n" + " notfoo: false\n" + , FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn_root = fy_document_root(fyd); + UNIT_ASSERT_UNEQUAL(fyn_root, NULL); + + fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_foo, NULL); + + fyn_notfoo = fy_node_by_path(fyn_root, "/notfoo", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_notfoo, NULL); + + fyn_bar = fy_node_by_path(fyn_root, "/foo/bar", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_bar, NULL); + + fyn_baz = fy_node_by_path(fyn_root, "/foo/bar/2/baz", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_baz, NULL); + + str = fy_node_get_short_path(fyn_root); + UNIT_ASSERT_UNEQUAL(str, NULL); + UNIT_ASSERT_VALUES_EQUAL(str, "*r"); + UNIT_ASSERT_EQUAL(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_root); + free((void*)str); + + str = fy_node_get_short_path(fyn_foo); + UNIT_ASSERT_UNEQUAL(str, NULL); + UNIT_ASSERT_VALUES_EQUAL(str, "*f"); + UNIT_ASSERT_EQUAL(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_foo); + free((void*)str); + + str = fy_node_get_short_path(fyn_notfoo); + UNIT_ASSERT_UNEQUAL(str, NULL); + UNIT_ASSERT_VALUES_EQUAL(str, "*r/notfoo"); + UNIT_ASSERT_EQUAL(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_notfoo); + free((void*)str); + + str = fy_node_get_short_path(fyn_bar); + UNIT_ASSERT_UNEQUAL(str, NULL); + UNIT_ASSERT_VALUES_EQUAL(str, "*f/bar"); + UNIT_ASSERT_EQUAL(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_bar); + free((void*)str); + + str = fy_node_get_short_path(fyn_baz); + UNIT_ASSERT_UNEQUAL(str, NULL); + UNIT_ASSERT_VALUES_EQUAL(str, "*f/bar/2/baz"); + UNIT_ASSERT_EQUAL(fy_node_by_path(fy_document_root(fyd), str, FY_NT, FYNWF_FOLLOW), fyn_baz); + free((void*)str); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_scalar_path) { + struct fy_document *fyd; + struct fy_node *fyn_root, *fyn_foo; + + /* build document */ + fyd = fy_document_build_from_string(NULL, "--- foo\n", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn_root = fy_document_root(fyd); + UNIT_ASSERT_UNEQUAL(fyn_root, NULL); + + /* get the scalar root and verify */ + fyn_foo = fy_node_by_path(fyn_root, "/", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_foo, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fyn_foo), "foo"); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_scalar_path_array) { + struct fy_document *fyd; + struct fy_node *fyn_root, *fynt; + + /* build document */ + fyd = fy_document_build_from_string(NULL, "--- [ foo, bar, baz ]\n", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn_root = fy_document_root(fyd); + UNIT_ASSERT_UNEQUAL(fyn_root, NULL); + + /* get the scalars in the array and verify */ + fynt = fy_node_by_path(fyn_root, "/0", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fynt, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fynt), "foo"); + + fynt = fy_node_by_path(fyn_root, "/1", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fynt, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fynt), "bar"); + + fynt = fy_node_by_path(fyn_root, "/2", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fynt, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fynt), "baz"); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_nearest_anchor) { + struct fy_document *fyd; + struct fy_node *fyn, *fyn_root, *fyn_foo, *fyn_notfoo, *fyn_bar, *fyn_baz; + + /* build document */ + fyd = fy_document_build_from_string(NULL, + "--- &r\n" + " foo: &f\n" + " bar: [ 0, two, baz: what ]\n" + " frob: true\n" + " notfoo: false\n" + , FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn_root = fy_document_root(fyd); + UNIT_ASSERT_UNEQUAL(fyn_root, NULL); + + fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_foo, NULL); + + fyn_notfoo = fy_node_by_path(fyn_root, "/notfoo", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_notfoo, NULL); + + fyn_bar = fy_node_by_path(fyn_root, "/foo/bar", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_bar, NULL); + + fyn_baz = fy_node_by_path(fyn_root, "/foo/bar/2/baz", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_baz, NULL); + + /* get nearest anchor of root (is root) */ + fyn = fy_anchor_node(fy_node_get_nearest_anchor(fyn_root)); + UNIT_ASSERT_EQUAL(fyn, fyn_root); + + /* get nearest anchor of notfoo (is root) */ + fyn = fy_anchor_node(fy_node_get_nearest_anchor(fyn_notfoo)); + UNIT_ASSERT_EQUAL(fyn, fyn_root); + + /* get nearest anchor of baz (is foo) */ + fyn = fy_anchor_node(fy_node_get_nearest_anchor(fyn_baz)); + UNIT_ASSERT_EQUAL(fyn, fyn_foo); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_references) { + struct fy_document *fyd; + struct fy_node *fyn, *fyn_ref, *fyn_root, *fyn_foo, *fyn_notfoo, *fyn_bar, *fyn_baz; + char *path; + + /* build document */ + fyd = fy_document_build_from_string(NULL, + "---\n" + " foo: &f\n" + " bar: [ 0, two, baz: what ]\n" + " frob: true\n" + " notfoo: false\n" + , FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn_root = fy_document_root(fyd); + UNIT_ASSERT_UNEQUAL(fyn_root, NULL); + + fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_foo, NULL); + + fyn_notfoo = fy_node_by_path(fyn_root, "/notfoo", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_notfoo, NULL); + + fyn_bar = fy_node_by_path(fyn_root, "/foo/bar", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_bar, NULL); + + fyn_baz = fy_node_by_path(fyn_root, "/foo/bar/2/baz", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_baz, NULL); + + /* get reference to root */ + path = fy_node_get_reference(fyn_root); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("*/")); + free(path); + + /* get reference to /foo */ + path = fy_node_get_reference(fyn_foo); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("*f")); + free(path); + + /* get reference to /notfoo */ + path = fy_node_get_reference(fyn_notfoo); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("*/notfoo")); + free(path); + + /* get reference to /foo/bar/2/baz */ + path = fy_node_get_reference(fyn_baz); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("*/foo/bar/2/baz")); + free(path); + + /* create reference to root and verify it points there */ + fyn_ref = fy_node_create_reference(fyn_root); + UNIT_ASSERT_UNEQUAL(fyn_ref, NULL); + fyn = fy_node_resolve_alias(fyn_ref); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_root); + fy_node_free(fyn_ref); + + /* get reference to /foo */ + fyn_ref = fy_node_create_reference(fyn_foo); + UNIT_ASSERT_UNEQUAL(path, NULL); + fyn = fy_node_resolve_alias(fyn_ref); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_foo); + fy_node_free(fyn_ref); + + /* get reference to /notfoo */ + fyn_ref = fy_node_create_reference(fyn_notfoo); + UNIT_ASSERT_UNEQUAL(path, NULL); + fyn = fy_node_resolve_alias(fyn_ref); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_notfoo); + fy_node_free(fyn_ref); + + /* get reference to /bar */ + fyn_ref = fy_node_create_reference(fyn_bar); + UNIT_ASSERT_UNEQUAL(path, NULL); + fyn = fy_node_resolve_alias(fyn_ref); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_bar); + fy_node_free(fyn_ref); + + /* get reference to /baz */ + fyn_ref = fy_node_create_reference(fyn_baz); + UNIT_ASSERT_UNEQUAL(path, NULL); + fyn = fy_node_resolve_alias(fyn_ref); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_baz); + fy_node_free(fyn_ref); + + /* get relative reference to /foo starting at / */ + path = fy_node_get_relative_reference(fyn_root, fyn_foo); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("*/foo")); + free(path); + + /* get relative reference to /foo/bar/2/baz starting at / */ + path = fy_node_get_relative_reference(fyn_root, fyn_baz); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("*/foo/bar/2/baz")); + free(path); + + /* get relative reference to /foo/bar/2/baz starting at /foo */ + path = fy_node_get_relative_reference(fyn_foo, fyn_baz); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("*f/bar/2/baz")); + free(path); + + /* get relative reference to /notfoo at /foo (will return absolute) */ + path = fy_node_get_relative_reference(fyn_foo, fyn_notfoo); + UNIT_ASSERT_UNEQUAL(path, NULL); + UNIT_ASSERT_VALUES_EQUAL(path, TString("*/notfoo")); + free(path); + + /* create relative reference to /foo starting at / */ + fyn_ref = fy_node_create_relative_reference(fyn_root, fyn_foo); + UNIT_ASSERT_UNEQUAL(fyn_ref, NULL); + fyn = fy_node_resolve_alias(fyn_ref); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_foo); + fy_node_free(fyn_ref); + + /* create relative reference to /foo/bar/2/baz starting at / */ + fyn_ref = fy_node_create_relative_reference(fyn_root, fyn_baz); + UNIT_ASSERT_UNEQUAL(fyn_ref, NULL); + fyn = fy_node_resolve_alias(fyn_ref); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_baz); + fy_node_free(fyn_ref); + + /* create relative reference to /foo/bar/2/baz starting at /foo */ + fyn_ref = fy_node_create_relative_reference(fyn_foo, fyn_baz); + UNIT_ASSERT_UNEQUAL(fyn_ref, NULL); + fyn = fy_node_resolve_alias(fyn_ref); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_baz); + fy_node_free(fyn_ref); + + /* create relative reference to /notfoo starting at /foo (will use absolute) */ + fyn_ref = fy_node_create_relative_reference(fyn_foo, fyn_notfoo); + UNIT_ASSERT_UNEQUAL(fyn_ref, NULL); + fyn = fy_node_resolve_alias(fyn_ref); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_notfoo); + fy_node_free(fyn_ref); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_nearest_child_of) { + struct fy_document *fyd; + struct fy_node *fyn, *fyn_root, *fyn_foo, *fyn_bar, *fyn_baz; + + /* build document */ + fyd = fy_document_build_from_string(NULL, + "foo:\n" + " bar:\n" + " barz: [ zero, baz: true ]\n" + " frooz: notfoo\n" + , FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn_root = fy_document_root(fyd); + UNIT_ASSERT_UNEQUAL(fyn_root, NULL); + + fyn_foo = fy_node_by_path(fyn_root, "/foo", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_foo, NULL); + + fyn_bar = fy_node_by_path(fyn_root, "/foo/bar", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_bar, NULL); + + fyn_baz = fy_node_by_path(fyn_root, "/foo/bar/barz/1/baz", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_baz, NULL); + + /* nearest child to the root of /foo is /foo */ + fyn = fy_node_get_nearest_child_of(fyn_root, fyn_foo); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_foo); + + /* nearest child to the root of /foo/bar/barz/1/baz is /foo */ + fyn = fy_node_get_nearest_child_of(fyn_root, fyn_baz); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_foo); + + /* nearest child to foo of /foo/bar/barz/1/baz is /foo/bar */ + fyn = fy_node_get_nearest_child_of(fyn_foo, fyn_baz); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + UNIT_ASSERT_EQUAL(fyn, fyn_bar); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_create_empty_seq1) { + struct fy_document *fyd; + struct fy_node *fyn; + char *buf; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn = fy_node_build_from_string(fyd, "[ ]", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + + /* convert to string */ + buf = fy_emit_node_to_string(fy_document_root(fyd), FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* compare with expected result */ + UNIT_ASSERT_VALUES_EQUAL(buf, TString("[]")); + + free(buf); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_create_empty_seq2) { + struct fy_document *fyd; + struct fy_node *fyn; + char *buf; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn = fy_node_create_sequence(fyd); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + + /* convert to string */ + buf = fy_emit_node_to_string(fy_document_root(fyd), FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* compare with expected result */ + UNIT_ASSERT_VALUES_EQUAL(buf, TString("[]")); + + free(buf); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_create_empty_map1) { + struct fy_document *fyd; + struct fy_node *fyn; + char *buf; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn = fy_node_build_from_string(fyd, "{ }", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + + /* convert to string */ + buf = fy_emit_node_to_string(fy_document_root(fyd), FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* compare with expected result */ + UNIT_ASSERT_VALUES_EQUAL(buf, TString("{}")); + + free(buf); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_create_empty_map2) { + struct fy_document *fyd; + struct fy_node *fyn; + char *buf; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn = fy_node_create_mapping(fyd); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + + /* convert to string */ + buf = fy_emit_node_to_string(fy_document_root(fyd), FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* compare with expected result */ + UNIT_ASSERT_VALUES_EQUAL(buf, TString("{}")); + + free(buf); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_create_test_seq1) { + struct fy_document *fyd; + struct fy_node *fyn; + int ret; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn = fy_node_create_sequence(fyd); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + ret = fy_node_sequence_append(fyn, fy_node_create_scalar(fyd, "foo", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + ret = fy_node_sequence_append(fyn, fy_node_create_scalar(fyd, "bar", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + ret = fy_node_sequence_append(fyn, fy_node_build_from_string(fyd, "{ baz: frooz }", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + fy_document_set_root(fyd, fyn); + + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/0", FY_NT, FYNWF_DONT_FOLLOW), "foo", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/1", FY_NT, FYNWF_DONT_FOLLOW), "bar", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/2/baz", FY_NT, FYNWF_DONT_FOLLOW), "frooz", FY_NT) == true); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_create_test_map1) { + struct fy_document *fyd; + struct fy_node *fyn, *fyn1, *fyn2, *fyn3; + int ret; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn = fy_node_create_mapping(fyd); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + ret = fy_node_mapping_append(fyn, + fy_node_build_from_string(fyd, "seq", FY_NT), + fy_node_build_from_string(fyd, "[ zero, one ]", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + ret = fy_node_mapping_append(fyn, NULL, + fy_node_build_from_string(fyd, "value-of-null-key", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + ret = fy_node_mapping_append(fyn, + fy_node_build_from_string(fyd, "key-of-null-value", FY_NT), NULL); + UNIT_ASSERT_EQUAL(ret, 0); + + fy_document_set_root(fyd, fyn); + + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/seq/0", FY_NT, FYNWF_DONT_FOLLOW), "zero", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/seq/1", FY_NT, FYNWF_DONT_FOLLOW), "one", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/''", FY_NT, FYNWF_DONT_FOLLOW), "value-of-null-key", FY_NT) == true); + + fyn1 = fy_node_by_path(fy_document_root(fyd), "/key-of-null-value", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_EQUAL(fyn1, NULL); + + /* try to append duplicate key (it should fail) */ + fyn2 = fy_node_build_from_string(fyd, "seq", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn2, NULL); + fyn3 = fy_node_create_scalar(fyd, "dupl", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn3, NULL); + ret = fy_node_mapping_append(fyn, fyn2, fyn3); + UNIT_ASSERT_UNEQUAL(ret, 0); + + fy_node_free(fyn3); + fy_node_free(fyn2); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_insert_remove_seq) { + struct fy_document *fyd; + struct fy_node *fyn; + int ret; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fy_document_set_root(fyd, fy_node_build_from_string(fyd, "[ one, two, four ]", FY_NT)); + + /* check that the order is correct */ + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/0", FY_NT, FYNWF_DONT_FOLLOW), "one", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/1", FY_NT, FYNWF_DONT_FOLLOW), "two", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/2", FY_NT, FYNWF_DONT_FOLLOW), "four", FY_NT) == true); + + ret = fy_node_sequence_append(fy_document_root(fyd), fy_node_build_from_string(fyd, "five", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + ret = fy_node_sequence_prepend(fy_document_root(fyd), fy_node_build_from_string(fyd, "zero", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + ret = fy_node_sequence_insert_after(fy_document_root(fyd), + fy_node_by_path(fy_document_root(fyd), "/2", FY_NT, FYNWF_DONT_FOLLOW), + fy_node_build_from_string(fyd, "three", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + ret = fy_node_sequence_insert_before(fy_document_root(fyd), + fy_node_by_path(fy_document_root(fyd), "/3", FY_NT, FYNWF_DONT_FOLLOW), + fy_node_build_from_string(fyd, "two-and-a-half", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + fyn = fy_node_sequence_remove(fy_document_root(fyd), + fy_node_by_path(fy_document_root(fyd), "/3", FY_NT, FYNWF_DONT_FOLLOW)); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_node_free(fyn); + + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/0", FY_NT, FYNWF_DONT_FOLLOW), "zero", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/1", FY_NT, FYNWF_DONT_FOLLOW), "one", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/2", FY_NT, FYNWF_DONT_FOLLOW), "two", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/3", FY_NT, FYNWF_DONT_FOLLOW), "three", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/4", FY_NT, FYNWF_DONT_FOLLOW), "four", FY_NT) == true); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_insert_remove_map) { + struct fy_document *fyd; + struct fy_node *fyn; + int ret; + + fyd = fy_document_build_from_string(NULL, "{ one: 1, two: 2, four: 4 }", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* check that the order is correct */ + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/one", FY_NT, FYNWF_DONT_FOLLOW), "1", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/two", FY_NT, FYNWF_DONT_FOLLOW), "2", FY_NT) == true); + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/four", FY_NT, FYNWF_DONT_FOLLOW), "4", FY_NT) == true); + + ret = fy_node_mapping_append(fy_document_root(fyd), + fy_node_build_from_string(fyd, "three", FY_NT), + fy_node_build_from_string(fyd, "3", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/three", FY_NT, FYNWF_DONT_FOLLOW), "3", FY_NT) == true); + + ret = fy_node_mapping_prepend(fy_document_root(fyd), + fy_node_build_from_string(fyd, "zero", FY_NT), + fy_node_build_from_string(fyd, "0", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/zero", FY_NT, FYNWF_DONT_FOLLOW), "0", FY_NT) == true); + + ret = fy_node_mapping_append(fy_document_root(fyd), + fy_node_build_from_string(fyd, "two-and-a-half", FY_NT), + fy_node_build_from_string(fyd, "2.5", FY_NT)); + UNIT_ASSERT_EQUAL(ret, 0); + + UNIT_ASSERT(fy_node_compare_string(fy_node_by_path(fy_document_root(fyd), "/two-and-a-half", FY_NT, FYNWF_DONT_FOLLOW), "2.5", FY_NT) == true); + + fyn = fy_node_mapping_remove_by_key(fy_document_root(fyd), + fy_node_build_from_string(fyd, "two-and-a-half", FY_NT)); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_node_free(fyn); + + /* it must be removed */ + fyn = fy_node_by_path(fy_document_root(fyd), "/two-and-a-half", FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_EQUAL(fyn, NULL); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_sort) { + struct fy_document *fyd; + fy_node_pair *fynp; + void *iter; + int ret, count; + + fyd = fy_document_build_from_string(NULL, "{ a: 5, { z: bar }: 1, z: 7, " + "[ a, b, c] : 3, { a: whee } : 2 , " + "b: 6, [ z ]: 4 }", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + ret = fy_node_sort(fy_document_root(fyd), NULL, NULL); + UNIT_ASSERT_EQUAL(ret, 0); + + /* check for correct count value */ + count = fy_node_mapping_item_count(fy_document_root(fyd)); + UNIT_ASSERT_EQUAL(count, 7); + + /* forward iterator first */ + iter = NULL; + + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), "1"); + + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), "2"); + + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), "3"); + + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), "4"); + + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), "5"); + + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), "6"); + + fynp = fy_node_mapping_iterate(fy_document_root(fyd), &iter); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_pair_value(fynp)), "7"); + + fy_document_destroy(fyd); +} + +static char *join_docs(const char *tgt_text, const char *tgt_path, + const char *src_text, const char *src_path, + const char *emit_path) +{ + struct fy_document *fyd_tgt, *fyd_src; + struct fy_node *fyn_tgt, *fyn_src, *fyn_emit; + char *output; + int ret; + + /* insert which overwrites root ( <map> <- <scalar> ) */ + fyd_tgt = fy_document_build_from_string(NULL, tgt_text, FY_NT); + UNIT_ASSERT_UNEQUAL(fyd_tgt, NULL); + + fyd_src = fy_document_build_from_string(NULL, src_text, FY_NT); + UNIT_ASSERT_UNEQUAL(fyd_src, NULL); + + fyn_tgt = fy_node_by_path(fy_document_root(fyd_tgt), tgt_path, FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_tgt, NULL); + + fyn_src = fy_node_by_path(fy_document_root(fyd_src), src_path, FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_src, NULL); + + ret = fy_node_insert(fyn_tgt, fyn_src); + UNIT_ASSERT_EQUAL(ret, 0); + + ret = fy_document_set_parent(fyd_tgt, fyd_src); + UNIT_ASSERT_EQUAL(ret, 0); + + fyn_emit = fy_node_by_path(fy_document_root(fyd_tgt), emit_path, FY_NT, FYNWF_DONT_FOLLOW); + UNIT_ASSERT_UNEQUAL(fyn_emit, NULL); + + output = fy_emit_node_to_string(fyn_emit, (fy_emitter_cfg_flags) (FYECF_MODE_FLOW_ONELINE | FYECF_WIDTH_INF)); + UNIT_ASSERT_UNEQUAL(output, NULL); + + fy_document_destroy(fyd_tgt); + + return output; +} + +Y_UNIT_TEST(doc_join_scalar_to_scalar) { + char *output; + + output = join_docs( + "foo", "/", /* target */ + "bar", "/", /* source */ + "/"); /* emit path */ + + UNIT_ASSERT_VALUES_EQUAL(output, TString("bar")); + free(output); +} + +Y_UNIT_TEST(doc_join_scalar_to_map) { + char *output; + + output = join_docs( + "{ foo: baz }", "/", /* target */ + "bar", "/", /* source */ + "/"); /* emit path */ + + UNIT_ASSERT_VALUES_EQUAL(output, TString("bar")); + free(output); +} + +Y_UNIT_TEST(doc_join_scalar_to_seq) { + char *output; + + output = join_docs( + "[ foo, baz ]", "/", /* target */ + "bar", "/", /* source */ + "/"); /* emit path */ + + UNIT_ASSERT_VALUES_EQUAL(output, TString("bar")); + free(output); +} + +Y_UNIT_TEST(doc_join_map_to_scalar) { + char *output; + + output = join_docs( + "foo", "/", /* target */ + "{bar: baz}", "/", /* source */ + "/"); /* emit path */ + + UNIT_ASSERT_VALUES_EQUAL(output, TString("{bar: baz}")); + free(output); +} + +Y_UNIT_TEST(doc_join_map_to_seq) { + char *output; + + output = join_docs( + "[foo, frooz]", "/", /* target */ + "{bar: baz}", "/", /* source */ + "/"); /* emit path */ + + UNIT_ASSERT_VALUES_EQUAL(output, TString("{bar: baz}")); + free(output); +} + +Y_UNIT_TEST(doc_join_map_to_map) { + char *output; + + output = join_docs( + "{foo: frooz}", "/", /* target */ + "{bar: baz}", "/", /* source */ + "/"); /* emit path */ + + UNIT_ASSERT_VALUES_EQUAL(output, TString("{foo: frooz, bar: baz}")); + free(output); +} + +Y_UNIT_TEST(doc_join_seq_to_scalar) { + char *output; + + output = join_docs( + "foo", "/", /* target */ + "[bar, baz]", "/", /* source */ + "/"); /* emit path */ + + UNIT_ASSERT_VALUES_EQUAL(output, TString("[bar, baz]")); + free(output); +} + +Y_UNIT_TEST(doc_join_seq_to_seq) { + char *output; + + output = join_docs( + "[foo, frooz]", "/", /* target */ + "[bar, baz]", "/", /* source */ + "/"); /* emit path */ + + UNIT_ASSERT_VALUES_EQUAL(output, TString("[foo, frooz, bar, baz]")); + free(output); +} + +Y_UNIT_TEST(doc_join_seq_to_map) { + char *output; + + output = join_docs( + "{foo: frooz}", "/", /* target */ + "[bar, baz]", "/", /* source */ + "/"); /* emit path */ + + UNIT_ASSERT_VALUES_EQUAL(output, TString("[bar, baz]")); + free(output); +} + +Y_UNIT_TEST(doc_join_tags) { + char *output; + + output = join_docs( + "%TAG !a! tag:a.com,2019:\n" + "---\n" + "- !a!foo\n" + " foo: bar\n", "/", + "%TAG !b! tag:b.com,2019:\n" + "---\n" + "- !b!bar\n" + " something: other\n", "/", + "/"); + + UNIT_ASSERT_VALUES_EQUAL(output, TString("[!a!foo {foo: bar}, !b!bar {something: other}]")); + free(output); +} + +Y_UNIT_TEST(doc_build_with_tags) { + struct fy_document *fyd; + struct fy_node *fyn; + struct fy_token *fyt; + char *buf; + int rc; + + /* build document */ + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* create a sequence and set it as root */ + fyn = fy_node_create_sequence(fyd); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + fyn = NULL; + + /* create a node, containing a new tag */ + fyn = fy_node_build_from_string(fyd, "%TAG !e! tag:example.com,2000:app/\n---\n- foo\n- !e!foo bar\n", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + /* append it to the root of the document */ + rc = fy_node_sequence_append(fy_document_root(fyd), fyn); + UNIT_ASSERT_EQUAL(rc, 0); + fyn = NULL; + + /* there must be a new tag */ + fyt = fy_document_tag_directive_lookup(fyd, "!e!"); + UNIT_ASSERT_UNEQUAL(fyt, NULL); + + /* try to build another, but with a different !e! prefix, it must fail */ + fyn = fy_node_build_from_string(fyd, "%TAG !e! tag:example.com,2019:app/\n---\n- foo\n- !e!foo bar\n", FY_NT); + UNIT_ASSERT_EQUAL(fyn, NULL); + + /* manually add a tag */ + rc = fy_document_tag_directive_add(fyd, "!f!", "tag:example.com,2019:f/"); + UNIT_ASSERT_EQUAL(rc, 0); + + /* build a node with a tag that's already in the document */ + fyn = fy_node_build_from_string(fyd, "!f!whiz frooz\n", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + /* append it to the root of the document */ + rc = fy_node_sequence_append(fy_document_root(fyd), fyn); + UNIT_ASSERT_EQUAL(rc, 0); + fyn = NULL; + + /* convert to string */ + buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + free(buf); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(doc_attach_check) { + struct fy_document *fyd; + struct fy_node *fyn, *fyn_seq, *fyn_map; + struct fy_node *fyn_foo, *fyn_foo2, *fyn_bar, *fyn_baz; + struct fy_node_pair *fynp; + int rc; + + /* build document */ + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* create a sequence */ + fyn_seq = fy_node_create_sequence(fyd); + UNIT_ASSERT_UNEQUAL(fyn_seq, NULL); + + /* create a mapping */ + fyn_map = fy_node_create_mapping(fyd); + UNIT_ASSERT_UNEQUAL(fyn_map, NULL); + + /* create a simple scalar node foo */ + fyn_foo = fy_node_build_from_string(fyd, "foo", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn_foo, NULL); + + /* create another simple scalar node bar */ + fyn_bar = fy_node_build_from_string(fyd, "bar", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn_bar, NULL); + + /* create another simple scalar node baz */ + fyn_baz = fy_node_build_from_string(fyd, "baz", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn_baz, NULL); + + /* create a scalar node with the same content as foo */ + fyn_foo2 = fy_node_build_from_string(fyd, "foo", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn_foo2, NULL); + + /* set the root as the sequence */ + rc = fy_document_set_root(fyd, fyn_seq); + UNIT_ASSERT_EQUAL(rc, 0); + + /* should fail since fyn_seq is now attached */ + rc = fy_document_set_root(fyd, fyn_seq); + UNIT_ASSERT_UNEQUAL(rc, 0); + + /* freeing should fail, since it's attached too */ + rc = fy_node_free(fyn_seq); + UNIT_ASSERT_UNEQUAL(rc, 0); + + /* append it to the sequence */ + rc = fy_node_sequence_append(fyn_seq, fyn_foo); + UNIT_ASSERT_EQUAL(rc, 0); + + /* freeing should fail, since it's attached to the seq */ + rc = fy_node_free(fyn_foo); + UNIT_ASSERT_UNEQUAL(rc, 0); + + /* trying to append it to the sequence again should fail */ + rc = fy_node_sequence_append(fyn_seq, fyn_foo); + UNIT_ASSERT_UNEQUAL(rc, 0); + + /* append the mapping to the sequence */ + rc = fy_node_sequence_append(fyn_seq, fyn_map); + UNIT_ASSERT_EQUAL(rc, 0); + + /* this should fail, since foo is attached to the sequence */ + rc = fy_node_mapping_append(fyn_map, fyn_foo, fyn_bar); + UNIT_ASSERT_UNEQUAL(rc, 0); + + /* this should be OK, since foo2 is not attached */ + rc = fy_node_mapping_append(fyn_map, fyn_foo2, fyn_bar); + UNIT_ASSERT_EQUAL(rc, 0); + + /* remove foo from the sequence */ + fyn = fy_node_sequence_remove(fyn_seq, fyn_foo); + UNIT_ASSERT_EQUAL(fyn, fyn_foo); + + /* trying to append the same key should fail */ + rc = fy_node_mapping_append(fyn_map, fyn_foo, NULL); + UNIT_ASSERT_UNEQUAL(rc, 0); + + /* append the baz: NULL mapping */ + rc = fy_node_mapping_append(fyn_map, fyn_baz, NULL); + UNIT_ASSERT_EQUAL(rc, 0); + + /* get the baz: null node pair */ + fynp = fy_node_mapping_lookup_pair(fyn_map, fyn_baz); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_EQUAL(fy_node_pair_key(fynp), fyn_baz); + UNIT_ASSERT_EQUAL(fy_node_pair_value(fynp), NULL); + + /* trying to set the same key in the mapping should fail */ + rc = fy_node_pair_set_key(fynp, fyn_foo); + UNIT_ASSERT_UNEQUAL(rc, 0); + + /* get the foo: bar node pair */ + fynp = fy_node_mapping_lookup_pair(fyn_map, fyn_foo); + UNIT_ASSERT_UNEQUAL(fynp, NULL); + UNIT_ASSERT_EQUAL(fy_node_pair_key(fynp), fyn_foo2); + UNIT_ASSERT_EQUAL(fy_node_pair_value(fynp), fyn_bar); + + /* we're setting the same key to the mapping, but that's OK + * since the key is replaced */ + rc = fy_node_pair_set_key(fynp, fyn_foo); + UNIT_ASSERT_EQUAL(rc, 0); + + /* fyn_foo has been freed */ + fyn_foo = NULL; + + /* convert to string */ + rc = fy_emit_document_to_fp(fyd, FYECF_MODE_FLOW_ONELINE, stderr); + UNIT_ASSERT_EQUAL(rc, 0); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(manual_scalar_esc) { + // FIXME removed \e because it works incorrectly in MS C + const std::span MANUAL_SCALAR_ESC = "\\\"\0\a\b\t\v\f\r\xc2\x85\xc2\xa0\xe2\x80\xa8\xe2\x80\xa9"; + const TStringBuf MANUAL_SCALAR_ESC_TXT = "\"\\\\\\\"\\0\\a\\b\\t\\v\\f\\r\\N\\_\\L\\P\""; + const char *what = MANUAL_SCALAR_ESC.data(); + size_t what_sz = MANUAL_SCALAR_ESC.size() - 1; + struct fy_document *fyd; + struct fy_node *fyn; + char *buf; + const char *buf2; + size_t sz2; + int rc; + + /* build document */ + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* create a manual scalar with all the escapes */ + fyn = fy_node_create_scalar(fyd, what, what_sz); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + fyn = NULL; + + /* emit to a buffer */ + buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* destroy the old document */ + fy_document_destroy(fyd); + fyd = NULL; + + /* verify that the resulting document is in the escaped form */ + UNIT_ASSERT_VALUES_EQUAL(buf, TString(MANUAL_SCALAR_ESC_TXT) + "\n"); + + /* now load the result back and verify that it contains exactly the same */ + fyd = fy_document_build_from_string(NULL, buf, FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* get the scalar content */ + buf2 = fy_node_get_scalar(fy_document_root(fyd), &sz2); + UNIT_ASSERT_UNEQUAL(buf2, NULL); + + /* sizes must match */ + UNIT_ASSERT_EQUAL(what_sz, sz2); + + /* and the strings too */ + rc = memcmp(what, buf2, what_sz); + UNIT_ASSERT_EQUAL(rc, 0); + + /* free the document */ + fy_document_destroy(fyd); + fyd = NULL; + + free(buf); +} + +Y_UNIT_TEST(manual_scalar_quoted) { + const TStringBuf MANUAL_SCALAR_QUOTED = "&foo"; + const TStringBuf MANUAL_SCALAR_QUOTED_TXT = "\"&foo\""; + const char *what = MANUAL_SCALAR_QUOTED.data(); + size_t what_sz = MANUAL_SCALAR_QUOTED.length(); + struct fy_document *fyd; + struct fy_node *fyn; + char *buf; + const char *buf2; + size_t sz2; + int rc; + + /* build document */ + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* create a manual scalar with all the escapes */ + fyn = fy_node_create_scalar(fyd, what, what_sz); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + fyn = NULL; + + /* emit to a buffer */ + buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* destroy the old document */ + fy_document_destroy(fyd); + fyd = NULL; + + /* verify that the resulting document is in the escaped form */ + UNIT_ASSERT_VALUES_EQUAL(buf, TString(MANUAL_SCALAR_QUOTED_TXT) + "\n"); + + /* now load the result back and verify that it contains exactly the same */ + fyd = fy_document_build_from_string(NULL, buf, FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* get the scalar content */ + buf2 = fy_node_get_scalar(fy_document_root(fyd), &sz2); + UNIT_ASSERT_UNEQUAL(buf2, NULL); + + /* sizes must match */ + UNIT_ASSERT_EQUAL(what_sz, sz2); + + /* and the strings too */ + rc = memcmp(what, buf2, what_sz); + UNIT_ASSERT_EQUAL(rc, 0); + + /* free the document */ + fy_document_destroy(fyd); + fyd = NULL; + + free(buf); +} + +Y_UNIT_TEST(manual_scalar_copy) { + const TStringBuf MANUAL_SCALAR_COPY = "foo"; + const char *what = MANUAL_SCALAR_COPY.data(); + size_t what_sz = MANUAL_SCALAR_COPY.length(); + char *what_copy; + struct fy_document *fyd; + struct fy_node *fyn; + char *buf; + + /* build document */ + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + what_copy = (char*)malloc(what_sz); + UNIT_ASSERT_UNEQUAL(what_copy, NULL); + memcpy(what_copy, what, what_sz); + + /* create a manual scalar with all the escapes */ + fyn = fy_node_create_scalar_copy(fyd, what_copy, what_sz); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + /* free the data */ + free(what_copy); + + fy_document_set_root(fyd, fyn); + fyn = NULL; + + /* emit to a buffer */ + buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* verify that the resulting document is the one we used + '\n' */ + UNIT_ASSERT_VALUES_EQUAL(buf, TString(MANUAL_SCALAR_COPY) + "\n"); + + /* destroy the old document */ + fy_document_destroy(fyd); + fyd = NULL; + + free(buf); + +} + +Y_UNIT_TEST(manual_scalarf) { + struct fy_document *fyd; + struct fy_node *fyn; + char *buf; + + /* build document */ + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* create a manual scalar using the printf interface */ + fyn = fy_node_create_scalarf(fyd, "foo%d", 13); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + fyn = NULL; + + /* emit to a buffer */ + buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* verify that the resulting document is the one we used + '\n' */ + UNIT_ASSERT_VALUES_EQUAL(buf, TString("foo13\n")); + + /* destroy the old document */ + fy_document_destroy(fyd); + fyd = NULL; + + free(buf); +} + +Y_UNIT_TEST(manual_valid_anchor) { + struct fy_document *fyd; + struct fy_node *fyn; + int ret; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn = fy_node_create_sequence(fyd); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + + fyn = fy_node_create_scalar(fyd, "foo", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + ret = fy_node_sequence_append(fy_document_root(fyd), fyn); + UNIT_ASSERT_EQUAL(ret, 0); + + /* create a valid anchor */ + ret = fy_node_set_anchor(fyn, "foo", FY_NT); + UNIT_ASSERT_EQUAL(ret, 0); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(manual_invalid_anchor) { + struct fy_document *fyd; + struct fy_node *fyn; + int ret; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn = fy_node_create_sequence(fyd); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + + fyn = fy_node_create_scalar(fyd, "foo", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + ret = fy_node_sequence_append(fy_document_root(fyd), fyn); + UNIT_ASSERT_EQUAL(ret, 0); + + /* create an invalid anchor */ + ret = fy_node_set_anchor(fyn, "*foo", FY_NT); + UNIT_ASSERT_UNEQUAL(ret, 0); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(manual_anchor_removal) { + struct fy_document *fyd; + struct fy_node *fyn; + int ret; + + fyd = fy_document_create(NULL); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn = fy_node_create_sequence(fyd); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + fy_document_set_root(fyd, fyn); + + fyn = fy_node_create_scalar(fyd, "foo", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn, NULL); + + ret = fy_node_sequence_append(fy_document_root(fyd), fyn); + UNIT_ASSERT_EQUAL(ret, 0); + + /* create a valid anchor */ + ret = fy_node_set_anchor(fyn, "foo", FY_NT); + UNIT_ASSERT_EQUAL(ret, 0); + + fprintf(stderr, "---\n# with anchor\n"); + fy_emit_document_to_fp(fyd, FYECF_MODE_FLOW_ONELINE, stderr); + + /* should fail (an anchor already exists) */ + ret = fy_node_set_anchor(fyn, "bar", FY_NT); + UNIT_ASSERT_UNEQUAL(ret, 0); + + /* should succeed */ + ret = fy_node_remove_anchor(fyn); + UNIT_ASSERT_EQUAL(ret, 0); + + fprintf(stderr, "---\n# without anchor\n"); + fy_emit_document_to_fp(fyd, FYECF_MODE_FLOW_ONELINE, stderr); + + fy_document_destroy(fyd); +} + +Y_UNIT_TEST(manual_block_flow_mix) { + struct fy_document *fyd; + struct fy_node *fyn_mapping, *fyn_key, *fyn_value; + char *buf; + int ret; + + fyd = fy_document_build_from_string(NULL, "--- &root\n{\n}\n", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn_mapping = fy_document_root(fyd); + UNIT_ASSERT_UNEQUAL(fyn_mapping, NULL); + + UNIT_ASSERT(fy_node_is_mapping(fyn_mapping) == true); + + fyn_key = fy_node_create_scalar(fyd, "key", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn_key, NULL); + + fyn_value = fy_node_build_from_string(fyd, "|\n literal\n", FY_NT); + UNIT_ASSERT_UNEQUAL(fyn_value, NULL); + + ret = fy_node_mapping_append(fyn_mapping, fyn_key, fyn_value); + UNIT_ASSERT_EQUAL(ret, 0); + + /* emit document to buffer */ + buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + + /* destroy the first document */ + fy_document_destroy(fyd); + fyd = NULL; + + /* read the emitted document back */ + fyd = fy_document_build_from_string(NULL, buf, FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* compare with expected result */ + UNIT_ASSERT_VALUES_EQUAL(fy_node_get_scalar0(fy_node_by_path(fy_document_root(fyd), "/key", FY_NT, FYNWF_DONT_FOLLOW)), "literal\n"); + + /* destroy the second document */ + fy_document_destroy(fyd); + fyd = NULL; + + free(buf); + +} + +/* FIXME + * This test is disabled because we can't make compatible + * alloca-based API on windows because original library + * uses gcc inline block extensions it will be uncommented + * when new API will be introduced + */ +#if false +Y_UNIT_TEST(alloca_check) { + struct fy_document *fyd; + char *buf; + const char *abuf; + + /* build document */ + fyd = fy_document_build_from_string(NULL, "{ " + "foo: 10, bar : [ ten, 20 ], baz:{ frob: boo, deep: { deeper: yet } }, " + "}", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + /* fy_emit_document_to_string*() */ + buf = fy_emit_document_to_string(fyd, FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + abuf = fy_emit_document_to_string_alloca(fyd, FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(abuf, NULL); + UNIT_ASSERT_VALUES_EQUAL(buf, abuf); + free(buf); + + /* fy_emit_node_to_string*() */ + buf = fy_emit_node_to_string(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW), FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(buf, NULL); + abuf = fy_emit_node_to_string_alloca(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW), FYECF_MODE_FLOW_ONELINE); + UNIT_ASSERT_UNEQUAL(abuf, NULL); + UNIT_ASSERT_VALUES_EQUAL(buf, abuf); + free(buf); + + /* path check eq */ + buf = fy_node_get_path(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW)); + UNIT_ASSERT_UNEQUAL(buf, NULL); + UNIT_ASSERT_VALUES_EQUAL(buf, "/foo"); + abuf = fy_node_get_path_alloca(fy_node_by_path(fy_document_root(fyd), "/foo", FY_NT, FYNWF_DONT_FOLLOW)); + UNIT_ASSERT_UNEQUAL(abuf, NULL); + UNIT_ASSERT_VALUES_EQUAL(abuf, "/foo"); + UNIT_ASSERT_VALUES_EQUAL(buf, abuf); + free(buf); + + /* check that a bad path is "" */ + abuf = fy_node_get_path_alloca(NULL); + UNIT_ASSERT_UNEQUAL(abuf, NULL); + UNIT_ASSERT_VALUES_EQUAL(abuf, ""); + + fy_document_destroy(fyd); + +} +#endif + +Y_UNIT_TEST(scanf_check) { + struct fy_document *fyd; + struct fy_node *fyn_root; + int ret, ival; + char sval[256]; + + /* build document */ + fyd = fy_document_build_from_string(NULL, "{ " + "foo: 10, bar : 20, baz:{ frob: boo }, " + "frooz: [ 1, { key: value }, three ]" + "}", FY_NT); + UNIT_ASSERT_UNEQUAL(fyd, NULL); + + fyn_root = fy_document_root(fyd); + UNIT_ASSERT_UNEQUAL(fyn_root, NULL); + + /* check scanf accesses to scalars */ + ret = fy_node_scanf(fyn_root, "/foo %d", &ival); + UNIT_ASSERT_EQUAL(ret, 1); + UNIT_ASSERT_EQUAL(ival, 10); + + ret = fy_node_scanf(fyn_root, "/bar %d", &ival); + UNIT_ASSERT_EQUAL(ret, 1); + UNIT_ASSERT_EQUAL(ival, 20); + + ret = fy_node_scanf(fyn_root, "/baz/frob %s", sval); + UNIT_ASSERT_EQUAL(ret, 1); + UNIT_ASSERT_VALUES_EQUAL(sval, "boo"); + + ret = fy_node_scanf(fyn_root, "/frooz/0 %d", &ival); + UNIT_ASSERT_EQUAL(ret, 1); + UNIT_ASSERT_EQUAL(ival, 1); + + ret = fy_node_scanf(fyn_root, "/frooz/1/key %s", sval); + UNIT_ASSERT_EQUAL(ret, 1); + UNIT_ASSERT_VALUES_EQUAL(sval, "value"); + + ret = fy_node_scanf(fyn_root, "/frooz/2 %s", sval); + UNIT_ASSERT_EQUAL(ret, 1); + UNIT_ASSERT_VALUES_EQUAL(sval, "three"); + + fy_document_destroy(fyd); +} + +} |