Writing Extensions
Create your own ARO packages with custom actions, feature sets, and native plugins. Share them with the community or use them in your projects.
Package Structure
An ARO package is a directory with a plugin.yaml manifest and
source files. The manifest is required - without it, ARO
won't recognize the directory as a package.
my-package/
├── plugin.yaml # Required: Package manifest
├── README.md # Optional: Documentation
├── features/ # ARO feature sets (.aro files)
│ ├── csv-parser.aro
│ └── csv-formatter.aro
├── Sources/ # Native plugins (Swift, Rust, C)
│ └── CSVParser.swift
└── tests/ # Test files
└── csv-parser.test.aro
The plugin.yaml Manifest
The plugin.yaml file describes your package. It's the only required
file and contains all metadata ARO needs to load your package.
# plugin.yaml - Package manifest
name: csv-tools
version: 1.0.0
description: "CSV parsing, validation, and formatting tools"
author: "Your Name"
license: MIT
aro-version: ">=0.2.0"
# What this package provides
provides:
- type: aro-files # ARO feature sets
path: features/
- type: swift-plugin # Swift native code
path: Sources/
- type: aro-templates # Template files (optional)
path: templates/
# Dependencies on other packages (optional)
dependencies:
aro-core-utils:
git: "git@github.com:arolang/core-utils.git"
ref: "v2.0.0"
# Build configuration (optional)
build:
swift:
minimum-version: "6.2"
targets:
- name: CSVTools
path: Sources/
Required Fields
| Field | Description |
|---|---|
name | Unique package identifier |
version | Semantic version (e.g., 1.0.0) |
provides | List of content types and paths |
Provides Types
| Type | Description |
|---|---|
aro-files | ARO feature sets (.aro files) |
swift-plugin | Swift native plugins |
rust-plugin | Rust native plugins |
c-plugin | C/C++ native plugins |
python-plugin | Python plugins |
aro-templates | HTML templates |
Pure ARO Packages
The simplest packages contain only .aro files. These provide
reusable feature sets without any native code:
(* features/csv-parser.aro *)
(Parse CSV File: CSV Processing) {
Extract the <content> from the <file>.
Split the <lines> from the <content> by "\n".
Extract the <header: first> from the <lines>.
Split the <columns> from the <header> by ",".
Transform each <row> in the <lines: 1-> with <columns>.
Return an <OK: status> with <rows>.
}
(Validate CSV Schema: CSV Validation) {
Extract the <data> from the <input: data>.
Extract the <schema> from the <input: schema>.
Validate the <data> against the <schema>.
Return a <Valid: status>.
}
Native Plugins
For performance-critical operations or system integrations, you can write native plugins in Swift, Rust, C, or Python.
Swift Plugins
Use the AROPluginKit SDK with the @AROExport macro:
// Sources/CSVParser.swift
import AROPluginKit
@AROExport
private let plugin = AROPlugin(name: "csv-parser", version: "1.0.0", handle: "CSV")
.action("ParseCSV", verbs: ["parsecsv"], role: "own",
prepositions: ["from"]) { input in
let data = input.string("data") ?? ""
let rows = parseCSV(data)
return .success(["rows": rows, "count": rows.count])
}
Rust Plugins
Use #[action] and aro_export! macros from the SDK:
// src/lib.rs
use aro_plugin_sdk::prelude::*;
#[action(name = "ParseCSV", verbs = ["parsecsv"], role = "own",
prepositions = ["from"], description = "Parse CSV data")]
fn parse_csv(input: &Input) -> PluginResult<Output> {
let data = input.string("data")
.ok_or_else(|| PluginError::missing("data"))?;
let rows = parse(data);
Ok(Output::new().set("rows", json!(rows)))
}
aro_export! {
name: "csv-parser-rs",
version: "1.0.0",
handle: "CSV",
actions: [parse_csv],
qualifiers: [],
}
Python Plugins
Use @plugin and @action decorators:
# src/plugin.py
from aro_plugin_sdk import plugin, action, export_abi, run, AROInput
@plugin(name="csv-tools-py", version="1.0.0", handle="CSV")
class CSVPlugin:
pass
@action(name="parse-csv", verbs=["parsecsv"], role="own",
prepositions=["from"], description="Parse CSV data")
def handle_parse_csv(input: AROInput):
content = input.string("data")
rows = parse_csv(content)
return {"rows": rows, "count": len(rows)}
export_abi(globals())
if __name__ == "__main__":
run()
C Plugins
Use ARO_PLUGIN() and ARO_ACTION() macros from the single-header SDK:
/* src/csv_plugin.c */
#define ARO_PLUGIN_SDK_IMPLEMENTATION
#include "aro_plugin_sdk.h"
ARO_PLUGIN("csv-parser-c", "1.0.0")
ARO_HANDLE("CSV")
ARO_ACTION("ParseCSV", "own", "from") {
const char* data = aro_input_string(ctx, "data");
/* parse CSV and populate output */
return aro_ok(ctx);
}
Plugin SDK Registration
Each language has an idiomatic SDK that generates the required C ABI exports automatically:
| Language | Registration | SDK |
|---|---|---|
| Swift | @AROExport macro | AROPluginKit (SPM) |
| Rust | #[action] + aro_export! | aro-plugin-sdk (crate) |
| C / C++ | ARO_PLUGIN() + ARO_ACTION() | aro_plugin_sdk.h (header) |
| Python | @plugin + @action + export_abi() | aro-plugin-sdk (pip) |
Testing Your Package
Create test files alongside your feature sets:
(* tests/csv-parser.test.aro *)
(Test Parse CSV: CSV Parser Tests) {
Create the <test-data> with "name,age\nAlice,30\nBob,25".
ParseCSV the <result> from the <test-data>.
Assert that <result: length> equals 2.
Assert that <result: 0 name> equals "Alice".
Return a <Passed: status>.
}
Run tests with:
aro test ./my-package
Publishing Your Package
ARO uses Git repositories for package distribution. To share your package:
- Create a Git repository for your package
- Ensure
plugin.yamlis in the root - Tag releases with semantic versions (e.g.,
v1.0.0) - Share the Git URL with users
# Users can install with:
aro add git@github.com:yourname/csv-tools.git
aro add git@github.com:yourname/csv-tools.git --ref v1.0.0
Version Compatibility
Always specify the aro-version field in your plugin.yaml
to indicate which ARO versions your package supports. Use semantic version
ranges like >=0.2.0 or ^1.0.0.
Best Practices
- Use semantic versioning - Follow semver for version numbers
- Document your actions - Include a README with examples
- Test thoroughly - Include test files for all feature sets
- Keep dependencies minimal - Only depend on what you need
- Use descriptive names - Prefix actions with your domain (e.g.,
CSVParse)
Related Documentation
Packages - Installing and using packages
Custom Actions - The ActionImplementation protocol
Feature Sets - Organizing code into feature sets