Repository: microsoft/regorus (language: python)

Issue:
Update bindings with newly added Engine APIs
New functions have been added to Engine e.g.: eval_bool_query, eval_allow_query etc.
The extensions need to be updated to expose these functions.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 67bd23a4..8c2c60a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,11 @@ Cargo.lock
 worktrees/
 
 # build folders
-**/build
\ No newline at end of file
+**/build
+
+# Generated C# bindings
+**/*.g.cs
+
+# Generated C, C++ headers
+bindings/ffi/regorus.h
+bindings/ffi/regorus.ffi.hpp
\ No newline at end of file
diff --git a/bindings/c-nostd/main.c b/bindings/c-nostd/main.c
index 9cd54e71..7d80c4ad 100644
--- a/bindings/c-nostd/main.c
+++ b/bindings/c-nostd/main.c
@@ -46,21 +46,21 @@ int main() {
     free(buffer);
     if (r.status != RegorusStatusOk)
 	goto error;
-    printf("Loaded policy %s\n", r.output);
+    printf("Loaded package %s\n", r.output);
     regorus_result_drop(r);
 
     r = regorus_engine_add_policy(engine, "api.rego", (buffer = file_to_string("../../../tests/aci/api.rego")));
     free(buffer);
     if (r.status != RegorusStatusOk)
 	goto error;
-    printf("Loaded policy %s\n", r.output);
+    printf("Loaded package %s\n", r.output);
     regorus_result_drop(r);
 
     r = regorus_engine_add_policy(engine, "policy.rego", (buffer = file_to_string("../../../tests/aci/policy.rego")));
     free(buffer);
     if (r.status != RegorusStatusOk)
 	goto error;
-    printf("Loaded policy %s\n", r.output);
+    printf("Loaded package %s\n", r.output);
     regorus_result_drop(r);
 
     // Add data
@@ -77,8 +77,8 @@ int main() {
 	goto error;
     regorus_result_drop(r);
 
-    // Eval query
-    r = regorus_engine_eval_query(engine, "data.framework.mount_overlay=x");
+    // Eval rule.
+    r = regorus_engine_eval_rule(engine, "data.framework.mount_overlay");
     if (r.status != RegorusStatusOk)
 	goto error;
 
diff --git a/bindings/c/CMakeLists.txt b/bindings/c/CMakeLists.txt
index d0fb422c..6acbb727 100644
--- a/bindings/c/CMakeLists.txt
+++ b/bindings/c/CMakeLists.txt
@@ -25,7 +25,7 @@ corrosion_import_crate(
   FEATURES "regorus/semver"
 
   # Link statically
-  CRATE_TYPES "staticlib"
+  CRATE_TYPES "cdylib"
   )
 
 add_executable(regorus_test main.c)
diff --git a/bindings/c/main.c b/bindings/c/main.c
index 17651ba6..0066b39e 100644
--- a/bindings/c/main.c
+++ b/bindings/c/main.c
@@ -10,19 +10,19 @@ int main() {
     r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/framework.rego");
     if (r.status != RegorusStatusOk)
 	goto error;
-    printf("Loaded policy %s\n", r.output);
+    printf("Loaded package %s\n", r.output);
     regorus_result_drop(r);
 
     r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/api.rego");
     if (r.status != RegorusStatusOk)
 	goto error;
-    printf("Loaded policy %s\n", r.output);
+    printf("Loaded package %s\n", r.output);
     regorus_result_drop(r);
     
     r = regorus_engine_add_policy_from_file(engine, "../../../tests/aci/policy.rego");
     if (r.status != RegorusStatusOk)
 	goto error;
-    printf("Loaded policy %s\n", r.output);
+    printf("Loaded package %s\n", r.output);
     regorus_result_drop(r);
 
     // Add data
@@ -37,22 +37,61 @@ int main() {
 	goto error;
     regorus_result_drop(r);
 
-    // Eval query
-    r = regorus_engine_eval_query(engine, "data.framework.mount_overlay=x");
+    // Eval rule.
+    r = regorus_engine_eval_query(engine, "data.framework.mount_overlay");
     if (r.status != RegorusStatusOk)
 	goto error;
 
     // Print output
-    printf("%s", r.output);
+    printf("%s\n", r.output);
     regorus_result_drop(r);
     
-    
     // Free the engine.
     regorus_engine_drop(engine);
 
+    // Create another engine.
+    engine = regorus_engine_new();
+
+    r = regorus_engine_add_policy(
+	engine,
+	"test.rego",
+	"package test\n"
+	"x = 1\n"
+	"message = `Hello`"
+	);
+
+    // Evaluate rule.
+    if (r.status != RegorusStatusOk)
+	goto error;
+
+    r = regorus_engine_set_enable_coverage(engine, true);
+    regorus_result_drop(r);
+    
+    r = regorus_engine_eval_query(engine, "data.test.message");
+    if (r.status != RegorusStatusOk)
+	goto error;
+
+    // Print output
+    printf("%s\n", r.output);
+    regorus_result_drop(r);
+	
+    // Print pretty coverage report.
+    r = regorus_engine_get_coverage_report_pretty(engine);
+    if (r.status != RegorusStatusOk)
+	goto error;
+
+    printf("%s\n", r.output);
+    regorus_result_drop(r);
+    
+    // Free the engine.
+    regorus_engine_drop(engine);
+    
     return 0;
+    
 error:
     printf("%s", r.error_message);
+    regorus_result_drop(r);
+    regorus_engine_drop(engine);
 	
     return 1;
 }
diff --git a/bindings/cpp/CMakeLists.txt b/bindings/cpp/CMakeLists.txt
index a510438b..04870fc0 100644
--- a/bindings/cpp/CMakeLists.txt
+++ b/bindings/cpp/CMakeLists.txt
@@ -26,7 +26,7 @@ corrosion_import_crate(
   FEATURES "regorus/semver"
 
   # Link statically
-  CRATE_TYPES "staticlib")
+  CRATE_TYPES "cdylib")
 
 add_executable(regorus_test main.cpp)
 # Add path to <regorus-source-folder>/bindings/ffi
diff --git a/bindings/cpp/main.cpp b/bindings/cpp/main.cpp
index 08145249..82672472 100644
--- a/bindings/cpp/main.cpp
+++ b/bindings/cpp/main.cpp
@@ -6,6 +6,8 @@ void example()
     // Create engine
     regorus::Engine engine;
 
+    engine.set_enable_coverage(true);
+    
     // Add policies.
     engine.add_policy("objects.rego",R"(package objects
 
@@ -67,6 +69,14 @@ f := e["dev"])");
     } else {
 	std::cerr<<result.error()<<std::endl;
     }
+
+    // Print coverage report
+    auto result1 = engine.get_coverage_report_pretty();
+    if (result1) {
+	std::cout<<result1.output()<<std::endl;
+    } else {
+	std::cerr<<result1.error()<<std::endl;
+    }
 }
 
 int main() {
@@ -89,7 +99,7 @@ int main() {
 	    std::cerr<<result.error()<<std::endl;
 	    return -1;
 	}
-	std::cout<<"Loaded policy "<<result.output()<< std::endl;
+	std::cout<<"Loaded package "<<result.output()<< std::endl;
     }
     {
 	auto result = engine.add_data_from_json_file("../../../tests/aci/data.json");
@@ -99,7 +109,7 @@ int main() {
 	}
     }
 
-    // Set input and eval query.
+    // Set input and eval rule.
     {
 	auto result = engine.set_input_from_json_file("../../../tests/aci/input.json");
 	if (!result) {
@@ -107,13 +117,13 @@ int main() {
 	    return -1;
 	}
     }
-    auto result = engine.eval_query("data.framework.mount_overlay = x");
+    auto result = engine.eval_rule("data.framework.mount_overlay");
     if (!result) {
 	std::cerr<<result.error()<<std::endl;
 	return -1;
     }
 
     std::cout<<result.output()<<std::endl;
-    
+
     example();
 }
diff --git a/bindings/cpp/regorus.hpp b/bindings/cpp/regorus.hpp
index 692e480f..36edd821 100644
--- a/bindings/cpp/regorus.hpp
+++ b/bindings/cpp/regorus.hpp
@@ -83,6 +83,26 @@ namespace regorus {
 	    return Result(regorus_engine_eval_query(engine, query));
 	}
 	
+	Result eval_rule(const char* rule) {
+	    return Result(regorus_engine_eval_rule(engine, rule));
+	}
+	
+	Result set_enable_coverage(bool enable) {
+	    return Result(regorus_engine_set_enable_coverage(engine, enable));
+	}
+	
+	Result clear_coverage_data() {
+            return Result(regorus_engine_clear_coverage_data(engine));
+	}
+	
+	Result get_coverage_report() {
+            return Result(regorus_engine_get_coverage_report(engine));
+	}
+	
+	Result get_coverage_report_pretty() {
+            return Result(regorus_engine_get_coverage_report_pretty(engine));
+	}
+	
 	~Engine() {
 	    regorus_engine_drop(engine);
 	}
diff --git a/bindings/csharp/Program.cs b/bindings/csharp/Program.cs
index 702ecaa2..ea98a624 100644
--- a/bindings/csharp/Program.cs
+++ b/bindings/csharp/Program.cs
@@ -33,19 +33,26 @@
 
 w.Restart();
 
-// Set input and eval query.
+// Set input and eval rule.
 engine.SetInputFromJsonFile("../../tests/aci/input.json");
-var results = engine.EvalQuery("data.framework.mount_overlay = x");
-var resultsDoc = System.Text.Json.JsonDocument.Parse(results);
+var value = engine.EvalQuery("data.framework.mount_overlay");
+var valueDoc = System.Text.Json.JsonDocument.Parse(value);
 
 w.Stop();
 var evalTicks = w.ElapsedTicks;
 
-Console.WriteLine("{0}", results);
+Console.WriteLine("{0}", valueDoc);
 
 
 Console.WriteLine("Engine creation took {0} msecs", (newEngineTicks*nanosecPerTick)/(1000.0*1000.0));
 Console.WriteLine("Load policies and data took {0} msecs", (loadPoliciesTicks*nanosecPerTick)/(1000.0*1000.0));
 Console.WriteLine("EvalQuery took {0} msecs", (evalTicks*nanosecPerTick)/(1000.0*1000.0));
 
+engine = new Regorus.Engine();
+engine.AddPolicy(
+  "test.rego", 
+  "package test\nx = 1\nmessage = `Hello`");
 
+engine.SetEnableCoverage(true);
+Console.WriteLine("{0}", engine.EvalRule("data.test.message"));
+Console.WriteLine("{0}", engine.GetCoverageReportPretty());
diff --git a/bindings/csharp/Regorus.cs b/bindings/csharp/Regorus.cs
index d63670cd..fac09ee0 100644
--- a/bindings/csharp/Regorus.cs
+++ b/bindings/csharp/Regorus.cs
@@ -29,117 +29,159 @@ public object Clone()
 
 	}
 
-	public void AddPolicy(string path, string rego)
+	public string AddPolicy(string path, string rego)
 	{
 	    var pathBytes = Encoding.UTF8.GetBytes(path);
 	    var regoBytes = Encoding.UTF8.GetBytes(rego);
-	    
+
 	    unsafe
 	    {
 		fixed (byte* pathPtr = pathBytes)
 		{
 		    fixed(byte* regoPtr = regoBytes)
 		    {
-			CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy(E, pathPtr, regoPtr));
+			return CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy(E, pathPtr, regoPtr));
 		    }
-		}		
+		}
 	    }
 	}
 
-	public void AddPolicyFromFile(string path)
+	public string AddPolicyFromFile(string path)
 	{
 	    var pathBytes = Encoding.UTF8.GetBytes(path);
-	    
+
 	    unsafe
 	    {
 		fixed (byte* pathPtr = pathBytes)
 		{
-		    CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy_from_file(E, pathPtr));
-		}		
+		    return CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy_from_file(E, pathPtr));
+		}
 	    }
 	}
-	
+
 	public void AddDataJson(string data)
 	{
 	    var dataBytes = Encoding.UTF8.GetBytes(data);
-	    
+
 	    unsafe
 	    {
 		fixed (byte* dataPtr = dataBytes)
 		{
 		    CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_json(E, dataPtr));
-
-		}		
+		}
 	    }
 	}
-	
+
 	public void AddDataFromJsonFile(string path)
 	{
 	    var pathBytes = Encoding.UTF8.GetBytes(path);
-	    
+
 	    unsafe
 	    {
 		fixed (byte* pathPtr = pathBytes)
 		{
 		    CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_from_json_file(E, pathPtr));
-
-		}		
+		}
 	    }
 	}
-	
+
 	public void SetInputJson(string input)
 	{
 	    var inputBytes = Encoding.UTF8.GetBytes(input);
-	    
+
 	    unsafe
 	    {
 		fixed (byte* inputPtr = inputBytes)
 		{
 		    CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_json(E, inputPtr));
-
-		}		
+		}
 	    }
 	}
-	
+
 	public void SetInputFromJsonFile(string path)
 	{
 	    var pathBytes = Encoding.UTF8.GetBytes(path);
-	    
+
 	    unsafe
 	    {
 		fixed (byte* pathPtr = pathBytes)
 		{
 		    CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_from_json_file(E, pathPtr));
-
-		}		
+		}
 	    }
 	}
-	
+
 	public string EvalQuery(string query)
 	{
 	    var queryBytes = Encoding.UTF8.GetBytes(query);
 
-	    var resultJson = "";
 	    unsafe
 	    {
 		fixed (byte* queryPtr = queryBytes)
 		{
-		    var result = RegorusFFI.API.regorus_engine_eval_query(E, queryPtr);
-		    if (result.status == RegorusFFI.RegorusStatus.RegorusStatusOk) {
-			if (result.output is not null) {
-			    resultJson = System.Runtime.InteropServices.Marshal.PtrToStringUTF8((IntPtr)result.output);
-			}
-			RegorusFFI.API.regorus_result_drop(result);
-		    } else {
-			CheckAndDropResult(result);
-		    }
-		    
-		}		
+		    return CheckAndDropResult(RegorusFFI.API.regorus_engine_eval_query(E, queryPtr));
+		}
 	    }
-	    if (resultJson is not null) {
-		return resultJson;
-	    } else {
-		return "";
+	}
+
+	public string EvalRule(string rule)
+	{
+	    var ruleBytes = Encoding.UTF8.GetBytes(rule);
+
+	    unsafe
+	    {
+		fixed (byte* rulePtr = ruleBytes)
+		{
+		    return CheckAndDropResult(RegorusFFI.API.regorus_engine_eval_query(E, rulePtr));
+		}
+	    }
+	}
+	
+	public void SetEnableCoverage(bool enable)
+	{
+	    unsafe
+	    {
+		CheckAndDropResult(RegorusFFI.API.regorus_engine_set_enable_coverage(E, enable));
+	    }
+	}
+	
+	public void ClearCoverageData()
+	{
+	    unsafe
+	    {
+		CheckAndDropResult(RegorusFFI.API.regorus_engine_clear_coverage_data(E));
+	    }
+	}
+	
+	public string GetCoverageReport()
+	{
+	    unsafe
+	    {
+		return CheckAndDropResult(RegorusFFI.API.regorus_engine_get_coverage_report(E));
+	    }
+	}
+	
+	public string GetCoverageReportPretty()
+	{
+	    unsafe
+	    {
+		return CheckAndDropResult(RegorusFFI.API.regorus_engine_get_coverage_report_pretty(E));
+	    }
+	}
+	
+	public void SetGatherPrints(bool enable)
+	{
+	    unsafe
+	    {
+		CheckAndDropResult(RegorusFFI.API.regorus_engine_set_gather_prints(E, enable));
+	    }
+	}
+	
+	public string TakePrints()
+	{
+	    unsafe
+	    {
+		return CheckAndDropResult(RegorusFFI.API.regorus_engine_take_prints(E));
 	    }
 	}
 	
@@ -151,8 +193,8 @@ public string EvalQuery(string query)
 	    }
 	}
 
-	
-	void CheckAndDropResult(RegorusFFI.RegorusResult result)
+
+	string CheckAndDropResult(RegorusFFI.RegorusResult result)
 	{
 	    if (result.status != RegorusFFI.RegorusStatus.RegorusStatusOk) {
 		unsafe {
@@ -162,7 +204,16 @@ void CheckAndDropResult(RegorusFFI.RegorusResult result)
 		    throw ex;
 		}
 	    }
-	    RegorusFFI.API.regorus_result_drop(result);		    
+
+	    var resultString = "";
+	    unsafe
+	    {
+		if (result.output is not null) {
+		    resultString = System.Runtime.InteropServices.Marshal.PtrToStringUTF8((IntPtr)result.output);
+		}
+		RegorusFFI.API.regorus_result_drop(result);
+	    }
+	    return resultString;
 	}
 
     }
diff --git a/bindings/ffi/Cargo.toml b/bindings/ffi/Cargo.toml
index 55fab98e..ec875095 100644
--- a/bindings/ffi/Cargo.toml
+++ b/bindings/ffi/Cargo.toml
@@ -13,8 +13,9 @@ regorus = { path  = "../..", default-features = false }
 serde_json = "1.0.113"
 
 [features]
-default = ["std", "regorus/full-opa"]
+default = ["std", "coverage", "regorus/arc", "regorus/full-opa"]
 std = ["regorus/std"]
+coverage = ["regorus/coverage"]
 custom_allocator = []
 
 [build-dependencies]
diff --git a/bindings/ffi/RegorusFFI.g.cs b/bindings/ffi/RegorusFFI.g.cs
deleted file mode 100644
index 5ea1a603..00000000
--- a/bindings/ffi/RegorusFFI.g.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-// <auto-generated>
-// This code is generated by csbindgen.
-// DON'T CHANGE THIS DIRECTLY.
-// </auto-generated>
-#pragma warning disable CS8500
-#pragma warning disable CS8981
-using System;
-using System.Runtime.InteropServices;
-
-
-namespace RegorusFFI
-{
-    internal static unsafe partial class API
-    {
-        const string __DllName = "regorus_ffi";
-
-
-
-        /// <summary>Drop a `RegorusResult`.  `output` and `error_message` strings are not valid after drop.</summary>
-        [DllImport(__DllName, EntryPoint = "regorus_result_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern void regorus_result_drop(RegorusResult r);
-
-        /// <summary>Construct a new Engine  See https://docs.rs/regorus/latest/regorus/struct.Engine.html</summary>
-        [DllImport(__DllName, EntryPoint = "regorus_engine_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusEngine* regorus_engine_new();
-
-        /// <summary>Clone a [`RegorusEngine`]  To avoid having to parse same policy again, the engine can be cloned after policies and data have been added.</summary>
-        [DllImport(__DllName, EntryPoint = "regorus_engine_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusEngine* regorus_engine_clone(RegorusEngine* engine);
-
-        [DllImport(__DllName, EntryPoint = "regorus_engine_drop", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern void regorus_engine_drop(RegorusEngine* engine);
-
-        /// <summary>Add a policy  The policy is parsed into AST. See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_policy  * `path`: A filename to be associated with the policy. * `rego`: Rego policy.</summary>
-        [DllImport(__DllName, EntryPoint = "regorus_engine_add_policy", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusResult regorus_engine_add_policy(RegorusEngine* engine, byte* path, byte* rego);
-
-        [DllImport(__DllName, EntryPoint = "regorus_engine_add_policy_from_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusResult regorus_engine_add_policy_from_file(RegorusEngine* engine, byte* path);
-
-        /// <summary>Add policy data.  See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_data * `data`: JSON encoded value to be used as policy data.</summary>
-        [DllImport(__DllName, EntryPoint = "regorus_engine_add_data_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusResult regorus_engine_add_data_json(RegorusEngine* engine, byte* data);
-
-        [DllImport(__DllName, EntryPoint = "regorus_engine_add_data_from_json_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusResult regorus_engine_add_data_from_json_file(RegorusEngine* engine, byte* path);
-
-        /// <summary>Clear policy data.  See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.clear_data</summary>
-        [DllImport(__DllName, EntryPoint = "regorus_engine_clear_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusResult regorus_engine_clear_data(RegorusEngine* engine);
-
-        /// <summary>Set input.  See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_input * `input`: JSON encoded value to be used as input to query.</summary>
-        [DllImport(__DllName, EntryPoint = "regorus_engine_set_input_json", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusResult regorus_engine_set_input_json(RegorusEngine* engine, byte* input);
-
-        [DllImport(__DllName, EntryPoint = "regorus_engine_set_input_from_json_file", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusResult regorus_engine_set_input_from_json_file(RegorusEngine* engine, byte* path);
-
-        /// <summary>Evaluate query.  See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_query * `query`: Rego expression to be evaluate.</summary>
-        [DllImport(__DllName, EntryPoint = "regorus_engine_eval_query", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
-        public static extern RegorusResult regorus_engine_eval_query(RegorusEngine* engine, byte* query);
-
-
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    internal unsafe partial struct RegorusResult
-    {
-        public RegorusStatus status;
-        public byte* output;
-        public byte* error_message;
-    }
-
-    [StructLayout(LayoutKind.Sequential)]
-    internal unsafe partial struct RegorusEngine
-    {
-    }
-
-
-    internal enum RegorusStatus : uint
-    {
-        RegorusStatusOk,
-        RegorusStatusError,
-    }
-
-
-}
diff --git a/bindings/ffi/regorus.ffi.hpp b/bindings/ffi/regorus.ffi.hpp
deleted file mode 100644
index 13a98bd5..00000000
--- a/bindings/ffi/regorus.ffi.hpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef REGORUS_FFI_HPP
-#define REGORUS_FFI_HPP
-
-#include <cstdarg>
-#include <cstdint>
-#include <cstdlib>
-#include <ostream>
-#include <new>
-
-/// Status of a call on `RegorusEngine`.
-enum class RegorusStatus {
-  /// The operation was successful.
-  RegorusStatusOk,
-  /// The operation was unsuccessful.
-  RegorusStatusError,
-};
-
-/// Wrapper for `regorus::Engine`.
-struct RegorusEngine;
-
-/// Result of a call on `RegorusEngine`.
-///
-/// Must be freed using `regorus_result_drop`.
-struct RegorusResult {
-  /// Status
-  RegorusStatus status;
-  /// Output produced by the call.
-  /// Owned by Rust.
-  char *output;
-  /// Errors produced by the call.
-  /// Owned by Rust.
-  char *error_message;
-};
-
-extern "C" {
-
-/// Drop a `RegorusResult`.
-///
-/// `output` and `error_message` strings are not valid after drop.
-void regorus_result_drop(RegorusResult r);
-
-/// Construct a new Engine
-///
-/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html
-RegorusEngine *regorus_engine_new();
-
-/// Clone a [`RegorusEngine`]
-///
-/// To avoid having to parse same policy again, the engine can be cloned
-/// after policies and data have been added.
-RegorusEngine *regorus_engine_clone(RegorusEngine *engine);
-
-void regorus_engine_drop(RegorusEngine *engine);
-
-/// Add a policy
-///
-/// The policy is parsed into AST.
-/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_policy
-///
-/// * `path`: A filename to be associated with the policy.
-/// * `rego`: Rego policy.
-RegorusResult regorus_engine_add_policy(RegorusEngine *engine, const char *path, const char *rego);
-
-RegorusResult regorus_engine_add_policy_from_file(RegorusEngine *engine, const char *path);
-
-/// Add policy data.
-///
-/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_data
-/// * `data`: JSON encoded value to be used as policy data.
-RegorusResult regorus_engine_add_data_json(RegorusEngine *engine, const char *data);
-
-RegorusResult regorus_engine_add_data_from_json_file(RegorusEngine *engine, const char *path);
-
-/// Clear policy data.
-///
-/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.clear_data
-RegorusResult regorus_engine_clear_data(RegorusEngine *engine);
-
-/// Set input.
-///
-/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_input
-/// * `input`: JSON encoded value to be used as input to query.
-RegorusResult regorus_engine_set_input_json(RegorusEngine *engine, const char *input);
-
-RegorusResult regorus_engine_set_input_from_json_file(RegorusEngine *engine, const char *path);
-
-/// Evaluate query.
-///
-/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_query
-/// * `query`: Rego expression to be evaluate.
-RegorusResult regorus_engine_eval_query(RegorusEngine *engine, const char *query);
-
-extern uint8_t *regorus_aligned_alloc(uintptr_t alignment, uintptr_t size);
-
-extern void regorus_free(uint8_t *ptr);
-
-} // extern "C"
-
-#endif // REGORUS_FFI_HPP
diff --git a/bindings/ffi/regorus.h b/bindings/ffi/regorus.h
deleted file mode 100644
index db696b92..00000000
--- a/bindings/ffi/regorus.h
+++ /dev/null
@@ -1,131 +0,0 @@
-#ifndef REGORUS_H
-#define REGORUS_H
-
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-/**
- * Status of a call on `RegorusEngine`.
- */
-typedef enum RegorusStatus {
-  /**
-   * The operation was successful.
-   */
-  RegorusStatusOk,
-  /**
-   * The operation was unsuccessful.
-   */
-  RegorusStatusError,
-} RegorusStatus;
-
-/**
- * Wrapper for `regorus::Engine`.
- */
-typedef struct RegorusEngine RegorusEngine;
-
-/**
- * Result of a call on `RegorusEngine`.
- *
- * Must be freed using `regorus_result_drop`.
- */
-typedef struct RegorusResult {
-  /**
-   * Status
-   */
-  enum RegorusStatus status;
-  /**
-   * Output produced by the call.
-   * Owned by Rust.
-   */
-  char *output;
-  /**
-   * Errors produced by the call.
-   * Owned by Rust.
-   */
-  char *error_message;
-} RegorusResult;
-
-/**
- * Drop a `RegorusResult`.
- *
- * `output` and `error_message` strings are not valid after drop.
- */
-void regorus_result_drop(struct RegorusResult r);
-
-/**
- * Construct a new Engine
- *
- * See https://docs.rs/regorus/latest/regorus/struct.Engine.html
- */
-struct RegorusEngine *regorus_engine_new(void);
-
-/**
- * Clone a [`RegorusEngine`]
- *
- * To avoid having to parse same policy again, the engine can be cloned
- * after policies and data have been added.
- */
-struct RegorusEngine *regorus_engine_clone(struct RegorusEngine *engine);
-
-void regorus_engine_drop(struct RegorusEngine *engine);
-
-/**
- * Add a policy
- *
- * The policy is parsed into AST.
- * See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_policy
- *
- * * `path`: A filename to be associated with the policy.
- * * `rego`: Rego policy.
- */
-struct RegorusResult regorus_engine_add_policy(struct RegorusEngine *engine,
-                                               const char *path,
-                                               const char *rego);
-
-struct RegorusResult regorus_engine_add_policy_from_file(struct RegorusEngine *engine,
-                                                         const char *path);
-
-/**
- * Add policy data.
- *
- * See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_data
- * * `data`: JSON encoded value to be used as policy data.
- */
-struct RegorusResult regorus_engine_add_data_json(struct RegorusEngine *engine, const char *data);
-
-struct RegorusResult regorus_engine_add_data_from_json_file(struct RegorusEngine *engine,
-                                                            const char *path);
-
-/**
- * Clear policy data.
- *
- * See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.clear_data
- */
-struct RegorusResult regorus_engine_clear_data(struct RegorusEngine *engine);
-
-/**
- * Set input.
- *
- * See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_input
- * * `input`: JSON encoded value to be used as input to query.
- */
-struct RegorusResult regorus_engine_set_input_json(struct RegorusEngine *engine, const char *input);
-
-struct RegorusResult regorus_engine_set_input_from_json_file(struct RegorusEngine *engine,
-                                                             const char *path);
-
-/**
- * Evaluate query.
- *
- * See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_query
- * * `query`: Rego expression to be evaluate.
- */
-struct RegorusResult regorus_engine_eval_query(struct RegorusEngine *engine, const char *query);
-
-extern uint8_t *regorus_aligned_alloc(uintptr_t alignment, uintptr_t size);
-
-extern void regorus_free(uint8_t *ptr);
-
-#endif /* REGORUS_H */
diff --git a/bindings/ffi/src/lib.rs b/bindings/ffi/src/lib.rs
index 890214f1..4d9298ad 100644
--- a/bindings/ffi/src/lib.rs
+++ b/bindings/ffi/src/lib.rs
@@ -119,21 +119,20 @@ pub extern "C" fn regorus_engine_new() -> *mut RegorusEngine {
 ///
 /// To avoid having to parse same policy again, the engine can be cloned
 /// after policies and data have been added.
+///
 #[no_mangle]
 pub extern "C" fn regorus_engine_clone(engine: *mut RegorusEngine) -> *mut RegorusEngine {
-    unsafe {
-        if engine.is_null() {
-            return std::ptr::null_mut();
-        }
-        Box::into_raw(Box::new((*engine).clone()))
+    match to_ref(&engine) {
+        Ok(e) => Box::into_raw(Box::new(e.clone())),
+        _ => std::ptr::null_mut(),
     }
 }
 
 #[no_mangle]
 pub extern "C" fn regorus_engine_drop(engine: *mut RegorusEngine) {
-    if !engine.is_null() {
+    if let Ok(e) = to_ref(&engine) {
         unsafe {
-            let _ = Box::from_raw(engine);
+            let _ = Box::from_raw(std::ptr::from_mut(e));
         }
     }
 }
@@ -187,6 +186,18 @@ pub extern "C" fn regorus_engine_add_data_json(
     }())
 }
 
+/// Get list of loaded Rego packages as JSON.
+///
+/// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
+/// * `data`: JSON encoded value to be used as policy data.
+#[no_mangle]
+pub extern "C" fn regorus_engine_get_packages(engine: *mut RegorusEngine) -> RegorusResult {
+    to_regorus_string_result(|| -> Result<String> {
+        serde_json::to_string_pretty(&to_ref(&engine)?.engine.get_packages()?)
+            .map_err(anyhow::Error::msg)
+    }())
+}
+
 #[cfg(feature = "std")]
 #[no_mangle]
 pub extern "C" fn regorus_engine_add_data_from_json_file(
@@ -196,7 +207,7 @@ pub extern "C" fn regorus_engine_add_data_from_json_file(
     to_regorus_result(|| -> Result<()> {
         to_ref(&engine)?
             .engine
-            .add_data(regorus::Value::from_json_file(&from_c_str("path", path)?)?)
+            .add_data(regorus::Value::from_json_file(from_c_str("path", path)?)?)
     }())
 }
 
@@ -237,7 +248,7 @@ pub extern "C" fn regorus_engine_set_input_from_json_file(
     to_regorus_result(|| -> Result<()> {
         to_ref(&engine)?
             .engine
-            .set_input(regorus::Value::from_json_file(&from_c_str("path", path)?)?);
+            .set_input(regorus::Value::from_json_file(from_c_str("path", path)?)?);
         Ok(())
     }())
 }
@@ -267,6 +278,139 @@ pub extern "C" fn regorus_engine_eval_query(
     }
 }
 
+/// Evaluate specified rule.
+///
+/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_rule
+/// * `rule`: Path to the rule.
+#[no_mangle]
+pub extern "C" fn regorus_engine_eval_rule(
+    engine: *mut RegorusEngine,
+    rule: *const c_char,
+) -> RegorusResult {
+    let output = || -> Result<String> {
+        to_ref(&engine)?
+            .engine
+            .eval_rule(from_c_str("rule", rule)?)?
+            .to_json_str()
+    }();
+    match output {
+        Ok(out) => RegorusResult {
+            status: RegorusStatus::RegorusStatusOk,
+            output: to_c_str(out),
+            error_message: std::ptr::null_mut(),
+        },
+        Err(e) => to_regorus_result(Err(e)),
+    }
+}
+
+/// Enable/disable coverage.
+///
+/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_enable_coverage
+/// * `enable`: Whether to enable or disable coverage.
+#[no_mangle]
+#[cfg(feature = "coverage")]
+pub extern "C" fn regorus_engine_set_enable_coverage(
+    engine: *mut RegorusEngine,
+    enable: bool,
+) -> RegorusResult {
+    to_regorus_result(|| -> Result<()> {
+        to_ref(&engine)?.engine.set_enable_coverage(enable);
+        Ok(())
+    }())
+}
+
+/// Get coverage report.
+///
+/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.get_coverage_report
+#[no_mangle]
+#[cfg(feature = "coverage")]
+pub extern "C" fn regorus_engine_get_coverage_report(engine: *mut RegorusEngine) -> RegorusResult {
+    let output = || -> Result<String> {
+        Ok(serde_json::to_string_pretty(
+            &to_ref(&engine)?.engine.get_coverage_report()?,
+        )?)
+    }();
+    match output {
+        Ok(out) => RegorusResult {
+            status: RegorusStatus::RegorusStatusOk,
+            output: to_c_str(out),
+            error_message: std::ptr::null_mut(),
+        },
+        Err(e) => to_regorus_result(Err(e)),
+    }
+}
+
+/// Get pretty printed coverage report.
+///
+/// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
+#[no_mangle]
+#[cfg(feature = "coverage")]
+pub extern "C" fn regorus_engine_get_coverage_report_pretty(
+    engine: *mut RegorusEngine,
+) -> RegorusResult {
+    let output = || -> Result<String> {
+        to_ref(&engine)?
+            .engine
+            .get_coverage_report()?
+            .to_string_pretty()
+    }();
+    match output {
+        Ok(out) => RegorusResult {
+            status: RegorusStatus::RegorusStatusOk,
+            output: to_c_str(out),
+            error_message: std::ptr::null_mut(),
+        },
+        Err(e) => to_regorus_result(Err(e)),
+    }
+}
+
+/// Clear coverage data.
+///
+/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.clear_coverage_data
+#[no_mangle]
+#[cfg(feature = "coverage")]
+pub extern "C" fn regorus_engine_clear_coverage_data(engine: *mut RegorusEngine) -> RegorusResult {
+    to_regorus_result(|| -> Result<()> {
+        to_ref(&engine)?.engine.clear_coverage_data();
+        Ok(())
+    }())
+}
+
+/// Whether to gather output of print statements.
+///
+/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_gather_prints
+/// * `enable`: Whether to enable or disable gathering print statements.
+#[no_mangle]
+pub extern "C" fn regorus_engine_set_gather_prints(
+    engine: *mut RegorusEngine,
+    enable: bool,
+) -> RegorusResult {
+    to_regorus_result(|| -> Result<()> {
+        to_ref(&engine)?.engine.set_gather_prints(enable);
+        Ok(())
+    }())
+}
+
+/// Take all the gathered print statements.
+///
+/// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.take_prints
+#[no_mangle]
+pub extern "C" fn regorus_engine_take_prints(engine: *mut RegorusEngine) -> RegorusResult {
+    let output = || -> Result<String> {
+        Ok(serde_json::to_string_pretty(
+            &to_ref(&engine)?.engine.take_prints()?,
+        )?)
+    }();
+    match output {
+        Ok(out) => RegorusResult {
+            status: RegorusStatus::RegorusStatusOk,
+            output: to_c_str(out),
+            error_message: std::ptr::null_mut(),
+        },
+        Err(e) => to_regorus_result(Err(e)),
+    }
+}
+
 #[cfg(feature = "custom_allocator")]
 extern "C" {
     fn regorus_aligned_alloc(alignment: usize, size: usize) -> *mut u8;
diff --git a/bindings/go/main.go b/bindings/go/main.go
index 05c8a1a3..eb2ac321 100644
--- a/bindings/go/main.go
+++ b/bindings/go/main.go
@@ -12,7 +12,7 @@ func main() {
 	var err error
 
 	t := time.Now();
-	
+
 	// Create new engine
 	engine := regorus.NewEngine()
 	defer engine.Close()
@@ -26,10 +26,12 @@ func main() {
 		"../../tests/aci/policy.rego",
 	}
 	for _, policy := range policies {
-		if err := engine.AddPolicyFromFile(policy); err != nil {
+		var pkg string
+		if pkg, err = engine.AddPolicyFromFile(policy); err != nil {
 			fmt.Fprintf(os.Stderr, "error: %v\n", err)
 			os.Exit(1)
 		}
+		fmt.Printf("Loaded package %s\n", pkg);
 	}
 	if err = engine.AddDataFromJsonFile("../../tests/aci/data.json"); err != nil {
 		fmt.Fprintf(os.Stderr, "error: %v\n", err)
@@ -38,21 +40,48 @@ func main() {
 	elapsed2 := time.Since(t)
 
 	t = time.Now()
-	// Set input and eval query.
+	// Set input.
 	if err = engine.SetInputFromJsonFile("../../tests/aci/input.json"); err != nil {
 		fmt.Fprintf(os.Stderr, "error: %v\n", err)
 		os.Exit(1)
 	}
 
-
-	if output, err = engine.EvalQuery("data.framework.mount_overlay = x"); err != nil {
+	// Eval Rule
+	if output, err = engine.EvalRule("data.framework.mount_overlay"); err != nil {
 		fmt.Fprintf(os.Stderr, "error: %v\n", err)
 		os.Exit(1)
 	}
 	elapsed3 := time.Since(t)
 
-	fmt.Println("{%s}", output)
+	fmt.Printf("%s\n", output)
 	fmt.Printf("NewEngine took %v\n", elapsed1)
 	fmt.Printf("Add policies and data took %v\n", elapsed2)
 	fmt.Printf("Set input and eval query took %v\n", elapsed3)
+
+	// Create new engine.
+	engine1 := regorus.NewEngine()
+	defer engine1.Close()
+
+	// Enable coverage
+	engine1.SetEnableCoverage(true)
+
+	var pkg string
+	pkg, err = engine1.AddPolicy("test.rego", "package test\nx = 1\nmessage = `Hello`")
+	fmt.Printf("Loaded package %s\n", pkg)
+
+	// Eval Rule
+	if output, err = engine1.EvalRule("data.test.message"); err != nil {
+		fmt.Fprintf(os.Stderr, "error: %v\n", err)
+		os.Exit(1)
+	}
+
+	fmt.Printf("%s\n", output)
+
+	// Print pretty coverage report.
+	if output, err = engine1.GetCoverageReportPretty(); err != nil {
+		fmt.Fprintf(os.Stderr, "error: %v\n", err)
+		os.Exit(1)
+	}
+
+	fmt.Printf("%s\n", output)
 }
diff --git a/bindings/go/pkg/regorus/mod.go b/bindings/go/pkg/regorus/mod.go
index e1529ef5..55d7da6e 100644
--- a/bindings/go/pkg/regorus/mod.go
+++ b/bindings/go/pkg/regorus/mod.go
@@ -28,7 +28,7 @@ func (e *Engine) Clone() *Engine {
 	return c
 }
 
-func (e *Engine) AddPolicy(path string, rego string) error {
+func (e *Engine) AddPolicy(path string, rego string) (string, error) {
 	path_c := C.CString(path)
 	defer C.free(unsafe.Pointer(path_c))
 
@@ -38,21 +38,21 @@ func (e *Engine) AddPolicy(path string, rego string) error {
 	result := C.regorus_engine_add_policy(e.e, path_c, rego_c)
 	defer C.regorus_result_drop(result)
 	if result.status != C.RegorusStatusOk {
-		return fmt.Errorf("%s", C.GoString(result.error_message))
+		return "", fmt.Errorf("%s", C.GoString(result.error_message))
 	}
-	return nil
+	return C.GoString(result.output), nil
 }
 
-func (e *Engine) AddPolicyFromFile(path string) error {
+func (e *Engine) AddPolicyFromFile(path string) (string, error) {
 	path_c := C.CString(path)
 	defer C.free(unsafe.Pointer(path_c))
 
 	result := C.regorus_engine_add_policy_from_file(e.e, path_c)
 	defer C.regorus_result_drop(result)
 	if result.status != C.RegorusStatusOk {
-		return fmt.Errorf("%s", C.GoString(result.error_message))
+		return "", fmt.Errorf("%s", C.GoString(result.error_message))
 	}
-	return nil
+	return C.GoString(result.output), nil
 }
 
 func (e *Engine) AddDataJson(data string) error {
@@ -115,3 +115,73 @@ func (e *Engine) EvalQuery(query string) (string, error) {
 
 	return C.GoString(result.output), nil
 }
+
+func (e *Engine) EvalRule(rule string) (string, error) {
+	rule_c := C.CString(rule)
+	defer C.free(unsafe.Pointer(rule_c))
+
+	result := C.regorus_engine_eval_rule(e.e, rule_c)
+	defer C.regorus_result_drop(result)
+	if result.status != C.RegorusStatusOk {
+		return "", fmt.Errorf("%s", C.GoString(result.error_message))
+	}
+
+	return C.GoString(result.output), nil
+}
+
+func (e *Engine) SetEnableCoverage(enable bool) error {
+	result := C.regorus_engine_set_enable_coverage(e.e, C.bool(enable))
+	defer C.regorus_result_drop(result)
+	if result.status != C.RegorusStatusOk {
+		return fmt.Errorf("%s", C.GoString(result.error_message))
+	}
+	return nil
+}
+
+func (e *Engine) ClearCoverageData() error {
+	result := C.regorus_engine_clear_coverage_data(e.e)
+	defer C.regorus_result_drop(result)
+	if result.status != C.RegorusStatusOk {
+		return fmt.Errorf("%s", C.GoString(result.error_message))
+	}
+	return nil
+}
+
+func (e *Engine) GetCoverageReport() (string, error) {
+	result := C.regorus_engine_get_coverage_report(e.e)
+	defer C.regorus_result_drop(result)
+	if result.status != C.RegorusStatusOk {
+		return "", fmt.Errorf("%s", C.GoString(result.error_message))
+	}
+
+	return C.GoString(result.output), nil
+}
+
+func (e *Engine) GetCoverageReportPretty() (string, error) {
+	result := C.regorus_engine_get_coverage_report_pretty(e.e)
+	defer C.regorus_result_drop(result)
+	if result.status != C.RegorusStatusOk {
+		return "", fmt.Errorf("%s", C.GoString(result.error_message))
+	}
+
+	return C.GoString(result.output), nil
+}
+
+func (e *Engine) SetGatherPrints(b bool) error {
+	result := C.regorus_engine_set_gather_prints(e.e, C.bool(b))
+	defer C.regorus_result_drop(result)
+	if result.status != C.RegorusStatusOk {
+		return fmt.Errorf("%s", C.GoString(result.error_message))
+	}
+	return nil
+}
+
+func (e *Engine) TakePrints() (string, error) {
+	result := C.regorus_engine_take_prints(e.e)
+	defer C.regorus_result_drop(result)
+	if result.status != C.RegorusStatusOk {
+		return "", fmt.Errorf("%s", C.GoString(result.error_message))
+	}
+
+	return C.GoString(result.output), nil
+}
diff --git a/bindings/java/Cargo.toml b/bindings/java/Cargo.toml
index 33b1840f..272cfac5 100644
--- a/bindings/java/Cargo.toml
+++ b/bindings/java/Cargo.toml
@@ -10,8 +10,11 @@ keywords = ["interpreter", "opa", "policy-as-code", "rego"]
 [lib]
 crate-type = ["cdylib"]
 
+[features]
+default = ["regorus/std", "regorus/full-opa"]
+
 [dependencies]
 anyhow = "1.0"
 serde_json = "1.0.112"
 jni = "0.21.1"
-regorus = { path = "../.." }
+regorus = { path  = "../..", default-features = false, features = ["arc"] }
diff --git a/bindings/java/Test.java b/bindings/java/Test.java
index c36e7fb6..3940f013 100644
--- a/bindings/java/Test.java
+++ b/bindings/java/Test.java
@@ -6,15 +6,31 @@
 public class Test {
     public static void main(String[] args) {
         try (Engine engine = new Engine()) {
-            engine.addPolicy(
+            String pkg = engine.addPolicy(
                 "hello.rego",
-                "package test\nmessage = concat(\", \", [input.message, data.message])"
+                "package test\nx=1\nmessage = concat(\", \", [input.message, data.message])"
             );
+	    System.out.println("Loaded package " + pkg);
+
+	    
             engine.addDataJson("{\"message\":\"World!\"}");
             engine.setInputJson("{\"message\":\"Hello\"}");
-            String resJson = engine.evalQuery("data.test.message");
 
+	    // Evaluate query.
+	    String resJson = engine.evalQuery("data.test.message");
             System.out.println(resJson);
+
+	    // Enable coverage.
+	    engine.setEnableCoverage(true);
+
+	    // Evaluate rule.
+	    String valueJson = engine.evalRule("data.test.message");
+            System.out.println(valueJson);
+
+	    String coverageJson = engine.getCoverageReport();
+	    System.out.println(coverageJson);
+
+	    System.out.println(engine.getCoverageReportPretty());
         }
     }
 }
diff --git a/bindings/java/com_microsoft_regorus_Engine.h b/bindings/java/com_microsoft_regorus_Engine.h
index 86f7e2f1..ec507880 100644
--- a/bindings/java/com_microsoft_regorus_Engine.h
+++ b/bindings/java/com_microsoft_regorus_Engine.h
@@ -15,20 +15,28 @@ extern "C" {
 JNIEXPORT jlong JNICALL Java_com_microsoft_regorus_Engine_nativeNewEngine
   (JNIEnv *, jclass);
 
+/*
+ * Class:     com_microsoft_regorus_Engine
+ * Method:    nativeClone
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_com_microsoft_regorus_Engine_nativeClone
+  (JNIEnv *, jclass, jlong);
+
 /*
  * Class:     com_microsoft_regorus_Engine
  * Method:    nativeAddPolicy
- * Signature: (JLjava/lang/String;Ljava/lang/String;)V
+ * Signature: (JLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;
  */
-JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeAddPolicy
+JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeAddPolicy
   (JNIEnv *, jclass, jlong, jstring, jstring);
 
 /*
  * Class:     com_microsoft_regorus_Engine
  * Method:    nativeAddPolicyFromFile
- * Signature: (JLjava/lang/String;)V
+ * Signature: (JLjava/lang/String;)Ljava/lang/String;
  */
-JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeAddPolicyFromFile
+JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeAddPolicyFromFile
   (JNIEnv *, jclass, jlong, jstring);
 
 /*
@@ -79,6 +87,62 @@ JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeSetInputJsonFromF
 JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeEvalQuery
   (JNIEnv *, jclass, jlong, jstring);
 
+/*
+ * Class:     com_microsoft_regorus_Engine
+ * Method:    nativeEvalRule
+ * Signature: (JLjava/lang/String;)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeEvalRule
+  (JNIEnv *, jclass, jlong, jstring);
+
+/*
+ * Class:     com_microsoft_regorus_Engine
+ * Method:    nativeSetEnableCoverage
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeSetEnableCoverage
+  (JNIEnv *, jclass, jlong, jboolean);
+
+/*
+ * Class:     com_microsoft_regorus_Engine
+ * Method:    nativeGetCoverageReport
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeGetCoverageReport
+  (JNIEnv *, jclass, jlong);
+
+/*
+ * Class:     com_microsoft_regorus_Engine
+ * Method:    nativeGetCoverageReportAsColoredString
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeGetCoverageReportAsColoredString
+  (JNIEnv *, jclass, jlong);
+
+/*
+ * Class:     com_microsoft_regorus_Engine
+ * Method:    nativeClearCoverageData
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeClearCoverageData
+  (JNIEnv *, jclass, jlong);
+
+/*
+ * Class:     com_microsoft_regorus_Engine
+ * Method:    nativeSetGatherPrints
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeSetGatherPrints
+  (JNIEnv *, jclass, jlong, jboolean);
+
+/*
+ * Class:     com_microsoft_regorus_Engine
+ * Method:    nativeTakePrints
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeTakePrints
+  (JNIEnv *, jclass, jlong);
+
 /*
  * Class:     com_microsoft_regorus_Engine
  * Method:    nativeDestroyEngine
diff --git a/bindings/java/src/lib.rs b/bindings/java/src/lib.rs
index 304ae71c..f09b07b2 100644
--- a/bindings/java/src/lib.rs
+++ b/bindings/java/src/lib.rs
@@ -17,6 +17,17 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeNewEngine(
     Box::into_raw(Box::new(engine)) as jlong
 }
 
+#[no_mangle]
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClone(
+    _env: JNIEnv,
+    _class: JClass,
+    engine_ptr: jlong,
+) -> jlong {
+    let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
+    let c = engine.clone();
+    Box::into_raw(Box::new(c)) as jlong
+}
+
 #[no_mangle]
 pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicy(
     env: JNIEnv,
@@ -24,14 +35,19 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicy(
     engine_ptr: jlong,
     path: JString,
     rego: JString,
-) {
-    let _ = throw_err(env, |env| {
+) -> jstring {
+    let res = throw_err(env, |env| {
         let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
         let path: String = env.get_string(&path)?.into();
         let rego: String = env.get_string(&rego)?.into();
-        engine.add_policy(path, rego)?;
-        Ok(())
+        let pkg = env.new_string(engine.add_policy(path, rego)?)?;
+        Ok(pkg.into_raw())
     });
+
+    match res {
+        Ok(val) => val,
+        Err(_) => JObject::null().into_raw(),
+    }
 }
 
 #[no_mangle]
@@ -40,13 +56,37 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicyFromFile
     _class: JClass,
     engine_ptr: jlong,
     path: JString,
-) {
-    let _ = throw_err(env, |env| {
+) -> jstring {
+    let res = throw_err(env, |env| {
         let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
         let path: String = env.get_string(&path)?.into();
-        engine.add_policy_from_file(path)?;
-        Ok(())
+        let pkg = env.new_string(engine.add_policy_from_file(path)?)?;
+        Ok(pkg.into_raw())
     });
+
+    match res {
+        Ok(val) => val,
+        Err(_) => JObject::null().into_raw(),
+    }
+}
+
+#[no_mangle]
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetPackages(
+    env: JNIEnv,
+    _class: JClass,
+    engine_ptr: jlong,
+) -> jstring {
+    let res = throw_err(env, |env| {
+        let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
+        let packages = engine.get_packages()?;
+        let packages_json = env.new_string(serde_json::to_string_pretty(&packages)?)?;
+        Ok(packages_json.into_raw())
+    });
+
+    match res {
+        Ok(val) => val,
+        Err(_) => JObject::null().into_raw(),
+    }
 }
 
 #[no_mangle]
@@ -117,7 +157,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetInputJsonFromF
     let _ = throw_err(env, |env| {
         let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
         let path: String = env.get_string(&path)?.into();
-        engine.set_input(Value::from_json_file(&path)?);
+        engine.set_input(Value::from_json_file(path)?);
         Ok(())
     });
 }
@@ -144,12 +184,133 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeEvalQuery(
 }
 
 #[no_mangle]
-pub unsafe extern "system" fn Java_com_microsoft_regorus_Engine_nativeDestroyEngine(
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeEvalRule(
+    env: JNIEnv,
+    _class: JClass,
+    engine_ptr: jlong,
+    rule: JString,
+) -> jstring {
+    let res = throw_err(env, |env| {
+        let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
+        let rule: String = env.get_string(&rule)?.into();
+        let value = engine.eval_rule(rule)?;
+        let output = env.new_string(value.to_json_str()?)?;
+        Ok(output.into_raw())
+    });
+
+    match res {
+        Ok(val) => val,
+        Err(_) => JObject::null().into_raw(),
+    }
+}
+
+#[no_mangle]
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetEnableCoverage(
+    env: JNIEnv,
+    _class: JClass,
+    engine_ptr: jlong,
+    enable: bool,
+) {
+    let _ = throw_err(env, |_| {
+        let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
+        engine.set_enable_coverage(enable);
+        Ok(())
+    });
+}
+
+#[no_mangle]
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetCoverageReport(
+    env: JNIEnv,
+    _class: JClass,
+    engine_ptr: jlong,
+) -> jstring {
+    let res = throw_err(env, |env| {
+        let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
+        let report = engine.get_coverage_report()?;
+        let output = env.new_string(serde_json::to_string_pretty(&report)?)?;
+        Ok(output.into_raw())
+    });
+
+    match res {
+        Ok(val) => val,
+        Err(_) => JObject::null().into_raw(),
+    }
+}
+
+#[no_mangle]
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeGetCoverageReportPretty(
+    env: JNIEnv,
+    _class: JClass,
+    engine_ptr: jlong,
+) -> jstring {
+    let res = throw_err(env, |env| {
+        let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
+        let report = engine.get_coverage_report()?.to_string_pretty()?;
+        let output = env.new_string(&report)?;
+        Ok(output.into_raw())
+    });
+
+    match res {
+        Ok(val) => val,
+        Err(_) => JObject::null().into_raw(),
+    }
+}
+
+#[no_mangle]
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearCoverageData(
+    env: JNIEnv,
+    _class: JClass,
+    engine_ptr: jlong,
+) {
+    let _ = throw_err(env, |_| {
+        let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
+        engine.clear_coverage_data();
+        Ok(())
+    });
+}
+
+#[no_mangle]
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetGatherPrints(
+    env: JNIEnv,
+    _class: JClass,
+    engine_ptr: jlong,
+    b: bool,
+) {
+    let _ = throw_err(env, |_| {
+        let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
+        engine.set_gather_prints(b);
+        Ok(())
+    });
+}
+
+#[no_mangle]
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeTakePrints(
+    env: JNIEnv,
+    _class: JClass,
+    engine_ptr: jlong,
+) -> jstring {
+    let res = throw_err(env, |env| {
+        let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
+        let prints = engine.take_prints()?;
+        let output = env.new_string(serde_json::to_string_pretty(&prints)?)?;
+        Ok(output.into_raw())
+    });
+
+    match res {
+        Ok(val) => val,
+        Err(_) => JObject::null().into_raw(),
+    }
+}
+
+#[no_mangle]
+pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeDestroyEngine(
     _env: JNIEnv,
     _class: JClass,
     engine_ptr: jlong,
 ) {
-    let _engine = Box::from_raw(engine_ptr as *mut Engine);
+    unsafe {
+        let _engine = Box::from_raw(engine_ptr as *mut Engine);
+    }
 }
 
 fn throw_err<T>(mut env: JNIEnv, mut f: impl FnMut(&mut JNIEnv) -> Result<T>) -> Result<T> {
diff --git a/bindings/java/src/main/java/com/microsoft/regorus/Engine.java b/bindings/java/src/main/java/com/microsoft/regorus/Engine.java
index 11fc0724..dc8c2373 100644
--- a/bindings/java/src/main/java/com/microsoft/regorus/Engine.java
+++ b/bindings/java/src/main/java/com/microsoft/regorus/Engine.java
@@ -16,20 +16,29 @@
 /**
  * Regorus Engine.
  */
-public class Engine implements AutoCloseable {
+public class Engine implements AutoCloseable, Cloneable {
     // Methods exposed from Rust side, you can run
     // `javac -h . src/main/java/com/microsoft/regorus/Engine.java` to update
     // expected native header at `bindings/java/com_microsoft_regorus_Engine.h`
     // if you update the native API.
     private static native long nativeNewEngine();
-    private static native void nativeAddPolicy(long enginePtr, String path, String rego);
-    private static native void nativeAddPolicyFromFile(long enginePtr, String path);
+    private static native long nativeClone(long enginePtr);
+    private static native String nativeAddPolicy(long enginePtr, String path, String rego);
+    private static native String nativeAddPolicyFromFile(long enginePtr, String path);
+    private static native String nativeGetPackages(long enginePtr);
     private static native void nativeClearData(long enginePtr);
     private static native void nativeAddDataJson(long enginePtr, String data);
     private static native void nativeAddDataJsonFromFile(long enginePtr, String path);
     private static native void nativeSetInputJson(long enginePtr, String input);
     private static native void nativeSetInputJsonFromFile(long enginePtr, String path);
     private static native String nativeEvalQuery(long enginePtr, String query);
+    private static native String nativeEvalRule(long enginePtr, String qrule);
+    private static native void nativeSetEnableCoverage(long enginePtr, boolean enable);
+    private static native String nativeGetCoverageReport(long enginePtr);
+    private static native String nativeGetCoverageReportPretty(long enginePtr);
+    private static native void nativeClearCoverageData(long enginePtr);
+    private static native void nativeSetGatherPrints(long enginePtr, boolean b);
+    private static native String nativeTakePrints(long enginePtr);
     private static native void nativeDestroyEngine(long enginePtr);
 
     // Pointer to Engine allocated on Rust's heap, all native methods works on
@@ -43,25 +52,50 @@ public Engine() {
         enginePtr = nativeNewEngine();
     }
 
+    
+    Engine(long ptr) {
+	enginePtr = ptr;
+    }
+
+    /**
+     * Efficiently clones an Engine.
+     */
+    public Engine Clone() {
+	return new Engine(nativeClone(enginePtr));
+    }
+    
     /**
      * Adds an inline Rego policy.
      * 
      * @param filename Filename of this Rego policy.
      * @param rego     Rego policy.
+     * 
+     * @return Rego package defined in the policy.
      */
-    public void addPolicy(String filename, String rego) {
-        nativeAddPolicy(enginePtr, filename, rego);
+    public String addPolicy(String filename, String rego) {
+        return nativeAddPolicy(enginePtr, filename, rego);
     }
 
     /**
      * Adds a Rego policy from given path.
      * 
      * @param path Path of the Rego policy.
+     * 
+     * @return Rego package defined in the policy.
      */
-    public void addPolicyFromFile(String path) {
-        nativeAddPolicyFromFile(enginePtr, path);
+    public String addPolicyFromFile(String path) {
+        return nativeAddPolicyFromFile(enginePtr, path);
     }
 
+    /**
+     * Get list of loaded Rego packages.
+     * 
+     * @return List of Rego packages as a JSON array of strings.
+     */
+    public String getPackages() {
+        return nativeGetPackages(enginePtr);
+    }
+    
     /**
      * Clears the data  document.
      */
@@ -137,6 +171,70 @@ public String evalQuery(String query) {
         return nativeEvalQuery(enginePtr, query);
     }
     
+    /**
+     * Evaluates given Rego rule and returns a JSON string as a result.
+     * 
+     * @param rule Path of the  Rego rule.
+     * 
+     * @return Value of the rule as a JSON string.
+     */
+    public String evalRule(String rule) {
+        return nativeEvalRule(enginePtr, rule);
+    }
+    
+    /**
+     * Enable/disable coverage.
+     * 
+     * @param enable Whether to enable coverage or not.
+     * 
+     */
+    public void setEnableCoverage(boolean enable) {
+        nativeSetEnableCoverage(enginePtr, enable);
+    }
+    
+    /**
+     * Clear coverage data.
+     * 
+     */
+    public void clearCoverageData() {
+        nativeClearCoverageData(enginePtr);
+    }
+    
+    /**
+     * Get coverage report as json string.
+     * 
+     */
+    public String getCoverageReport() {
+        return nativeGetCoverageReport(enginePtr);
+    }
+    
+    /**
+     * Get coverage report as ANSI color coded string.
+     * 
+     */
+    public String getCoverageReportPretty() {
+        return nativeGetCoverageReportPretty(enginePtr);
+    }
+    
+    /**
+     * Enable/disable gathering prints.
+     * 
+     * @param b Whether to gather prints or not.
+     * 
+     */
+    public void setGatherPrints(boolean b) {
+        nativeSetGatherPrints(enginePtr, b);
+    }
+    
+    /**
+     * Take gathered prints.
+     * 
+     */
+    public String takePrints() {
+        return nativeTakePrints(enginePtr);
+    }
+
+    
     @Override
     public void close() {
         nativeDestroyEngine(enginePtr);
diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml
index d62f688d..33875dc2 100644
--- a/bindings/python/Cargo.toml
+++ b/bindings/python/Cargo.toml
@@ -11,10 +11,13 @@ keywords = ["interpreter", "opa", "policy-as-code", "rego"]
 [lib]
 crate-type = ["cdylib"]
 
+[features]
+default = ["regorus/std", "regorus/full-opa"]
+
 [dependencies]
 anyhow = "1.0"
 ordered-float = "4.2.0"
 pyo3 = {version = "0.21.0", features = ["anyhow", "extension-module"] }
-regorus = { path  = "../.." }
+regorus = { path  = "../..", default-features = false, features = ["arc"] }
 serde_json = "1.0.112"
 
diff --git a/bindings/python/README.md b/bindings/python/README.md
index 2e91aa25..20e31b93 100644
--- a/bindings/python/README.md
+++ b/bindings/python/README.md
@@ -10,7 +10,7 @@ Regorus can be used in Python via `regorus` package. (It is not yet available in
 
 See [Repository](https://github.com/microsoft/regorus).
 
-To build this binding, see [building](https://github.com/microsoft/regorus/bindings/python/building.md)
+To build this binding, see [building](https://github.com/microsoft/regorus/blob/main/bindings/python/building.md)
 
 ## Usage
 ```Python
@@ -54,14 +54,11 @@ input = {
 }
 engine.set_input(input)
 
-# Eval query
-results = engine.eval_query('data.framework.mount_overlay=x')
+# Eval rule
+value = engine.eval_rule('data.framework.mount_overlay')
 
-# Print results
-print(results['result'][0])
+# Print value
+print(value)
 
-# Eval query as json
-results_json = engine.eval_query_as_json('data.framework.mount_overlay=x')
-print(results_json)
 ```
 
diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs
index 4502cddc..0766633f 100644
--- a/bindings/python/src/lib.rs
+++ b/bindings/python/src/lib.rs
@@ -21,19 +21,7 @@ impl Default for Engine {
     }
 }
 
-impl Clone for Engine {
-    /// Clone a [`Engine`]
-    ///
-    /// To avoid having to parse same policy again, the engine can be cloned
-    /// after policies and data have been added.
-    fn clone(&self) -> Self {
-        Self {
-            engine: self.engine.clone(),
-        }
-    }
-}
-
-fn from<'source>(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
+fn from(ob: &Bound<'_, PyAny>) -> Result<Value, PyErr> {
     // dicts
     Ok(if let Ok(dict) = ob.downcast::<PyDict>() {
         let mut map = BTreeMap::new();
@@ -141,7 +129,7 @@ fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
 
         Value::Array(_) => {
             let list = PyList::empty_bound(py);
-            for v in std::mem::replace(v.as_array_mut()?, Vec::new()) {
+            for v in std::mem::take(v.as_array_mut()?) {
                 list.append(to(v, py)?)?;
             }
             list.into()
@@ -149,7 +137,7 @@ fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
 
         Value::Set(_) => {
             let set = PySet::empty_bound(py)?;
-            for v in std::mem::replace(v.as_set_mut()?, BTreeSet::new()) {
+            for v in std::mem::take(v.as_set_mut()?) {
                 set.add(to(v, py)?)?;
             }
             set.into()
@@ -157,7 +145,7 @@ fn to(mut v: Value, py: Python<'_>) -> Result<PyObject> {
 
         Value::Object(_) => {
             let dict = PyDict::new_bound(py);
-            for (k, v) in std::mem::replace(v.as_object_mut()?, BTreeMap::new()) {
+            for (k, v) in std::mem::take(v.as_object_mut()?) {
                 dict.set_item(to(k, py)?, to(v, py)?)?;
             }
             dict.into()
@@ -221,7 +209,7 @@ impl Engine {
     ///
     /// * `path`: Path to JSON policy data.
     pub fn add_data_from_json_file(&mut self, path: String) -> Result<()> {
-        let data = Value::from_json_file(&path)?;
+        let data = Value::from_json_file(path)?;
         self.engine.add_data(data)
     }
 
@@ -254,7 +242,7 @@ impl Engine {
     ///
     /// * `path`: Path to JSON input data.
     pub fn set_input_from_json_file(&mut self, path: String) -> Result<()> {
-        let input = Value::from_json_file(&path)?;
+        let input = Value::from_json_file(path)?;
         self.engine.set_input(input);
         Ok(())
     }
@@ -299,6 +287,69 @@ impl Engine {
         let results = self.engine.eval_query(query, false)?;
         serde_json::to_string_pretty(&results).map_err(|e| anyhow!("{e}"))
     }
+
+    /// Evaluate rule.
+    ///
+    /// * `rule`: Full path to the rule.
+    pub fn eval_rule(&mut self, rule: String, py: Python<'_>) -> Result<PyObject> {
+        to(self.engine.eval_rule(rule)?, py)
+    }
+
+    /// Evaluate rule and return value as json.
+    ///
+    /// * `rule`: Full path to the rule.
+    pub fn eval_rule_as_json(&mut self, rule: String) -> Result<String> {
+        let v = self.engine.eval_rule(rule)?;
+        v.to_json_str()
+    }
+
+    /// Enable code coverage
+    ///
+    /// * `enable`: Whether to enable coverage or not.
+    pub fn set_enable_coverage(&mut self, enable: bool) {
+        self.engine.set_enable_coverage(enable)
+    }
+
+    /// Get coverage report as json.
+    ///
+    pub fn get_coverage_report_as_json(&self) -> Result<String> {
+        let report = self.engine.get_coverage_report()?;
+        serde_json::to_string_pretty(&report).map_err(|e| anyhow!("{e}"))
+    }
+
+    /// Get coverage report as pretty printable string.
+    ///
+    pub fn get_coverage_report_pretty(&self) -> Result<String> {
+        self.engine.get_coverage_report()?.to_string_pretty()
+    }
+
+    /// Clear coverage data.
+    ///
+    pub fn clear_coverage_data(&mut self) {
+        self.engine.clear_coverage_data();
+    }
+
+    /// Gather print statements instead of printing to stderr.
+    ///
+    pub fn set_gather_prints(&mut self, b: bool) {
+        self.engine.set_gather_prints(b)
+    }
+
+    /// Take gathered prints.
+    ///
+    pub fn take_prints(&mut self) -> Result<Vec<String>> {
+        self.engine.take_prints()
+    }
+
+    /// Clone a [`Engine`]
+    ///
+    /// To avoid having to parse same policy again, the engine can be cloned
+    /// after policies and data have been added.
+    fn clone(&self) -> Self {
+        Self {
+            engine: self.engine.clone(),
+        }
+    }
 }
 
 #[pymodule]
diff --git a/bindings/ruby/ext/regorusrb/Cargo.toml b/bindings/ruby/ext/regorusrb/Cargo.toml
index b6888c77..c9cd25d7 100644
--- a/bindings/ruby/ext/regorusrb/Cargo.toml
+++ b/bindings/ruby/ext/regorusrb/Cargo.toml
@@ -9,8 +9,11 @@ publish = false
 crate-type = ["cdylib"]
 path = "src/lib.rs"
 
+[features]
+default = ["regorus/std", "regorus/full-opa"]
+
 [dependencies]
 magnus = { version = "0.6.4" }
-regorus = { git = "https://github.com/microsoft/regorus" }
+regorus = { git = "https://github.com/microsoft/regorus", default-features = false, features = ["arc"] }
 serde_json = "1.0.117"
 serde_magnus = "0.8.1"
diff --git a/bindings/ruby/ext/regorusrb/src/lib.rs b/bindings/ruby/ext/regorusrb/src/lib.rs
index 6888e489..27dfcb36 100644
--- a/bindings/ruby/ext/regorusrb/src/lib.rs
+++ b/bindings/ruby/ext/regorusrb/src/lib.rs
@@ -72,7 +72,7 @@ impl Engine {
     }
 
     fn add_data_from_json_file(&self, path: String) -> Result<(), Error> {
-        let json_data = regorus::Value::from_json_file(&path).map_err(|e| {
+        let json_data = regorus::Value::from_json_file(path).map_err(|e| {
             Error::new(
                 runtime_error(),
                 format!("Failed to parse JSON data file: {}", e),
@@ -112,7 +112,7 @@ impl Engine {
     }
 
     fn add_input_from_json_file(&self, path: String) -> Result<(), Error> {
-        let json_data = regorus::Value::from_json_file(&path).map_err(|e| {
+        let json_data = regorus::Value::from_json_file(path).map_err(|e| {
             Error::new(
                 runtime_error(),
                 format!("Failed to parse JSON input file: {}", e),
@@ -193,7 +193,8 @@ impl Engine {
     }
 
     fn set_enable_coverage(&self, enable: bool) -> Result<(), Error> {
-        Ok(self.engine.borrow_mut().set_enable_coverage(enable))
+        self.engine.borrow_mut().set_enable_coverage(enable);
+        Ok(())
     }
 
     fn get_coverage_report_as_json(&self) -> Result<String, Error> {
@@ -237,12 +238,14 @@ impl Engine {
     }
 
     fn clear_coverage_data(&self) -> Result<(), Error> {
-        Ok(self.engine.borrow_mut().clear_coverage_data())
+        self.engine.borrow_mut().clear_coverage_data();
+        Ok(())
     }
 
     // Print statements can be gathered async instead of printing to stderr
     fn set_gather_prints(&self, enable: bool) -> Result<(), Error> {
-        Ok(self.engine.borrow_mut().set_gather_prints(enable))
+        self.engine.borrow_mut().set_gather_prints(enable);
+        Ok(())
     }
 
     fn take_prints(&self) -> Result<Vec<String>, Error> {
diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml
index ba588e03..6ff3a14d 100644
--- a/bindings/wasm/Cargo.toml
+++ b/bindings/wasm/Cargo.toml
@@ -10,8 +10,11 @@ keywords = ["interpreter", "opa", "policy-as-code", "rego"]
 [lib]
 crate-type = ["cdylib"]
 
+[features]
+default = ["regorus/std", "regorus/full-opa"]
+
 [dependencies]
-regorus = { path  = "../.." }
+regorus = { path  = "../..", default-features = false, features = ["arc"] }
 serde_json = "1.0.111"
 wasm-bindgen = "0.2.90"
 
diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md
index fb3e8703..902b01f3 100644
--- a/bindings/wasm/README.md
+++ b/bindings/wasm/README.md
@@ -10,72 +10,10 @@
 
 See [Repository](https://github.com/microsoft/regorus).
 
-To build this binding, see [building](https://github.com/microsoft/regorus/bindings/wasm/building.md)
+To build this binding, see [building.md](https://github.com/microsoft/regorus/blob/main/bindings/wasm/building.md)
 
 
 
 ## Usage
 
-```javascript
-
-var regorus = require('regorusjs')
-
-// Create an engine.
-var engine = new regorus.Engine();
-
-// Add Rego policy.
-engine.add_policy(
-    // Associate this file name with policy
-    'hello.rego',
-    
-    // Rego policy
-`
-  package test
-  
-  # Join messages
-  message = concat(", ", [input.message, data.message])
-`)
-
-// Set policy data
-engine.add_data_json(`
- {
-    "message" : "World!"
- }
-`)
-
-// Set policy input
-engine.set_input_json(`
- {
-	"message" : "Hello"
- }
-`)
-
-// Eval query
-results = engine.eval_query('data.test.message')
-
-// Display
-console.log(results)
-// {
-//   "result": [
-//     {
-//       "expressions": [
-//         {
-//           "value": "Hello, World!",
-//           "text": "data.test.message",
-//           "location": {
-//             "row": 1,
-//             "col": 1
-//           }
-//         }
-//       ]
-//     }
-//   ]
-// }
-
-// Convert results to object
-results = JSON.parse(results)
-
-// Process result
-console.log(results.result[0].expressions[0].value)
-// Hello, World!
-```
+See [test.js](https://github.com/microsoft/regorus/blob/main/bindings/wasm/test.js) for example usage.
diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs
index 397bb74f..0d09448e 100644
--- a/bindings/wasm/src/lib.rs
+++ b/bindings/wasm/src/lib.rs
@@ -1,6 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
+#![allow(non_snake_case)]
+
 use wasm_bindgen::prelude::*;
 
 #[wasm_bindgen]
@@ -50,7 +52,7 @@ impl Engine {
     ///
     /// * `path`: A filename to be associated with the policy.
     /// * `rego`: Rego policy.
-    pub fn add_policy(&mut self, path: String, rego: String) -> Result<String, JsValue> {
+    pub fn addPolicy(&mut self, path: String, rego: String) -> Result<String, JsValue> {
         self.engine.add_policy(path, rego).map_err(error_to_jsvalue)
     }
 
@@ -58,15 +60,22 @@ impl Engine {
     ///
     /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.add_data
     /// * `data`: JSON encoded value to be used as policy data.
-    pub fn add_data_json(&mut self, data: String) -> Result<(), JsValue> {
+    pub fn addDataJson(&mut self, data: String) -> Result<(), JsValue> {
         let data = regorus::Value::from_json_str(&data).map_err(error_to_jsvalue)?;
         self.engine.add_data(data).map_err(error_to_jsvalue)
     }
 
+    /// Get the list of packages defined by loaded policies.
+    ///
+    /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_packages
+    pub fn getPackages(&self) -> Result<Vec<String>, JsValue> {
+        self.engine.get_packages().map_err(error_to_jsvalue)
+    }
+
     /// Clear policy data.
     ///
     /// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.clear_data
-    pub fn clear_data(&mut self) -> Result<(), JsValue> {
+    pub fn clearData(&mut self) -> Result<(), JsValue> {
         self.engine.clear_data();
         Ok(())
     }
@@ -75,7 +84,7 @@ impl Engine {
     ///
     /// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.set_input
     /// * `input`: JSON encoded value to be used as input to query.
-    pub fn set_input_json(&mut self, input: String) -> Result<(), JsValue> {
+    pub fn setInputJson(&mut self, input: String) -> Result<(), JsValue> {
         let input = regorus::Value::from_json_str(&input).map_err(error_to_jsvalue)?;
         self.engine.set_input(input);
         Ok(())
@@ -85,13 +94,75 @@ impl Engine {
     ///
     /// See https://docs.rs/regorus/0.1.0-alpha.2/regorus/struct.Engine.html#method.eval_query
     /// * `query`: Rego expression to be evaluate.
-    pub fn eval_query(&mut self, query: String) -> Result<String, JsValue> {
+    pub fn evalQuery(&mut self, query: String) -> Result<String, JsValue> {
         let results = self
             .engine
             .eval_query(query, false)
             .map_err(error_to_jsvalue)?;
         serde_json::to_string_pretty(&results).map_err(error_to_jsvalue)
     }
+
+    /// Evaluate rule(s) at given path.
+    ///
+    /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.eval_rule
+    ///
+    /// * `path`: The full path to the rule(s).
+    pub fn evalRule(&mut self, path: String) -> Result<String, JsValue> {
+        let v = self.engine.eval_rule(path).map_err(error_to_jsvalue)?;
+        v.to_json_str().map_err(error_to_jsvalue)
+    }
+
+    /// Gather output from print statements instead of emiting to stderr.
+    ///
+    /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_gather_prints
+    /// * `b`: Whether to enable gathering prints or not.
+    pub fn setGatherPrints(&mut self, b: bool) {
+        self.engine.set_gather_prints(b)
+    }
+
+    /// Take the gathered output of print statements.
+    ///
+    /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.take_prints
+    pub fn takePrints(&mut self) -> Result<Vec<String>, JsValue> {
+        self.engine.take_prints().map_err(error_to_jsvalue)
+    }
+
+    /// Enable/disable policy coverage.
+    ///
+    /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.set_enable_coverage
+    /// * `b`: Whether to enable gathering coverage or not.
+    pub fn setEnableCoverage(&mut self, enable: bool) {
+        self.engine.set_enable_coverage(enable)
+    }
+
+    /// Get the coverage report as json.
+    ///
+    /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.get_coverage_report
+    pub fn getCoverageReport(&self) -> Result<String, JsValue> {
+        let report = self
+            .engine
+            .get_coverage_report()
+            .map_err(error_to_jsvalue)?;
+        serde_json::to_string_pretty(&report).map_err(error_to_jsvalue)
+    }
+
+    /// Clear gathered coverage data.
+    ///
+    /// See https://docs.rs/regorus/latest/regorus/struct.Engine.html#method.clear_coverage_data
+    pub fn clearCoverageData(&mut self) {
+        self.engine.clear_coverage_data()
+    }
+
+    /// Get ANSI color coded coverage report.
+    ///
+    /// See https://docs.rs/regorus/latest/regorus/coverage/struct.Report.html#method.to_string_pretty
+    pub fn getCoverageReportPretty(&self) -> Result<String, JsValue> {
+        let report = self
+            .engine
+            .get_coverage_report()
+            .map_err(error_to_jsvalue)?;
+        report.to_string_pretty().map_err(error_to_jsvalue)
+    }
 }
 
 #[cfg(test)]
@@ -102,9 +173,10 @@ mod tests {
     #[wasm_bindgen_test]
     pub fn basic() -> Result<(), JsValue> {
         let mut engine = crate::Engine::new();
+        engine.setEnableCoverage(true);
 
         // Exercise all APIs.
-        engine.add_data_json(
+        engine.addDataJson(
             r#"
         {
            "foo" : "bar"
@@ -113,7 +185,7 @@ mod tests {
             .to_string(),
         )?;
 
-        engine.set_input_json(
+        engine.setInputJson(
             r#"
         {
            "message" : "Hello"
@@ -122,15 +194,16 @@ mod tests {
             .to_string(),
         )?;
 
-        engine.add_policy(
+        let pkg = engine.addPolicy(
             "hello.rego".to_string(),
             r#"
             package test
             message = input.message"#
                 .to_string(),
         )?;
+        assert_eq!(pkg, "data.test");
 
-        let results = engine.eval_query("data".to_string())?;
+        let results = engine.evalQuery("data".to_string())?;
         let r = regorus::Value::from_json_str(&results).map_err(crate::error_to_jsvalue)?;
 
         let v = &r["result"][0]["expressions"][0]["value"];
@@ -141,6 +214,38 @@ mod tests {
         // Test that data was set.
         assert_eq!(v["foo"], regorus::Value::from("bar"));
 
+        // Use eval_rule to perform same query.
+        let v = engine.evalRule("data.test.message".to_owned())?;
+        let v = regorus::Value::from_json_str(&v).map_err(crate::error_to_jsvalue)?;
+
+        // Ensure that input and policy were evaluated.
+        assert_eq!(v, regorus::Value::from("Hello"));
+
+        let pkgs = engine.getPackages()?;
+        assert_eq!(pkgs, vec!["data.test"]);
+
+        engine.setGatherPrints(true);
+        let _ = engine.evalQuery("print(\"Hello\")".to_owned());
+        let prints = engine.takePrints()?;
+        assert_eq!(prints, vec!["<query.rego>:1: Hello"]);
+
+        // Test clone.
+        let mut engine1 = engine.clone();
+
+        // Test code coverage.
+        let report = engine1.getCoverageReport()?;
+        let r = regorus::Value::from_json_str(&report).map_err(crate::error_to_jsvalue)?;
+
+        assert_eq!(
+            r["files"][0]["covered"]
+                .as_array()
+                .map_err(crate::error_to_jsvalue)?,
+            &vec![regorus::Value::from(3)]
+        );
+
+        println!("{}", engine1.getCoverageReportPretty()?);
+
+        engine1.clearCoverageData();
         Ok(())
     }
 }
diff --git a/examples/regorus.rs b/examples/regorus.rs
index 44989b76..6e046f1b 100644
--- a/examples/regorus.rs
+++ b/examples/regorus.rs
@@ -115,7 +115,7 @@ fn rego_eval(
     #[cfg(feature = "coverage")]
     if coverage {
         let report = engine.get_coverage_report()?;
-        println!("{}", report.to_colored_string()?);
+        println!("{}", report.to_string_pretty()?);
     }
 
     Ok(())
diff --git a/src/engine.rs b/src/engine.rs
index f9b164f8..8df331a2 100644
--- a/src/engine.rs
+++ b/src/engine.rs
@@ -240,7 +240,7 @@ impl Engine {
         &self.modules
     }
 
-    /// Evaluate rule(s) at given path.
+    /// Evaluate specified rule(s).
     ///
     /// [`Engine::eval_rule`] is often faster than [`Engine::eval_query`] and should be preferred if
     /// OPA style [`QueryResults`] are not needed.
@@ -280,10 +280,10 @@ impl Engine {
     /// # Ok(())
     /// # }
     /// ```
-    pub fn eval_rule(&mut self, path: String) -> Result<Value> {
+    pub fn eval_rule(&mut self, rule: String) -> Result<Value> {
         self.prepare_for_eval(false)?;
         self.interpreter.clean_internal_evaluation_state();
-        self.interpreter.eval_rule_in_path(path)
+        self.interpreter.eval_rule_in_path(rule)
     }
 
     /// Evaluate a Rego query.
diff --git a/src/lib.rs b/src/lib.rs
index 16c30d61..58e522f0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -402,7 +402,7 @@ pub mod coverage {
         ///
         /// <img src="https://github.com/microsoft/regorus/blob/main/docs/coverage.png?raw=true">
 
-        pub fn to_colored_string(&self) -> anyhow::Result<String> {
+        pub fn to_string_pretty(&self) -> anyhow::Result<String> {
             let mut s = String::default();
             s.push_str("COVERAGE REPORT:\n");
             for file in self.files.iter() {
@@ -411,7 +411,7 @@ pub mod coverage {
                     continue;
                 }
 
-                s.push_str(&format!("{}:", file.path));
+                s.push_str(&format!("{}:\n", file.path));
                 for (line, code) in file.code.split('\n').enumerate() {
                     let line = line as u32 + 1;
                     if file.not_covered.contains(&line) {


Test patch:
diff --git a/.github/workflows/test-ffi.yml b/.github/workflows/test-ffi.yml
new file mode 100644
index 00000000..991a327b
--- /dev/null
+++ b/.github/workflows/test-ffi.yml
@@ -0,0 +1,22 @@
+name: bindings/c-cpp
+
+on:
+  push:
+    branches: [ "main" ]
+  pull_request:
+    branches: [ "main" ]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+
+      - name: Test FFI
+        run: |
+          cargo build -r
+          cargo clippy --all-targets --no-deps -- -Dwarnings
+        working-directory: ./bindings/ffi
diff --git a/.github/workflows/test-java.yml b/.github/workflows/test-java.yml
index 182287c0..56ff08fc 100644
--- a/.github/workflows/test-java.yml
+++ b/.github/workflows/test-java.yml
@@ -22,7 +22,9 @@ jobs:
       - uses: dtolnay/rust-toolchain@stable
 
       - name: Building binding
-        run: cargo build --release --manifest-path bindings/java/Cargo.toml
+        run: |
+          cargo clippy --all-targets --no-deps -- -Dwarnings
+          cargo build --release --manifest-path bindings/java/Cargo.toml
 
       - name: Build jar
         run: mvn package
diff --git a/.github/workflows/test-python.yml b/.github/workflows/test-python.yml
index 84a434d8..2581e140 100644
--- a/.github/workflows/test-python.yml
+++ b/.github/workflows/test-python.yml
@@ -34,4 +34,5 @@ jobs:
         run:  |
           pip3 install dist/regorus-*.whl
           cd bindings/python
+          cargo clippy --all-targets --no-deps -- -Dwarnings
           python3 test.py          
diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml
index 298e8bc0..2b7e3b76 100644
--- a/.github/workflows/test-ruby.yml
+++ b/.github/workflows/test-ruby.yml
@@ -27,4 +27,5 @@ jobs:
       - name: Run ruby tests
         run:  |
           cd bindings/ruby
+          cargo clippy --all-targets --no-deps -- -Dwarnings
           bundle exec rake
diff --git a/.github/workflows/test-wasm.yml b/.github/workflows/test-wasm.yml
index 1a5e309e..9c2c4a36 100644
--- a/.github/workflows/test-wasm.yml
+++ b/.github/workflows/test-wasm.yml
@@ -26,5 +26,7 @@ jobs:
       - name: Test wasm binding
         run: |
           cd bindings/wasm
+          cargo clippy --all-targets --no-deps -- -Dwarnings
           wasm-pack build --target nodejs --release
+          wasm-pack test --release --node
           node test.js
diff --git a/bindings/python/test.py b/bindings/python/test.py
index 014a77fc..6ea61634 100644
--- a/bindings/python/test.py
+++ b/bindings/python/test.py
@@ -8,13 +8,13 @@
 
 # Load policies
 pkg = engine.add_policy_from_file('../../tests/aci/framework.rego')
-print(' Loaded Policy %s' % pkg)
+print(' Loaded package %s' % pkg)
 
 pkg = engine.add_policy_from_file('../../tests/aci/api.rego')
-print(' Loaded Policy %s' % pkg)
+print(' Loaded package %s' % pkg)
 
 pkg = engine.add_policy_from_file('../../tests/aci/policy.rego')
-print(' Loaded Policy %s' % pkg)
+print(' Loaded package %s' % pkg)
 
 # Add policy data
 data = {
@@ -55,3 +55,40 @@
 # Eval query as json
 results_json = engine.eval_query_as_json('data.framework.mount_overlay=x')
 print(results_json)
+
+# Eval rule
+v = engine.eval_rule('data.framework.mount_overlay')
+print(v)
+
+# Eval rule as json
+v = engine.eval_rule_as_json('data.framework.mount_overlay')
+print(v)
+
+# Enable coverage
+engine.set_enable_coverage(True)
+engine.eval_rule('data.framework.mount_overlay')
+
+# Print coverage
+report_json = engine.get_coverage_report_as_json()
+print(report_json)
+
+# Pretty coverage report
+report = engine.get_coverage_report_pretty()
+print(report)
+
+# Clone engine
+engine1 = engine.clone()
+
+
+# Clear coverage data
+engine.clear_coverage_data();
+
+print(engine1.get_coverage_report_pretty())
+
+# Enable gathering prints
+engine1.set_gather_prints(True)
+
+# Gather prints
+engine1.eval_query('print("Hello")')
+ps = engine1.take_prints()
+print(ps)
diff --git a/bindings/wasm/test.js b/bindings/wasm/test.js
index a0304bc2..61a06770 100644
--- a/bindings/wasm/test.js
+++ b/bindings/wasm/test.js
@@ -1,48 +1,87 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT License.
 
-var regorus = require('./pkg/regorusjs')
+var regorus = require('./pkg/regorusjs');
 
 // Create an engine.
 var engine = new regorus.Engine();
 
+// Enable code coverage
+engine.setEnableCoverage(true);
+
 // Add Rego policy.
-var pkg = engine.add_policy(
+var pkg = engine.addPolicy(
     // Associate this file name with policy
     'hello.rego',
     
     // Rego policy
 `
   package test
-  
+
+  x = 10
+
   # Join messages
   message = concat(", ", [input.message, data.message])
-`)
-console.log("Loaded policy " + pkg)
+`);
+
+console.log(pkg);
+// data.test
 
 // Set policy data
-engine.add_data_json(`
+engine.addDataJson(`
  {
     "message" : "World!"
  }
-`)
+`);
 
 // Set policy input
-engine.set_input_json(`
+engine.setInputJson(`
  {
 	"message" : "Hello"
  }
-`)
+`);
+
+// Eval rule as json
+var value = engine.evalRule('data.test.message');
+value = JSON.parse(value);
+
+// Display value 
+console.log(value);
+// Hello, World!
 
 // Eval query
-var results = engine.eval_query('data.test.message')
+results = engine.evalQuery('data.test.message');
 
 // Display
-console.log(results)
+console.log(results);
+// {
+//   "result": [
+//     {
+//       "expressions": [
+//         {
+//           "value": "Hello, World!",
+//           "text": "data.test.message",
+//           "location": {
+//             "row": 1,
+//             "col": 1
+//           }
+//         }
+//       ]
+//     }
+//   ]
+// }
 
 // Convert results to object
-results = JSON.parse(results)
+results = JSON.parse(results);
 
 // Process result
-console.log(results.result[0].expressions[0].value)
+console.log(results.result[0].expressions[0].value);
+// Hello, World!
+
+// Print coverage report
+report = engine.getCoverageReport();
+console.log(report);
 
+// Print pretty report.
+report = engine.getCoverageReportPretty();
+console.log(report);
diff --git a/tests/aci/main.rs b/tests/aci/main.rs
index 6b4f0284..3b773131 100644
--- a/tests/aci/main.rs
+++ b/tests/aci/main.rs
@@ -157,7 +157,7 @@ fn run_aci_tests_coverage(dir: &Path) -> Result<()> {
     }
 
     let report = engine.get_coverage_report()?;
-    println!("{}", report.to_colored_string()?);
+    println!("{}", report.to_string_pretty()?);
 
     Ok(())
 }
diff --git a/tests/kata/main.rs b/tests/kata/main.rs
index c19eb911..7b6f3b30 100644
--- a/tests/kata/main.rs
+++ b/tests/kata/main.rs
@@ -132,7 +132,7 @@ fn run_kata_tests(
             #[cfg(feature = "coverage")]
             {
                 let report = engine.get_coverage_report()?;
-                println!("{}", report.to_colored_string()?);
+                println!("{}", report.to_string_pretty()?);
             }
         }
     }