Repository: microsoft/regorus (language: python)

Issue:
[Lib] implement json.marshal_with_options builtin
Implement [json.marshal_with_options](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonmarshal_with_options).

Ensure that `cargo test -r --test opa -- jsonbuiltins` passes.


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index fc5ff7ce..83c51b7d 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 Regorus is also
   - *cross-platform* - Written in platform-agnostic Rust.
   - *current* - We strive to keep Regorus up to date with latest OPA release. Regorus supports `import rego.v1`.
-  - *compliant* - Regorus is mostly compliant with the latest [OPA release v0.63.0](https://github.com/open-policy-agent/opa/releases/tag/v0.63.0). See [OPA Conformance](#opa-conformance) for details. Note that while we behaviorally produce the same results, we don't yet support all the builtins.
+  - *compliant* - Regorus is mostly compliant with the latest [OPA release v0.64.0](https://github.com/open-policy-agent/opa/releases/tag/v0.64.0). See [OPA Conformance](#opa-conformance) for details. Note that while we behaviorally produce the same results, we don't yet support all the builtins.
   - *extensible* - Extend the Rego language by implementing custom stateful builtins in Rust.
     See [add_extension](https://github.com/microsoft/regorus/blob/fc68bf9c8bea36427dae9401a7d1f6ada771f7ab/src/engine.rs#L352).
     Support for extensibility using other languages coming soon.
@@ -69,7 +69,7 @@ $ cargo build -r --example regorus --features "yaml" --no-default-features; stri
 -rwxr-xr-x  1 anand  staff   2.9M Jan 19 11:26 target/release/examples/regorus*
 ```
 
-Regorus passes the [OPA v0.63.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few
+Regorus passes the [OPA v0.64.0 test-suite](https://www.openpolicyagent.org/docs/latest/ir/#test-suite) barring a few
 builtins. See [OPA Conformance](#opa-conformance) below.
 
 ## Bindings
@@ -245,7 +245,7 @@ Benchmark 1: opa eval -b tests/aci -d tests/aci/data.json -i tests/aci/input.jso
 ```
 ## OPA Conformance
 
-Regorus has been verified to be compliant with [OPA v0.63.0](https://github.com/open-policy-agent/opa/releases/tag/v0.63.0)
+Regorus has been verified to be compliant with [OPA v0.64.0](https://github.com/open-policy-agent/opa/releases/tag/v0.64.0)
 using a [test driver](https://github.com/microsoft/regorus/blob/main/tests/opa.rs) that loads and runs the OPA testsuite using Regorus, and verifies that expected outputs are produced.
 
 The test driver can be invoked by running:
diff --git a/docs/builtins.md b/docs/builtins.md
index 71e99029..9eafd6e0 100644
--- a/docs/builtins.md
+++ b/docs/builtins.md
@@ -159,24 +159,25 @@ In future, each builtin will be associated with a feature (many builtins could b
   | [type_name](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-types-type_name)   | _       |
   
 - [Encoding](https://www.openpolicyagent.org/docs/latest/policy-reference/#encoding)
-  | Builtin                                                                                                                          | Feature     |
-  |----------------------------------------------------------------------------------------------------------------------------------|-------------|
-  | [base64.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64is_valid)                 | `base64`    |
-  | [base64url.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urldecode)               | `base64`    |
-  | [base64url.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urlencode)               | `base64url` |
-  | [base64url.encode_no_pad](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urlencode_no_pad) | `base64url` |
-  | [hex.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-hexdecode)                           | `hex`       |
-  | [hex.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-hexencode)                           | `hex`       |
-  | [json.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonis_valid)                     | _           |
-  | [json.marshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonmarshal)                       | _           |
-  | [json.unmarshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonunmarshal)                   | _           |
-  | [urlquery.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlquerydecode)                 | `urlquery`  |
-  | [urlquery.decode_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlquerydecode_object)   | `urlquery`  |
-  | [urlquery.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlqueryencode)                 | `urlquery`  |
-  | [urlquery.encode_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlqueryencode_object)   | `urlquery`  |
-  | [yaml.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlis_valid)                     | `yaml`      |
-  | [yaml.marshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlmarshal)                       | `yaml`      |
-  | [yaml.unmarshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlunmarshal)                   | `yaml`            |
+  | Builtin                                                                                                                              | Feature     |
+  |--------------------------------------------------------------------------------------------------------------------------------------|-------------|
+  | [base64.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64is_valid)                     | `base64`    |
+  | [base64url.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urldecode)                   | `base64`    |
+  | [base64url.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urlencode)                   | `base64url` |
+  | [base64url.encode_no_pad](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-base64urlencode_no_pad)     | `base64url` |
+  | [hex.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-hexdecode)                               | `hex`       |
+  | [hex.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-hexencode)                               | `hex`       |
+  | [json.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonis_valid)                         | _           |
+  | [json.marshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonmarshal)                           | _           |
+  | [json.marshal_with_options](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonmarshal_with_options) | _           |
+  | [json.unmarshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-jsonunmarshal)                       | _           |
+  | [urlquery.decode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlquerydecode)                     | `urlquery`  |
+  | [urlquery.decode_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlquerydecode_object)       | `urlquery`  |
+  | [urlquery.encode](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlqueryencode)                     | `urlquery`  |
+  | [urlquery.encode_object](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-urlqueryencode_object)       | `urlquery`  |
+  | [yaml.is_valid](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlis_valid)                         | `yaml`      |
+  | [yaml.marshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlmarshal)                           | `yaml`      |
+  | [yaml.unmarshal](https://www.openpolicyagent.org/docs/latest/policy-reference/#builtin-encoding-yamlunmarshal)                       | `yaml`      |
 
 - [Time](https://www.openpolicyagent.org/docs/latest/policy-reference/#time)
    | Builtin                                                                                                                    | Feature |
diff --git a/src/builtins/encoding.rs b/src/builtins/encoding.rs
index 16a51d66..7ac34054 100644
--- a/src/builtins/encoding.rs
+++ b/src/builtins/encoding.rs
@@ -42,6 +42,7 @@ pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
     }
     m.insert("json.is_valid", (json_is_valid, 1));
     m.insert("json.marshal", (json_marshal, 1));
+    m.insert("json.marshal_with_options", (json_marshal_with_options, 2));
     m.insert("json.unmarshal", (json_unmarshal, 1));
 
     #[cfg(feature = "yaml")]
@@ -368,6 +369,74 @@ fn json_marshal(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool
     ))
 }
 
+fn json_marshal_with_options(
+    span: &Span,
+    params: &[Ref<Expr>],
+    args: &[Value],
+    _strict: bool,
+) -> Result<Value> {
+    let name = "json.marshal_with_options";
+    ensure_args_count(span, name, params, args, 2)?;
+
+    let options = ensure_object(name, &params[1], args[1].clone())?;
+    let (mut pretty, mut indent, mut prefix) = (true, Some("\t".to_owned()), None);
+    for (option, option_value) in options.iter() {
+        match option {
+            Value::String(s) if s.as_ref() == "pretty" && option_value.as_bool().is_ok() => {
+                pretty = option_value == &Value::Bool(true);
+            }
+            Value::String(s) if s.as_ref() == "pretty" => bail!(params[1]
+                .span()
+                .error("marshaling option `pretty` must be true or false")),
+            Value::String(s) if s.as_ref() == "prefix" && option_value.as_string().is_ok() => {
+                prefix = Some(option_value.as_string()?.as_ref().to_string());
+            }
+            Value::String(s) if s.as_ref() == "prefix" => bail!(params[1]
+                .span()
+                .error("marshaling option `pretty` must be string")),
+            Value::String(s) if s.as_ref() == "indent" && option_value.as_string().is_ok() => {
+                indent = Some(option_value.as_string()?.as_ref().to_string());
+            }
+            Value::String(s) if s.as_ref() == "indent" => bail!(params[1]
+                .span()
+                .error("marshaling option `pretty` must be string")),
+            _ => bail!(params[1]
+                .span()
+                .error("marshaling option must be one of `indent`, `prefix` or `pretty`")),
+        }
+    }
+
+    if !pretty || options.is_empty() {
+        return Ok(Value::String(
+            serde_json::to_string(&args[0])
+                .with_context(|| span.error("could not serialize to json"))?
+                .into(),
+        ));
+    }
+
+    let lines: Vec<String> = serde_json::to_string_pretty(&args[0])
+        .with_context(|| span.error("could not serialize to json"))?
+        .split('\n')
+        .map(|line| {
+            let mut line = line.to_string();
+
+            if let Some(indent) = &indent {
+                let start_trimmed = line.trim_start();
+                let leading_spaces = line.len() - start_trimmed.len();
+                let indentation_level = leading_spaces / 2;
+                line = indent.repeat(indentation_level) + start_trimmed;
+            }
+
+            if let Some(prefix) = &prefix {
+                line = prefix.to_owned() + &line;
+            }
+            line
+        })
+        .collect();
+
+    Ok(Value::from(lines.join("\n")))
+}
+
 fn json_unmarshal(
     span: &Span,
     params: &[Ref<Expr>],


Test patch:
diff --git a/tests/opa.rs b/tests/opa.rs
index 59b2a798..9c36b285 100644
--- a/tests/opa.rs
+++ b/tests/opa.rs
@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
 use walkdir::WalkDir;
 
 const OPA_REPO: &str = "https://github.com/open-policy-agent/opa";
-const OPA_BRANCH: &str = "v0.63.0";
+const OPA_BRANCH: &str = "v0.64.0";
 
 #[derive(Serialize, Deserialize, PartialEq, Debug)]
 #[serde(deny_unknown_fields)]