Blob Blame History Raw
From ad2bfa136290e72cdfd4b7877b49b3fc07203f9c Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Tue, 21 Feb 2023 16:26:22 +0800
Subject: [PATCH] clib: Introduce YAML support

Allowing both YAML and JSON input, the output format will matching input
format.

For `nmstate_net_state_retrieve()`, user can use
`NMSTATE_FLAG_YAML_OUTPUT` flag to instruct the output to be YAML
format.

Signed-off-by: Gris Ge <fge@redhat.com>
---
 rust/src/clib/Cargo.toml                      |  1 +
 rust/src/clib/apply.rs                        |  2 +-
 rust/src/clib/gen_conf.rs                     | 52 +++++++-----
 rust/src/clib/nmstate.h.in                    | 55 +++++++------
 rust/src/clib/policy.rs                       | 57 ++++++++-----
 rust/src/clib/query.rs                        | 49 ++++++++----
 .../{nmpolicy_test.c => nmpolicy_json_test.c} |  5 ++
 rust/src/clib/test/nmpolicy_yaml_test.c       | 80 +++++++++++++++++++
 .../{nmstate_test.c => nmstate_json_test.c}   |  5 ++
 rust/src/clib/test/nmstate_yaml_test.c        | 34 ++++++++
 rust/src/lib/Cargo.toml                       |  3 +
 rust/src/lib/net_state.rs                     | 14 +++-
 12 files changed, 274 insertions(+), 83 deletions(-)
 rename rust/src/clib/test/{nmpolicy_test.c => nmpolicy_json_test.c} (96%)
 create mode 100644 rust/src/clib/test/nmpolicy_yaml_test.c
 rename rust/src/clib/test/{nmstate_test.c => nmstate_json_test.c} (87%)
 create mode 100644 rust/src/clib/test/nmstate_yaml_test.c

diff --git a/rust/src/clib/Cargo.toml b/rust/src/clib/Cargo.toml
index 97e4128c..ed391b3a 100644
--- a/rust/src/clib/Cargo.toml
+++ b/rust/src/clib/Cargo.toml
@@ -16,6 +16,7 @@ crate-type = ["cdylib", "staticlib"]
 nmstate = { path = "../lib", default-features = false }
 libc = "0.2.74"
 serde_json = "1.0"
+serde_yaml = "0.9"
 log = "0.4.17"
 serde = { version = "1.0.137", features = ["derive"] }
 once_cell = "1.12.0"
diff --git a/rust/src/clib/apply.rs b/rust/src/clib/apply.rs
index 9a0d6fbc..67d39730 100644
--- a/rust/src/clib/apply.rs
+++ b/rust/src/clib/apply.rs
@@ -74,7 +74,7 @@ pub extern "C" fn nmstate_net_state_apply(
     };
 
     let mut net_state =
