schema tree FEATURE allow changing enabled features

At the cost of recompiling the whole context.
diff --git a/doc/transition.dox b/doc/transition.dox
index 63e93c8..fc15e17 100644
--- a/doc/transition.dox
+++ b/doc/transition.dox
@@ -138,7 +138,7 @@
  * This is an essential change allowing speed up and simplification of data validation, but requiring carefully determine
  * which format is more suitable for the specific use case. As mentioned, the compiled trees are better for data validation
  * and getting information about the intentioned structure of the schema with all the applied groupings, type modifications,
- * augments and deviations. On the other hand, the parsed trees are useful for the schema format conversions since they
+ * augments, deviations, and any defined if-features. On the other hand, the parsed trees are useful for the schema format conversions since they
  * provide the original structure of the modules. There is a number of new functions intended to work only with the
  * parsed or the compiled tree. These functions are prefixed with `lysp_` and `lysp_` prefixes.
  *
@@ -162,7 +162,7 @@
  * lys_set_private()            | ::lysc_set_private()            | Moving the private pointer only to the compiled tree.
  * lys_is_disabled()            | -                               | Make no sense since the nodes disabled by if-feature are not present in the compiled tree.
  * lys_features_list()          | -                               | Not needed, the list of features is available in the parsed tree of the module and submodule.
- * lys_features_enable(), lys_features_enable_force() | -         | Removed since the features are applied during the schema compilation and the list of enabled features is provided to the schema parser.
+ * lys_features_enable(), lys_features_enable_force() | ::lys_set_implemented()         | Set of enabled features can be changed but it causes the whole context (all the modules) to recompile.
  * lys_features_disable(), lys_features_disable_force() | -       | ^
  * lys_features_state()         | -                               | Redesign of the features handling in the schema tree, the feature's status is newly directly visible as ::LYS_FENABLED flag (in parsed feature structure).
  * -                            | ::lys_feature_value()           | Simplified API to get feature's state only based on a feature name string.
diff --git a/src/schema_features.c b/src/schema_features.c
index 8cd92a4..303ee51 100644
--- a/src/schema_features.c
+++ b/src/schema_features.c
@@ -472,12 +472,48 @@
     return LY_SUCCESS;
 }
 
