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.