2025-08-07 21:10:17 +02:00
|
|
|
<!doctype html>
|
2025-08-08 07:06:47 +02:00
|
|
|
<html>
|
|
|
|
|
<head>
|
2025-08-07 21:10:17 +02:00
|
|
|
<meta charset="utf-8" />
|
2025-08-08 07:06:47 +02:00
|
|
|
<title>CEL-Rust WASM-bindgen Example</title>
|
|
|
|
|
<style>
|
|
|
|
|
body {
|
|
|
|
|
font-family: Arial, sans-serif;
|
|
|
|
|
max-width: 1200px;
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
.container {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: 1fr 1fr;
|
|
|
|
|
gap: 20px;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
.full-width {
|
|
|
|
|
grid-column: 1 / -1;
|
|
|
|
|
}
|
2025-08-07 21:10:17 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
textarea,
|
|
|
|
|
input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
font-family: "Courier New", monospace;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
border: 2px solid #ddd;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
textarea {
|
|
|
|
|
height: 120px;
|
|
|
|
|
resize: vertical;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
.result {
|
|
|
|
|
background-color: #f8f9fa;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
border: 2px solid #28a745;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
font-family: "Courier New", monospace;
|
|
|
|
|
min-height: 50px;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
.error {
|
|
|
|
|
background-color: #f8d7da;
|
|
|
|
|
border-color: #dc3545;
|
|
|
|
|
color: #721c24;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
.success {
|
|
|
|
|
background-color: #d4edda;
|
|
|
|
|
border-color: #28a745;
|
|
|
|
|
color: #155724;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
label {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
display: block;
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
button {
|
|
|
|
|
background-color: #007bff;
|
|
|
|
|
color: white;
|
|
|
|
|
border: none;
|
|
|
|
|
padding: 10px 20px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
button:hover {
|
|
|
|
|
background-color: #0056b3;
|
|
|
|
|
}
|
2025-08-07 21:10:17 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
.examples {
|
|
|
|
|
margin-top: 30px;
|
|
|
|
|
}
|
2025-08-07 21:10:17 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
.example {
|
|
|
|
|
background-color: #f1f3f4;
|
|
|
|
|
padding: 15px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.example:hover {
|
|
|
|
|
background-color: #e8eaed;
|
|
|
|
|
}
|
2025-08-08 06:56:49 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
.example h4 {
|
|
|
|
|
margin: 0 0 10px 0;
|
|
|
|
|
color: #1976d2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.example code {
|
|
|
|
|
display: block;
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
margin: 5px 0;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<h1>CEL-Rust WASM-bindgen Demo</h1>
|
2025-08-07 21:10:17 +02:00
|
|
|
<p>
|
2025-08-08 09:57:55 +02:00
|
|
|
This demo uses <code>wasm-bindgen</code> to provide a JavaScript API for
|
|
|
|
|
CEL evaluation using <code>cel-rust</code>.
|
2025-08-07 21:10:17 +02:00
|
|
|
</p>
|
2025-08-08 06:56:49 +02:00
|
|
|
|
|
|
|
|
<div class="container">
|
2025-08-08 07:06:47 +02:00
|
|
|
<div>
|
|
|
|
|
<label for="expression">CEL Expression:</label>
|
|
|
|
|
<textarea id="expression" placeholder="Enter CEL expression...">
|
|
|
|
|
name + " is " + string(age) + " years old and " + (active ? "active" : "inactive")</textarea
|
|
|
|
|
>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label for="context">Context (as JavaScript object):</label>
|
|
|
|
|
<textarea
|
|
|
|
|
id="context"
|
|
|
|
|
placeholder="Enter context as JavaScript object..."
|
|
|
|
|
>
|
|
|
|
|
{"name": "Alice", "age": 30, "active": true, "scores": [95, 87, 92], "profile": {"country": "US", "level": "senior"}}</textarea
|
|
|
|
|
>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-08-08 06:56:49 +02:00
|
|
|
<div class="full-width">
|
2025-08-08 07:06:47 +02:00
|
|
|
<label>Result:</label>
|
2025-08-08 09:57:55 +02:00
|
|
|
<div id="result" class="result">WASM module not loaded yet...</div>
|
2025-08-08 07:06:47 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="examples">
|
|
|
|
|
<h2>Examples (click to try):</h2>
|
|
|
|
|
|
|
|
|
|
<div class="example" onclick="loadExample(this)">
|
|
|
|
|
<h4>Basic String Operations</h4>
|
|
|
|
|
<code
|
|
|
|
|
data-expression="'Hello ' + name + '!'"
|
|
|
|
|
data-context='{"name": "World"}'
|
|
|
|
|
>Expression: 'Hello ' + name + '!'</code
|
|
|
|
|
>
|
|
|
|
|
<code>Context: {"name": "World"}</code>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="example" onclick="loadExample(this)">
|
|
|
|
|
<h4>Math Operations</h4>
|
|
|
|
|
<code
|
2025-08-08 09:57:55 +02:00
|
|
|
data-expression="(price * double(quantity)) * (1.0 + tax)"
|
2025-08-08 07:06:47 +02:00
|
|
|
data-context='{"price": 10.50, "quantity": 3, "tax": 0.08}'
|
2025-08-08 09:57:55 +02:00
|
|
|
>Expression: (price * double(quantity)) * (1.0 + tax)</code
|
2025-08-08 07:06:47 +02:00
|
|
|
>
|
|
|
|
|
<code>Context: {"price": 10.50, "quantity": 3, "tax": 0.08}</code>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="example" onclick="loadExample(this)">
|
|
|
|
|
<h4>List Operations</h4>
|
|
|
|
|
<code
|
2025-08-08 09:57:55 +02:00
|
|
|
data-expression="string(scores.filter(s, s > 90).size()) + ' high scores out of ' + string(scores.size())"
|
2025-08-08 07:06:47 +02:00
|
|
|
data-context='{"scores": [85, 92, 78, 96, 88]}'
|
2025-08-08 09:57:55 +02:00
|
|
|
>Expression: string(scores.filter(s, s > 90).size()) + ' high
|
|
|
|
|
scores'</code
|
2025-08-08 07:06:47 +02:00
|
|
|
>
|
|
|
|
|
<code>Context: {"scores": [85, 92, 78, 96, 88]}</code>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="example" onclick="loadExample(this)">
|
|
|
|
|
<h4>Conditional Logic</h4>
|
|
|
|
|
<code
|
|
|
|
|
data-expression="age >= 18 ? (age >= 65 ? 'senior' : 'adult') : 'minor'"
|
|
|
|
|
data-context='{"age": 25}'
|
|
|
|
|
>Expression: age >= 18 ? (age >= 65 ? 'senior' : 'adult') :
|
|
|
|
|
'minor'</code
|
|
|
|
|
>
|
|
|
|
|
<code>Context: {"age": 25}</code>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="example" onclick="loadExample(this)">
|
|
|
|
|
<h4>Map/Object Access</h4>
|
|
|
|
|
<code
|
|
|
|
|
data-expression="user.profile.country == 'US' && user.profile.verified"
|
|
|
|
|
data-context='{"user": {"profile": {"country": "US", "verified": true}}}'
|
|
|
|
|
>Expression: user.profile.country == 'US' &&
|
|
|
|
|
user.profile.verified</code
|
|
|
|
|
>
|
|
|
|
|
<code
|
|
|
|
|
>Context: {"user": {"profile": {"country": "US", "verified":
|
|
|
|
|
true}}}</code
|
|
|
|
|
>
|
|
|
|
|
</div>
|
2025-08-09 20:55:57 +02:00
|
|
|
|
|
|
|
|
<div class="example" onclick="loadExample(this)">
|
|
|
|
|
<h4>Time and Date Operations</h4>
|
|
|
|
|
<code
|
|
|
|
|
data-expression="some_date < now - old ? 'old' : 'recent'"
|
|
|
|
|
data-context='{"some_date": "2023-10-01T12:00:00Z", "now": "2023-10-03T12:00:00Z", "old": "24h"}'
|
|
|
|
|
>Expression: some_date < now - old ? 'old' : 'recent'</code
|
|
|
|
|
>
|
|
|
|
|
<code
|
|
|
|
|
>Context: {"some_date": "2023-10-01T12:00:00Z", "now":
|
|
|
|
|
"2023-10-03T12:00:00Z", "old": "24h"}</code
|
|
|
|
|
>
|
2025-08-08 06:56:49 +02:00
|
|
|
</div>
|
2025-08-07 21:10:17 +02:00
|
|
|
|
2025-08-08 07:06:47 +02:00
|
|
|
<script type="module">
|
2025-08-08 09:57:55 +02:00
|
|
|
import init, { evaluate_cel } from "./pkg/cel_rust_wasm.js";
|
2025-08-08 07:06:47 +02:00
|
|
|
|
|
|
|
|
let wasmModule;
|
|
|
|
|
|
|
|
|
|
async function initWasm() {
|
|
|
|
|
wasmModule = await init();
|
|
|
|
|
console.log("WASM module loaded successfully");
|
|
|
|
|
|
2025-08-08 09:57:55 +02:00
|
|
|
// Make function globally available
|
2025-08-08 07:06:47 +02:00
|
|
|
window.evaluate_cel = evaluate_cel;
|
|
|
|
|
|
2025-08-08 09:57:55 +02:00
|
|
|
setTimeout(evaluate, 0);
|
2025-08-08 07:06:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize WASM when the page loads
|
|
|
|
|
initWasm().catch(console.error);
|
|
|
|
|
|
|
|
|
|
// Make functions globally available for onclick handlers
|
2025-08-08 09:57:55 +02:00
|
|
|
window.evaluate = evaluate;
|
2025-08-08 07:06:47 +02:00
|
|
|
window.loadExample = loadExample;
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<script>
|
2025-08-08 09:57:55 +02:00
|
|
|
function evaluate() {
|
2025-08-08 07:06:47 +02:00
|
|
|
if (!window.evaluate_cel) {
|
|
|
|
|
document.getElementById("result").textContent =
|
|
|
|
|
"WASM module not loaded yet...";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const expression = document.getElementById("expression").value;
|
|
|
|
|
const contextStr = document.getElementById("context").value;
|
|
|
|
|
const resultDiv = document.getElementById("result");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Parse the context as JavaScript object
|
|
|
|
|
const context = contextStr.trim() ? JSON.parse(contextStr) : {};
|
|
|
|
|
|
2025-08-08 07:38:48 +02:00
|
|
|
// Evaluate and get the actual JavaScript value
|
2025-08-08 07:06:47 +02:00
|
|
|
const result = window.evaluate_cel(expression, context);
|
|
|
|
|
|
2025-08-08 07:38:48 +02:00
|
|
|
resultDiv.className = "result success";
|
2025-08-08 09:57:55 +02:00
|
|
|
resultDiv.textContent = `Success (type '${typeof result}'): ${JSON.stringify(result, null, 2)}`;
|
2025-08-08 07:38:48 +02:00
|
|
|
|
|
|
|
|
// Log the actual JavaScript object to console for inspection
|
2025-08-08 09:57:55 +02:00
|
|
|
console.log("CEL Result:", result);
|
2025-08-08 07:38:48 +02:00
|
|
|
console.log("Type:", typeof result);
|
2025-08-08 07:06:47 +02:00
|
|
|
} catch (e) {
|
|
|
|
|
resultDiv.className = "result error";
|
2025-08-08 07:38:48 +02:00
|
|
|
resultDiv.textContent = `Error: ${e}`;
|
2025-08-08 09:57:55 +02:00
|
|
|
console.log("CEL Error:", e);
|
2025-08-08 07:06:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function loadExample(element) {
|
|
|
|
|
const codeElement = element.querySelector("code[data-expression]");
|
|
|
|
|
const expression = codeElement.getAttribute("data-expression");
|
|
|
|
|
const context = codeElement.getAttribute("data-context");
|
|
|
|
|
|
|
|
|
|
document.getElementById("expression").value = expression;
|
|
|
|
|
document.getElementById("context").value = context;
|
|
|
|
|
|
|
|
|
|
// Auto-evaluate
|
2025-08-08 09:57:55 +02:00
|
|
|
setTimeout(evaluate, 0);
|
2025-08-08 07:06:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Auto-evaluate on input change
|
|
|
|
|
document.getElementById("expression").addEventListener("input", () => {
|
2025-08-08 09:57:55 +02:00
|
|
|
setTimeout(evaluate, 100);
|
2025-08-08 07:06:47 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById("context").addEventListener("input", () => {
|
2025-08-08 09:57:55 +02:00
|
|
|
setTimeout(evaluate, 100);
|
2025-08-08 07:06:47 +02:00
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|