yin parser CHANGE add support for min-elements element
diff --git a/src/parser_yin.c b/src/parser_yin.c
index a84eab1..3c3361b 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -918,6 +918,88 @@
 }
 
 /**
+ * @brief Parse max-elements element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] min Value to write to.
+ * @param[in,out] flags Flags to write to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_minelements(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, uint32_t *min,
+                      uint16_t *flags, struct lysp_ext_instance **exts)
+{
+    const char *temp_val = NULL;
+    char *ptr;
+    unsigned long int num;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0},
+                                        };
+
+    *flags |= LYS_SET_MIN;
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, YANG_MAX_ELEMENTS));
+
+    if (!temp_val || temp_val[0] == '\0' || (temp_val[0] == '0' && temp_val[1] != '\0')) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL, strlen(temp_val), temp_val, "min-elements");
+        FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+        return LY_EVALID;
+    }
+
+    errno = 0;
+    num = strtoul(temp_val, &ptr, 10);
+    if (ptr[0] != 0) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL, strlen(temp_val), temp_val, "min-elements");
+        FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+        return LY_EVALID;
+    }
+    if (errno == ERANGE || num > UINT32_MAX) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_OOB, strlen(temp_val), temp_val, "min-elements");
+        FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+        return LY_EVALID;
+    }
+    *min = num;
+    FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+    return yin_parse_content(ctx, subelems, 1, data, YANG_MAX_ELEMENTS, NULL, exts);
+}
+
+static LY_ERR
+yin_parse_minmax(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                 enum yang_keyword parent, enum yang_keyword current, void *dest)
+{
+    assert(current == YANG_MAX_ELEMENTS || current == YANG_MIN_ELEMENTS);
+    assert(parent == YANG_LEAF_LIST || parent == YANG_REFINE || parent == YANG_LIST);
+    uint32_t *lim;
+    uint16_t *flags;
+    struct lysp_ext_instance **exts;
+
+    if (parent == YANG_LEAF_LIST) {
+        lim = (current == YANG_MAX_ELEMENTS) ? &((struct lysp_node_leaflist *)dest)->max : &((struct lysp_node_leaflist *)dest)->min;
+        flags = &((struct lysp_node_leaflist *)dest)->flags;
+        exts = &((struct lysp_node_leaflist *)dest)->exts;
+    } else if (parent == YANG_REFINE) {
+        lim = (current == YANG_MAX_ELEMENTS) ? &((struct lysp_refine *)dest)->max : &((struct lysp_refine *)dest)->min;
+        flags = &((struct lysp_refine *)dest)->flags;
+        exts = &((struct lysp_refine *)dest)->exts;
+    } else {
+        lim = (current == YANG_MAX_ELEMENTS) ? &((struct lysp_node_list *)dest)->max : &((struct lysp_node_list *)dest)->min;
+        flags = &((struct lysp_node_list *)dest)->flags;
+        exts = &((struct lysp_node_list *)dest)->exts;
+    }
+
+    if (current == YANG_MAX_ELEMENTS) {
+        LY_CHECK_RET(yin_parse_maxelements(ctx, attrs, data, lim, flags, exts));
+    } else {
+        LY_CHECK_RET(yin_parse_minelements(ctx, attrs, data, lim, flags, exts));
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
  * @brief Map keyword type to substatement info.
  *
  * @param[in] kw Keyword type.
@@ -1034,6 +1116,7 @@
     enum yang_keyword kw = YANG_NONE;
     struct yin_subelement *subelem = NULL;
     struct lysp_type *type, *nested_type;
+
     assert(is_ordered(subelem_info, subelem_info_size));
 
     if (ctx->xml_ctx.status == LYXML_ELEM_CONTENT) {
@@ -1184,16 +1267,7 @@
                     break;
                 case YANG_MAX_ELEMENTS:
                 case YANG_MIN_ELEMENTS:
-                    if (current_element == YANG_LEAF_LIST) {
-                        ret = yin_parse_maxelements(ctx, attrs, data, &((struct lysp_node_leaflist *)subelem->dest)->max,
-                                                    &((struct lysp_node_leaflist *)subelem->dest)->flags, exts);
-                    } else if (current_element == YANG_REFINE) {
-                        ret = yin_parse_maxelements(ctx, attrs, data, &((struct lysp_refine *)subelem->dest)->max,
-                            &((struct lysp_refine *)subelem->dest)->flags, exts);
-                    } else {
-                        ret = yin_parse_maxelements(ctx, attrs, data, &((struct lysp_node_list *)subelem->dest)->max,
-                                                    &((struct lysp_node_list *)subelem->dest)->flags, exts);
-                    }
+                    ret = yin_parse_minmax(ctx, attrs, data, current_element, kw, subelem->dest);
                     break;
                 case YANG_MODIFIER:
                     ret = yin_parse_modifier(ctx, attrs, data, (const char **)subelem->dest, exts);
diff --git a/tests/src/test_parser_yin.c b/tests/src/test_parser_yin.c
index 0cc7aaa..59d1cd9 100644
--- a/tests/src/test_parser_yin.c
+++ b/tests/src/test_parser_yin.c
@@ -1777,6 +1777,49 @@
     st->finished_correctly = true;
 }
 
+static void
+test_min_elems_elem(void **state)
+{
+    struct state *st = *state;
+    const char *data;
+    struct lysp_node_list list = {};
+    struct lysp_node_leaflist llist = {};
+    struct lysp_refine refine = {};
+
+    data = "<refine xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"0\"/> </refine>";
+    assert_int_equal(test_element_helper(st, &data, &refine, NULL, NULL, true), LY_SUCCESS);
+    assert_int_equal(refine.min, 0);
+    assert_true(refine.flags | LYS_SET_MIN);
+
+    data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"41\"/> </list>";
+    assert_int_equal(test_element_helper(st, &data, &list, NULL, NULL, true), LY_SUCCESS);
+    assert_int_equal(list.min, 41);
+    assert_true(list.flags | LYS_SET_MIN);
+
+    data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"50\"/> </leaf-list>";
+    assert_int_equal(test_element_helper(st, &data, &llist, NULL, NULL, true), LY_SUCCESS);
+    assert_int_equal(llist.min, 50);
+    assert_true(llist.flags | LYS_SET_MIN);
+
+    data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"-5\"/> </leaf-list>";
+    assert_int_equal(test_element_helper(st, &data, &llist, NULL, NULL, false), LY_EVALID);
+    logbuf_assert("Value \"-5\" is out of \"min-elements\" bounds. Line number 1.");
+
+    data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"99999999999999999\"/> </leaf-list>";
+    assert_int_equal(test_element_helper(st, &data, &llist, NULL, NULL, false), LY_EVALID);
+    logbuf_assert("Value \"99999999999999999\" is out of \"min-elements\" bounds. Line number 1.");
+
+    data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"5k\"/> </leaf-list>";
+    assert_int_equal(test_element_helper(st, &data, &llist, NULL, NULL, false), LY_EVALID);
+    logbuf_assert("Invalid value \"5k\" of \"min-elements\". Line number 1.");
+
+    data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"05\"/> </leaf-list>";
+    assert_int_equal(test_element_helper(st, &data, &llist, NULL, NULL, false), LY_EVALID);
+    logbuf_assert("Invalid value \"05\" of \"min-elements\". Line number 1.");
+
+    st->finished_correctly = true;
+}
+
 int
 main(void)
 {
@@ -1823,6 +1866,7 @@
         cmocka_unit_test_setup_teardown(test_yin_text_value_elem, setup_element_test, teardown_element_test),
         cmocka_unit_test_setup_teardown(test_type_elem, setup_element_test, teardown_element_test),
         cmocka_unit_test_setup_teardown(test_max_elems_elem, setup_element_test, teardown_element_test),
+        cmocka_unit_test_setup_teardown(test_min_elems_elem, setup_element_test, teardown_element_test),
     };
 
     return cmocka_run_group_tests(tests, setup_ly_ctx, destroy_ly_ctx);