+/**
+ * @brief Check whether all enabled features have their if-features satisfied.
+ * Enabled features with false if-features are disabled with a warning.
+ *
+ * @param[in] pmod Parsed module features to check.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_check_features(struct lysp_module *pmod)
+{
+    LY_ERR r;
+    uint32_t i = 0;
+    struct lysp_feature *f = NULL;
+
+    while ((f = lysp_feature_next(f, pmod, &i))) {
+        if (!(f->flags & LYS_FENABLED) || !f->iffeatures) {
+            /* disabled feature or no if-features to check */
+            continue;
+        }
+
+        assert(f->iffeatures_c);
+        r = lysc_iffeature_value(f->iffeatures_c);
+        if (r == LY_ENOT) {
+            LOGWRN(pmod->mod->ctx, "Feature \"%s\" cannot be enabled because its \"if-feature\" is not satisfied.",
+                    f->name);
+
+            /* disable feature and re-evaluate all the feature if-features again */
+            f->flags &= ~LYS_FENABLED;
+            return lys_check_features(pmod);
+        } else if (r) {
+            return r;
+        } /* else if-feature satisfied */
+    }
+
+    return LY_SUCCESS;
+}
+
 LY_ERR
 lys_enable_features(struct lysp_module *pmod, const char **features)
 {
-    uint32_t i;
-    struct lysp_feature *f;
-    LY_ERR ret;
+    uint32_t i = 0;
+    struct lysp_feature *f = 0;
 
     if (!features) {
         /* keep all features disabled */
@@ -486,8 +522,6 @@
 
     if (!strcmp(features[0], "*")) {
         /* enable all features */
-        f = NULL;
-        i = 0;
         while ((f = lysp_feature_next(f, pmod, &i))) {
             f->flags |= LYS_FENABLED;
         }
@@ -507,31 +541,61 @@
         }
     }
 
-check_iffeature:
-    /* check whether all enabled features have their if-features satisfied */
-    f = NULL;
-    i = 0;
-    while ((f = lysp_feature_next(f, pmod, &i))) {
-        if (!(f->flags & LYS_FENABLED) || !f->iffeatures) {
-            /* disabled feature or no if-features to check */
-            continue;
+    /* check final features if-feature state */
+    return lys_check_features(pmod);
+}
+
+LY_ERR
+lys_set_features(struct lysp_module *pmod, const char **features)
+{
+    uint32_t i = 0, j;
+    struct lysp_feature *f = 0;
+    ly_bool change = 0;
+
+    if (!features) {
+        /* disable all the features */
+        while ((f = lysp_feature_next(f, pmod, &i))) {
+            if (f->flags & LYS_FENABLED) {
+                f->flags &= ~LYS_FENABLED;
+                change = 1;
+            }
         }
+    } else if (!strcmp(features[0], "*")) {
+        /* enable all the features */
+        while ((f = lysp_feature_next(f, pmod, &i))) {
+            if (!(f->flags & LYS_FENABLED)) {
+                f->flags |= LYS_FENABLED;
+                change = 1;
+            }
+        }
+    } else {
+        /* enable specific features, disable the rest */
+        while ((f = lysp_feature_next(f, pmod, &i))) {
+            for (j = 0; features[j]; ++j) {
+                if (!strcmp(f->name, features[j])) {
+                    break;
+                }
+            }
 
-        assert(f->iffeatures_c);
-        ret = lysc_iffeature_value(f->iffeatures_c);
-        if (ret == LY_ENOT) {
-            LOGWRN(pmod->mod->ctx, "Feature \"%s\" cannot be enabled because its \"if-feature\" is not satisfied.",
-                    f->name);
-
-            /* disable feature and re-evaluate all the feature if-features again */
-            f->flags &= ~LYS_FENABLED;
-            goto check_iffeature;
-        } else if (ret) {
-            return ret;
-        } /* else if-feature satisfied */
+            if (features[j] && !(f->flags & LYS_FENABLED)) {
+                /* enable */
+                f->flags |= LYS_FENABLED;
+                change = 1;
+            } else if (!features[j] && (f->flags & LYS_FENABLED)) {
+                /* disable */
+                f->flags &= ~LYS_FENABLED;
+                change = 1;
+            }
+        }
     }
 
-    return LY_SUCCESS;
+    if (!change) {
+        /* features already set correctly */
+        return LY_EEXIST;
+    }
+
+    /* check final features if-feature state */
+    return lys_check_features(pmod);
 }
 
 /**
diff --git a/src/schema_features.h b/src/schema_features.h
index a785bd7..b63c810 100644
--- a/src/schema_features.h
+++ b/src/schema_features.h
@@ -37,13 +37,24 @@
  * can be enabled and have all their if-features true.
  *
  * @param[in] pmod Parsed module to modify.
- * @param[in] features Array of features ended with NULL to enable. NULL for all features disabled, '*' for all enabled
+ * @param[in] features Array of features ended with NULL to enable. NULL for all features disabled, '*' for all enabled.
  * @return LY_SUCCESS on success.
  * @return LY_ERR on error.
  */
 LY_ERR lys_enable_features(struct lysp_module *pmod, const char **features);
 
 /**
+ * @brief Set the specified features of a parsed module, with all the checks.
+ *
+ * @param[in] pmod Parsed module to modify.
+ * @param[in] features Array of features ended with NULL to set. NULL for all features disabled, '*' for all enabled.
+ * @return LY_SUCCESS on success.
+ * @return LY_EEXIST if the specified features were already set.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_set_features(struct lysp_module *pmod, const char **features);
+
+/**
  * @brief Compile if-features of features in the current module and all its submodules.
  *
  * @param[in] ctx Compile context.
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 398e3a1..3d56842 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -669,14 +669,25 @@
 API LY_ERR
 lys_set_implemented(struct lys_module *mod, const char **features)
 {
-    LY_ERR ret = LY_SUCCESS;
+    LY_ERR ret = LY_SUCCESS, r;
     struct lys_module *m;
     uint32_t i, idx;
 
     LY_CHECK_ARG_RET(NULL, mod, LY_EINVAL);
 
     if (mod->implemented) {
-        /* mod is already implemented */
+        /* mod is already implemented, set the features */
+        r = lys_set_features(mod->parsed, features);
+        if (r == LY_EEXIST) {
+            /* no changes */
+            return LY_SUCCESS;
+        } else if (r) {
+            /* error */
+            return r;
+        }
+
+        /* full recompilation */
+        lys_recompile(mod->ctx, NULL);
         return LY_SUCCESS;
     }
 
@@ -725,6 +736,7 @@
 
         ly_set_erase(&mod->ctx->implementing, NULL);
     }
+
     return ret;
 }
 
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 97c079c..e977f96 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -2149,8 +2149,9 @@
  *
  * @param[in] mod Module to make implemented. It is not an error
  * to provide already implemented module, it just does nothing.
- * @param[in] features Optional array of features ended with NULL to be enabled if the module is being implemented.
- * NULL for all features disabled and '*' for all enabled.
+ * @param[in] features Optional array of features ended with NULL to be enabled. NULL for all features disabled
+ * and '*' for all enabled. If the module is already implemented, these features are still correctly set and all
+ * the modules recompiled.
  * @return LY_SUCCESS on success.
  * @return LY_EDENIED in case the context contains some other revision of the same module which is already implemented.
  * @return LY_ERR on other errors during module compilation.