// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
#nullable enable
namespace Regorus
{
///
/// Represents a policy module with an ID and content.
///
public struct PolicyModule
{
///
/// Gets or sets the unique identifier for this policy module.
///
public string Id { get; set; }
///
/// Gets or sets the Rego policy content.
///
public string Content { get; set; }
///
/// Initializes a new instance of the PolicyModule struct.
///
/// The unique identifier for this policy module
/// The Rego policy content
public PolicyModule(string id, string content)
{
Id = id;
Content = content;
}
}
///
/// Provides static methods for compiling policies into efficient compiled representations.
/// These are convenience methods that create an engine internally and perform compilation.
///
public static unsafe class Compiler
{
///
/// Compiles a policy from data and modules with a specific entry point rule.
/// This is a convenience function that sets up an Engine internally and calls the appropriate compilation method.
///
/// JSON string containing static data for policy evaluation
/// List of policy modules to compile
/// The specific rule path to evaluate (e.g., "data.policy.allow")
/// A compiled policy that can be evaluated efficiently
/// Thrown when compilation fails
public static CompiledPolicy CompilePolicyWithEntrypoint(string dataJson, IEnumerable modules, string entryPointRule)
{
var dataBytes = Encoding.UTF8.GetBytes(dataJson + char.MinValue);
var entryPointBytes = Encoding.UTF8.GetBytes(entryPointRule + char.MinValue);
var modulesArray = modules.ToArray();
// Convert C# modules to native structs
var nativeModules = new Internal.RegorusPolicyModule[modulesArray.Length];
var pinnedHandles = new List();
try
{
for (int i = 0; i < modulesArray.Length; i++)
{
var idBytes = Encoding.UTF8.GetBytes(modulesArray[i].Id + char.MinValue);
var contentBytes = Encoding.UTF8.GetBytes(modulesArray[i].Content + char.MinValue);
var idHandle = GCHandle.Alloc(idBytes, GCHandleType.Pinned);
var contentHandle = GCHandle.Alloc(contentBytes, GCHandleType.Pinned);
pinnedHandles.Add(idHandle);
pinnedHandles.Add(contentHandle);
nativeModules[i] = new Internal.RegorusPolicyModule
{
id = (byte*)idHandle.AddrOfPinnedObject(),
content = (byte*)contentHandle.AddrOfPinnedObject()
};
}
fixed (byte* dataPtr = dataBytes)
fixed (byte* entryPointPtr = entryPointBytes)
fixed (Internal.RegorusPolicyModule* modulesPtr = nativeModules)
{
var result = Internal.API.regorus_compile_policy_with_entrypoint(
dataPtr, modulesPtr, (UIntPtr)modulesArray.Length, entryPointPtr);
var policy = GetCompiledPolicyResult(result);
return policy;
}
}
finally
{
foreach (var handle in pinnedHandles)
{
handle.Free();
}
}
}
///
/// Compiles a target-aware policy from data and modules.
/// This is a convenience function that sets up an Engine internally and calls target-aware compilation.
/// At least one module must contain a `__target__` declaration.
///
/// JSON string containing static data for policy evaluation
/// List of policy modules to compile
/// A compiled policy that can be evaluated efficiently
/// Thrown when compilation fails
public static CompiledPolicy CompilePolicyForTarget(string dataJson, IEnumerable modules)
{
var dataBytes = Encoding.UTF8.GetBytes(dataJson + char.MinValue);
var modulesArray = modules.ToArray();
// Convert C# modules to native structs
var nativeModules = new Internal.RegorusPolicyModule[modulesArray.Length];
var pinnedHandles = new List();
try
{
for (int i = 0; i < modulesArray.Length; i++)
{
var idBytes = Encoding.UTF8.GetBytes(modulesArray[i].Id + char.MinValue);
var contentBytes = Encoding.UTF8.GetBytes(modulesArray[i].Content + char.MinValue);
var idHandle = GCHandle.Alloc(idBytes, GCHandleType.Pinned);
var contentHandle = GCHandle.Alloc(contentBytes, GCHandleType.Pinned);
pinnedHandles.Add(idHandle);
pinnedHandles.Add(contentHandle);
nativeModules[i] = new Internal.RegorusPolicyModule
{
id = (byte*)idHandle.AddrOfPinnedObject(),
content = (byte*)contentHandle.AddrOfPinnedObject()
};
}
fixed (byte* dataPtr = dataBytes)
fixed (Internal.RegorusPolicyModule* modulesPtr = nativeModules)
{
var result = Internal.API.regorus_compile_policy_for_target(
dataPtr, modulesPtr, (UIntPtr)modulesArray.Length);
var policy = GetCompiledPolicyResult(result);
return policy;
}
}
finally
{
foreach (var handle in pinnedHandles)
{
handle.Free();
}
}
}
private static string? StringFromUTF8(IntPtr ptr)
{
#if NETSTANDARD2_1
return System.Runtime.InteropServices.Marshal.PtrToStringUTF8(ptr);
#else
int len = 0;
while (System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) { ++len; }
byte[] buffer = new byte[len];
System.Runtime.InteropServices.Marshal.Copy(ptr, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
#endif
}
private static CompiledPolicy GetCompiledPolicyResult(Internal.RegorusResult result)
{
try
{
if (result.status != Internal.RegorusStatus.Ok)
{
var message = StringFromUTF8((IntPtr)result.error_message);
throw new Exception(message ?? "Unknown compilation error occurred");
}
if (result.data_type != Internal.RegorusDataType.Pointer || result.pointer_value == null)
{
throw new Exception("Expected compiled policy pointer but got different data type");
}
return new CompiledPolicy((Internal.RegorusCompiledPolicy*)result.pointer_value);
}
finally
{
Internal.API.regorus_result_drop(result);
}
}
}
}