-        match nmstate::NetworkState::new_from_json(net_state_str) {
+        match nmstate::NetworkState::new_from_yaml(net_state_str) {
             Ok(n) => n,
             Err(e) => {
                 unsafe {
diff --git a/rust/src/clib/gen_conf.rs b/rust/src/clib/gen_conf.rs
index f63fb7b0..1ad7156b 100644
--- a/rust/src/clib/gen_conf.rs
+++ b/rust/src/clib/gen_conf.rs
@@ -68,7 +68,7 @@ pub extern "C" fn nmstate_generate_configurations(
         }
     };
 
-    let net_state = match nmstate::NetworkState::new_from_json(net_state_str) {
+    let net_state = match nmstate::NetworkState::new_from_yaml(net_state_str) {
         Ok(n) => n,
         Err(e) => {
             unsafe {
@@ -80,28 +80,44 @@ pub extern "C" fn nmstate_generate_configurations(
         }
     };
 
+    let input_is_json =
+        serde_json::from_str::<serde_json::Value>(net_state_str).is_ok();
     let result = net_state.gen_conf();
     unsafe {
         *log = CString::new(logger.drain(now)).unwrap().into_raw();
     }
     match result {
-        Ok(s) => match serde_json::to_string(&s) {
-            Ok(cfgs) => unsafe {
-                *configs = CString::new(cfgs).unwrap().into_raw();
-                NMSTATE_PASS
-            },
-            Err(e) => unsafe {
-                *err_msg =
-                    CString::new(format!("serde_json::to_string failure: {e}"))
-                        .unwrap()
-                        .into_raw();
-                *err_kind =
-                    CString::new(format!("{}", nmstate::ErrorKind::Bug))
-                        .unwrap()
-                        .into_raw();
-                NMSTATE_FAIL
-            },
-        },
+        Ok(s) => {
+            let serialize = if input_is_json {
+                serde_json::to_string(&s).map_err(|e| {
+                    nmstate::NmstateError::new(
+                        nmstate::ErrorKind::Bug,
+                        format!("Failed to convert state {s:?} to JSON: {e}"),
+                    )
+                })
+            } else {
+                serde_yaml::to_string(&s).map_err(|e| {
+                    nmstate::NmstateError::new(
+                        nmstate::ErrorKind::Bug,
+                        format!("Failed to convert state {s:?} to YAML: {e}"),
+                    )
+                })
+            };
+
+            match serialize {
+                Ok(cfgs) => unsafe {
+                    *configs = CString::new(cfgs).unwrap().into_raw();
+                    NMSTATE_PASS
+                },
+                Err(e) => unsafe {
+                    *err_msg =
+                        CString::new(e.msg().to_string()).unwrap().into_raw();
+                    *err_kind =
+                        CString::new(e.kind().to_string()).unwrap().into_raw();
+                    NMSTATE_FAIL
+                },
+            }
+        }
         Err(e) => {
             unsafe {
                 *err_msg = CString::new(e.msg()).unwrap().into_raw();
diff --git a/rust/src/clib/nmstate.h.in b/rust/src/clib/nmstate.h.in
index 0879d47e..391477fd 100644
--- a/rust/src/clib/nmstate.h.in
+++ b/rust/src/clib/nmstate.h.in
@@ -1,19 +1,4 @@
-/*
- * Copyright 2021 Red Hat
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
+// SPDX-License-Identifier: Apache-2.0
 
 #ifndef _LIBNMSTATE_H_
 #define _LIBNMSTATE_H_
@@ -44,6 +29,7 @@ extern "C" {
 #define NMSTATE_FLAG_NO_COMMIT              1 << 5
 #define NMSTATE_FLAG_MEMORY_ONLY            1 << 6
 #define NMSTATE_FLAG_RUNNING_CONFIG_ONLY    1 << 7
+#define NMSTATE_FLAG_YAML_OUTPUT            1 << 8
 
 /**
  * nmstate_net_state_retrieve - Retrieve network state
@@ -52,7 +38,7 @@ extern "C" {
  *      0.1
  *
  * Description:
- *      Retrieve network state in the format of JSON.
+ *      Retrieve network state in the format of JSON or YAML.
  *
  * @flags:
  *      Flags for special use cases:
@@ -60,6 +46,13 @@ extern "C" {
  *              No flag
  *          * NMSTATE_FLAG_KERNEL_ONLY
  *              Do not use external plugins, show kernel status only.
+ *          * NMSTATE_FLAG_INCLUDE_SECRETS
+ *              No not hide sercerts like password.
+ *          * NMSTATE_FLAG_RUNNING_CONFIG_ONLY
+ *              Only include running config excluding running status like auto
+ *              IP addresses and routes, LLDP neighbors.
+ *          * NMSTATE_FLAG_YAML_OUTPUT
+ *              Show the state in YAML format
  * @state:
  *      Output pointer of char array for network state in json format.
  *      The memory should be freed by nmstate_net_state_free().
@@ -90,7 +83,7 @@ int nmstate_net_state_retrieve(uint32_t flags, char **state, char **log,
  *      0.1
  *
  * Description:
- *      Apply network state in the format of JSON.
+ *      Apply network state in the format of JSON or YAML.
  *
  * @flags:
  *      Flags for special use cases:
@@ -98,8 +91,12 @@ int nmstate_net_state_retrieve(uint32_t flags, char **state, char **log,
  *              No flag
  *          * NMSTATE_FLAG_KERNEL_ONLY
  *              Do not use external plugins, apply to kernel only.
+ *          * NMSTATE_FLAG_NO_VERIFY
+ *              Do not verify state after applied
  *          * NMSTATE_FLAG_NO_COMMIT
  *              Do not commit new state after verification
+ *          * NMSTATE_FLAG_MEMORY_ONLY
+ *              No not store network state to persistent.
  * @state:
  *      Pointer of char array for network state in json format.
  * @log:
@@ -119,7 +116,8 @@ int nmstate_net_state_retrieve(uint32_t flags, char **state, char **log,
  *          * NMSTATE_FAIL
  *              On failure.
  */
-int nmstate_net_state_apply(uint32_t flags, const char *state, uint32_t rollback_timeout, char **log,
+int nmstate_net_state_apply(uint32_t flags, const char *state,
+                            uint32_t rollback_timeout, char **log,
                             char **err_kind, char **err_msg);
 
 /**
@@ -151,8 +149,8 @@ int nmstate_net_state_apply(uint32_t flags, const char *state, uint32_t rollback
  *          * NMSTATE_FAIL
  *              On failure.
  */
-int nmstate_checkpoint_commit(const char *checkpoint, char **log, char **err_kind,
-                          char **err_msg);
+int nmstate_checkpoint_commit(const char *checkpoint, char **log, 
+                              char **err_kind, char **err_msg);
 
 /**
  * nmstate_checkpoint_rollback - Rollback the checkpoint
@@ -183,8 +181,8 @@ int nmstate_checkpoint_commit(const char *checkpoint, char **log, char **err_kin
  *          * NMSTATE_FAIL
  *              On failure.
  */
-int nmstate_checkpoint_rollback(const char *checkpoint, char **log, char **err_kind,
-                                 char **err_msg);
+int nmstate_checkpoint_rollback(const char *checkpoint, char **log, 
+                                char **err_kind, char **err_msg);
 
 /**
  * nmstate_generate_configurations - Generate network configurations
@@ -199,9 +197,10 @@ int nmstate_checkpoint_rollback(const char *checkpoint, char **log, char **err_k
  *      as value.
  *
  * @state:
- *      Pointer of char array for network state in json format.
+ *      Pointer of char array for network state in JSON or YAML format.
  * @configs:
- *      Output pointer of char array for network configures in json format.
+ *      Output pointer of char array for network configures in JSON or
+ *      YAML(depend on which format you use in @state) format.
  *      The memory should be freed by nmstate_net_state_free().
  * @log:
  *      Output pointer of char array for logging.
@@ -231,14 +230,14 @@ int nmstate_generate_configurations(const char *state, char **configs,
  *      2.2
  *
  * Description:
- *      Generate new network state from policy again specifed state
+ *      Generate new network state from policy again specified state
  *
  * @policy:
- *      Pointer of char array for network policy in json format.
+ *      Pointer of char array for network policy in JSON/YAML format.
  * @current_state:
  *      Pointer of char array for current network state.
  * @state:
- *      Output pointer of char array for network state in json format.
+ *      Output pointer of char array for network state in JSON/YAML format.
  *      The memory should be freed by nmstate_net_state_free().
  * @log:
  *      Output pointer of char array for logging.
diff --git a/rust/src/clib/policy.rs b/rust/src/clib/policy.rs
index ec8c46c1..ea7dd036 100644
--- a/rust/src/clib/policy.rs
+++ b/rust/src/clib/policy.rs
@@ -67,6 +67,13 @@ pub extern "C" fn nmstate_net_state_from_policy(
         }
     };
 
+    let input_is_json =
+        if let Ok(policy_str) = unsafe { CStr::from_ptr(policy) }.to_str() {
+            serde_json::from_str::<serde_json::Value>(policy_str).is_ok()
+        } else {
+            false
+        };
+
     let mut policy = match deserilize_from_c_char::<NetworkPolicy>(
         policy, err_kind, err_msg,
     ) {
@@ -86,23 +93,37 @@ pub extern "C" fn nmstate_net_state_from_policy(
     }
 
     match result {
-        Ok(s) => match serde_json::to_string(&s) {
-            Ok(state_str) => unsafe {
-                *state = CString::new(state_str).unwrap().into_raw();
-                NMSTATE_PASS
-            },
-            Err(e) => unsafe {
-                *err_msg =
-                    CString::new(format!("serde_json::to_string failure: {e}"))
-                        .unwrap()
-                        .into_raw();
-                *err_kind =
-                    CString::new(format!("{}", nmstate::ErrorKind::Bug))
-                        .unwrap()
-                        .into_raw();
-                NMSTATE_FAIL
-            },
-        },
+        Ok(s) => {
+            let serialize = if input_is_json {
+                serde_json::to_string(&s).map_err(|e| {
+                    nmstate::NmstateError::new(
+                        nmstate::ErrorKind::Bug,
+                        format!("Failed to convert state {s:?} to JSON: {e}"),
+                    )
+                })
+            } else {
+                serde_yaml::to_string(&s).map_err(|e| {
+                    nmstate::NmstateError::new(
+                        nmstate::ErrorKind::Bug,
+                        format!("Failed to convert state {s:?} to YAML: {e}"),
+                    )
+                })
+            };
+
+            match serialize {
+                Ok(state_str) => unsafe {
+                    *state = CString::new(state_str).unwrap().into_raw();
+                    NMSTATE_PASS
+                },
+                Err(e) => unsafe {
+                    *err_msg =
+                        CString::new(e.msg().to_string()).unwrap().into_raw();
+                    *err_kind =
+                        CString::new(e.kind().to_string()).unwrap().into_raw();
+                    NMSTATE_FAIL
+                },
+            }
+        }
         Err(e) => {
             unsafe {
                 *err_msg = CString::new(e.msg()).unwrap().into_raw();
@@ -144,7 +165,7 @@ where
         }
     };
 
-    match serde_json::from_str(content_str) {
+    match serde_yaml::from_str(content_str) {
         Ok(n) => Some(n),
         Err(e) => {
             unsafe {
diff --git a/rust/src/clib/query.rs b/rust/src/clib/query.rs
index a24b9c83..12e44d05 100644
--- a/rust/src/clib/query.rs
+++ b/rust/src/clib/query.rs
@@ -14,6 +14,7 @@ pub(crate) const NMSTATE_FLAG_INCLUDE_SECRETS: u32 = 1 << 4;
 pub(crate) const NMSTATE_FLAG_NO_COMMIT: u32 = 1 << 5;
 pub(crate) const NMSTATE_FLAG_MEMORY_ONLY: u32 = 1 << 6;
 pub(crate) const NMSTATE_FLAG_RUNNING_CONFIG_ONLY: u32 = 1 << 7;
+pub(crate) const NMSTATE_FLAG_YAML_OUTPUT: u32 = 1 << 8;
 
 #[allow(clippy::not_unsafe_ptr_arg_deref)]
 #[no_mangle]
@@ -72,23 +73,37 @@ pub extern "C" fn nmstate_net_state_retrieve(
     }
 
     match result {
-        Ok(s) => match serde_json::to_string(&s) {
-            Ok(state_str) => unsafe {
-                *state = CString::new(state_str).unwrap().into_raw();
-                NMSTATE_PASS
-            },
-            Err(e) => unsafe {
-                *err_msg =
-                    CString::new(format!("serde_json::to_string failure: {e}"))
-                        .unwrap()
-                        .into_raw();
-                *err_kind =
-                    CString::new(format!("{}", nmstate::ErrorKind::Bug))
-                        .unwrap()
-                        .into_raw();
-                NMSTATE_FAIL
-            },
-        },
+        Ok(s) => {
+            let serialize = if (flags & NMSTATE_FLAG_YAML_OUTPUT) > 0 {
+                serde_yaml::to_string(&s).map_err(|e| {
+                    nmstate::NmstateError::new(
+                        nmstate::ErrorKind::Bug,
+                        format!("Failed to convert state {s:?} to YAML: {e}"),
+                    )
+                })
+            } else {
+                serde_json::to_string(&s).map_err(|e| {
+                    nmstate::NmstateError::new(
+                        nmstate::ErrorKind::Bug,
+                        format!("Failed to convert state {s:?} to JSON: {e}"),
+                    )
+                })
+            };
+
+            match serialize {
+                Ok(state_str) => unsafe {
+                    *state = CString::new(state_str).unwrap().into_raw();
+                    NMSTATE_PASS
+                },
+                Err(e) => unsafe {
+                    *err_msg =
+                        CString::new(e.msg().to_string()).unwrap().into_raw();
+                    *err_kind =
+                        CString::new(e.kind().to_string()).unwrap().into_raw();
+                    NMSTATE_FAIL
+                },
+            }
+        }
         Err(e) => {
             unsafe {
                 *err_msg = CString::new(e.msg()).unwrap().into_raw();
diff --git a/rust/src/clib/test/nmpolicy_test.c b/rust/src/clib/test/nmpolicy_json_test.c
similarity index 96%
rename from rust/src/clib/test/nmpolicy_test.c
rename to rust/src/clib/test/nmpolicy_json_test.c
index 7a71a5f5..8a0444d4 100644
--- a/rust/src/clib/test/nmpolicy_test.c
+++ b/rust/src/clib/test/nmpolicy_json_test.c
@@ -1,3 +1,6 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <assert.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -91,6 +94,8 @@ int main(void) {
 		rc = EXIT_FAILURE;
 	}
 
+	assert(state[0] == '{');
+
 	nmstate_cstring_free(state);
 	nmstate_cstring_free(err_kind);
 	nmstate_cstring_free(err_msg);
diff --git a/rust/src/clib/test/nmpolicy_yaml_test.c b/rust/src/clib/test/nmpolicy_yaml_test.c
new file mode 100644
index 00000000..7984f509
--- /dev/null
+++ b/rust/src/clib/test/nmpolicy_yaml_test.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nmstate.h>
+
+int main(void) {
+	int rc = EXIT_SUCCESS;
+	const char *policy = "\
+capture:\n\
+  default-gw: override me with the cache\n\
+  base-iface: >\n\
+    interfaces.name == capture.default-gw.routes.running.0.next-hop-interface\n\
+  base-iface-routes: >\n\
+    routes.running.next-hop-interface ==\n\
+    capture.default-gw.routes.running.0.next-hop-interface\n\
+  bridge-routes: >\n\
+    capture.base-iface-routes | routes.running.next-hop-interface:=\"br1\"\n\
+desired:\n\
+  interfaces:\n\
+  - name: br1\n\
+    description: Linux bridge with base interface as a port\n\
+    type: linux-bridge\n\
+    state: up\n\
+    bridge:\n\
+      options:\n\
+        stp:\n\
+          enabled: false\n\
+      port:\n\
+      - name: '{{ capture.base-iface.interfaces.0.name }}'\n\
+    ipv4: '{{ capture.base-iface.interfaces.0.ipv4 }}'\n\
+  routes:\n\
+    config: '{{ capture.bridge-routes.routes.running }}'";
+	const char *current_state = "\
+interfaces:\n\
+- name: eth1\n\
+  type: ethernet\n\
+  state: up\n\
+  mac-address: 1c:c1:0c:32:3b:ff\n\
+  ipv4:\n\
+    address:\n\
+    - ip: 192.0.2.251\n\
+      prefix-length: 24\n\
+    dhcp: false\n\
+    enabled: true\n\
+routes:\n\
+  config:\n\
+  - destination: 0.0.0.0/0\n\
+    next-hop-address: 192.0.2.1\n\
+    next-hop-interface: eth1\n\
+  running:\n\
+  - destination: 0.0.0.0/0\n\
+    next-hop-address: 192.0.2.1\n\
+    next-hop-interface: eth1";
+	char *state = NULL;
+	char *err_kind = NULL;
+	char *err_msg = NULL;
+	char *log = NULL;
+
+	if (nmstate_net_state_from_policy(policy, current_state, &state, &log,
+					  &err_kind, &err_msg) == NMSTATE_PASS)
+	{
+		printf("%s\n", state);
+	} else {
+		printf("%s: %s\n", err_kind, err_msg);
+		rc = EXIT_FAILURE;
+	}
+
+	assert(state[0] != '{');
+
+	nmstate_cstring_free(state);
+	nmstate_cstring_free(err_kind);
+	nmstate_cstring_free(err_msg);
+	nmstate_cstring_free(log);
+	exit(rc);
+}
diff --git a/rust/src/clib/test/nmstate_test.c b/rust/src/clib/test/nmstate_json_test.c
similarity index 87%
rename from rust/src/clib/test/nmstate_test.c
rename to rust/src/clib/test/nmstate_json_test.c
index 0e79cb15..1bfbcda7 100644
--- a/rust/src/clib/test/nmstate_test.c
+++ b/rust/src/clib/test/nmstate_json_test.c
@@ -1,3 +1,6 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <assert.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -21,6 +24,8 @@ int main(void) {
 		rc = EXIT_FAILURE;
 	}
 
+	assert(state[0] == '{');
+
 	nmstate_cstring_free(state);
 	nmstate_cstring_free(err_kind);
 	nmstate_cstring_free(err_msg);
diff --git a/rust/src/clib/test/nmstate_yaml_test.c b/rust/src/clib/test/nmstate_yaml_test.c
new file mode 100644
index 00000000..de0f2486
--- /dev/null
+++ b/rust/src/clib/test/nmstate_yaml_test.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: Apache-2.0
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nmstate.h>
+
+int main(void) {
+	int rc = EXIT_SUCCESS;
+	char *state = NULL;
+	char *err_kind = NULL;
+	char *err_msg = NULL;
+	char *log = NULL;
+	uint32_t flag = NMSTATE_FLAG_KERNEL_ONLY | NMSTATE_FLAG_YAML_OUTPUT;
+
+	if (nmstate_net_state_retrieve(flag, &state, &log, &err_kind, &err_msg)
+	    == NMSTATE_PASS) {
+		printf("%s\n", state);
+	} else {
+		printf("%s: %s\n", err_kind, err_msg);
+		rc = EXIT_FAILURE;
+	}
+
+	assert(state[0] != '{');
+
+	nmstate_cstring_free(state);
+	nmstate_cstring_free(err_kind);
+	nmstate_cstring_free(err_msg);
+	nmstate_cstring_free(log);
+	exit(rc);
+}
diff --git a/rust/src/lib/Cargo.toml b/rust/src/lib/Cargo.toml
index a27d0e1a..0142026d 100644
--- a/rust/src/lib/Cargo.toml
+++ b/rust/src/lib/Cargo.toml
@@ -15,6 +15,9 @@ edition = "2018"
 [lib]
 path = "lib.rs"
 
+[dependencies]
+serde_yaml = "0.9"
+
 [dependencies.nispor]
 version = "1.2.9"
 optional = true
diff --git a/rust/src/lib/net_state.rs b/rust/src/lib/net_state.rs
index 8ab79642..fe5fea78 100644
--- a/rust/src/lib/net_state.rs
+++ b/rust/src/lib/net_state.rs
@@ -274,7 +274,19 @@ impl NetworkState {
             Ok(s) => Ok(s),
             Err(e) => Err(NmstateError::new(
                 ErrorKind::InvalidArgument,
-                format!("Invalid json string: {e}"),
+                format!("Invalid JSON string: {e}"),
+            )),
+        }
+    }
+
+    /// Wrapping function of [serde_yaml::from_str()] with error mapped to
+    /// [NmstateError].
+    pub fn new_from_yaml(net_state_yaml: &str) -> Result<Self, NmstateError> {
+        match serde_yaml::from_str(net_state_yaml) {
+            Ok(s) => Ok(s),
+            Err(e) => Err(NmstateError::new(
+                ErrorKind::InvalidArgument,
+                format!("Invalid YAML string: {e}"),
             )),
         }
     }
-- 
2.39.2