moon new my-project
cd my-project
# Add liquid-moonbit as a dependency (when published)test "basic usage example" {
// Import the library
let template = parse("Hello, {{ name }}!")
let context = LiquidContext::new()
context.set("name", string_value("World"))
let result = template.render(context)
// Output: "Hello, World!"
assert_eq(result, "Hello, World!")
}<!-- Template -->
Hello, {{ user.name }}!
Your role: {{ user.role | capitalize }}
Last login: {{ user.last_login | date: "%B %d, %Y" }}test "variable substitution example" {
// MoonBit code
let context = LiquidContext::new()
let user_obj = Map::new()
user_obj.set("name", string_value("Alice"))
user_obj.set("role", string_value("admin"))
user_obj.set("last_login", string_value("2023-12-01"))
context.set("user", object_value(user_obj))
let template = parse("Hello, {{ user.name }}!\nYour role: {{ user.role | upcase }}")
let result = template.render(context)
assert_eq(result.contains("Hello, Alice!"), true)
}<!-- String filters -->
{{ "hello world" | upcase }} <!-- HELLO WORLD -->
{{ " spaced " | strip }} <!-- spaced -->
{{ "hello" | capitalize }} <!-- Hello -->
<!-- Array filters -->
{{ items | first }} <!-- First item -->
{{ items | last }} <!-- Last item -->
{{ items | size }} <!-- Array length -->
{{ items | join: ", " }} <!-- Comma-separated --><!-- Conditionals -->
{% if user.premium %}
Welcome, premium user!
{% else %}
Consider upgrading to premium.
{% endif %}
<!-- Loops -->
{% for product in products %}
{{ forloop.index }}. {{ product.name }} - ${{ product.price }}
{% endfor %}
<!-- Case statements -->
{% case user.role %}
{% when 'admin' %}
Admin Dashboard
{% when 'user' %}
User Dashboard
{% else %}
Guest Access
{% endcase %}test "api reference types example" {
// Value types
let str_val = string_value("hello")
let num_val = number_value(42.0)
let bool_val = bool_value(true)
let arr_val = array_value([str_val, num_val])
let obj_map = Map::new()
obj_map.set("key", str_val)
let obj_val = object_value(obj_map)
let null_val = null_value()
// Template context
let context = LiquidContext::new()
context.set("example", str_val)
// Verify types work
assert_eq(str_val.to_string(), "hello")
assert_eq(num_val.to_string(), "42")
assert_eq(bool_val.to_string(), "true")
assert_eq(arr_val.to_string(), "[hello, 42]")
assert_eq(obj_val.to_string().contains("key"), true)
assert_eq(null_val.to_string(), "null")
}test "main functions example" {
// Create a new context
let context = LiquidContext::new()
// Set variables
context.set("name", string_value("World"))
// Parse and render template
let template = parse("Hello {{ name }}!")
let result = template.render(context)
// Apply filters
let filtered = apply_filter(string_value("hello"), "upcase")
assert_eq(result, "Hello World!")
assert_eq(filtered.to_string(), "HELLO")
}Template String ā Lexer ā Tokens ā Parser ā AST ā Renderer ā Outputmoon testmoon run mainpub enum LiquidNode {
Text(String)
Variable(String, Array[String])
For(String, String, Array[LiquidNode])
If(String, Array[LiquidNode], Array[LiquidNode]?)
Unless(String, Array[LiquidNode])
Case(String, Array[(String, Array[LiquidNode])], Array[LiquidNode]?)
Assign(String, String)
Comment(String)
}pub enum LiquidValue {
String(String)
Number(Double)
Bool(Bool)
Array(Array[LiquidValue])
Object(Map[String, LiquidValue])
Null
}fn case_node(expression : String, when_branches : Array[(String, Array[LiquidNode])], else_body : Array[LiquidNode]?) -> LiquidNodefn if_node(condition : String, then_body : Array[LiquidNode], else_body : Array[LiquidNode]?) -> LiquidNode