← Back to Documentation

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

FieldDescription
nameUnique package identifier
versionSemantic version (e.g., 1.0.0)
providesList of content types and paths

Provides Types

TypeDescription
aro-filesARO feature sets (.aro files)
swift-pluginSwift native plugins
rust-pluginRust native plugins
c-pluginC/C++ native plugins
python-pluginPython plugins
aro-templatesHTML 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

// Sources/CSVParser.swift
import Foundation

@_cdecl("aro_plugin_info")
public func pluginInfo() -> UnsafePointer<CChar> {
    let info = """
    {"name":"csv-parser","version":"1.0.0","actions":["ParseCSV"]}
    """
    return (info as NSString).utf8String!
}

@_cdecl("aro_plugin_execute")
public func execute(
    action: UnsafePointer<CChar>,
    input: UnsafePointer<CChar>
) -> UnsafeMutablePointer<CChar> {
    let actionName = String(cString: action)
    let inputJson = String(cString: input)

    // Parse input, execute action, return result
    let result = processCSV(inputJson)
    return strdup(result)
}

@_cdecl("aro_plugin_free")
public func freeResult(ptr: UnsafeMutablePointer<CChar>) {
    free(ptr)
}

Rust Plugins

// src/lib.rs
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

#[no_mangle]
pub extern "C" fn aro_plugin_info() -> *const c_char {
    let info = r#"{"name":"csv-parser-rs","version":"1.0.0","actions":["ParseCSV"]}"#;
    CString::new(info).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn aro_plugin_execute(
    action: *const c_char,
    input: *const c_char
) -> *mut c_char {
    let action = unsafe { CStr::from_ptr(action).to_str().unwrap() };
    let input = unsafe { CStr::from_ptr(input).to_str().unwrap() };

    // Process and return result
    let result = process_csv(input);
    CString::new(result).unwrap().into_raw()
}

#[no_mangle]
pub extern "C" fn aro_plugin_free(ptr: *mut c_char) {
    unsafe { drop(CString::from_raw(ptr)); }
}

Python Plugins

# src/csv_tools.py
import json
import csv
from io import StringIO

def aro_plugin_info():
    return {
        "name": "csv-tools-py",
        "version": "1.0.0",
        "language": "python",
        "actions": ["ParseCSV", "AnalyzeCSV"]
    }

def aro_action_parse_csv(input_json):
    params = json.loads(input_json)
    content = params["content"]

    reader = csv.DictReader(StringIO(content))
    rows = list(reader)

    return json.dumps({"rows": rows})

def aro_action_analyze_csv(input_json):
    params = json.loads(input_json)
    # Use pandas for analysis
    import pandas as pd
    df = pd.read_csv(StringIO(params["content"]))
    return json.dumps(df.describe().to_dict())

Plugin FFI Protocol

All native plugins must implement three C-compatible functions:

FunctionPurpose
aro_plugin_info()Returns JSON with plugin metadata
aro_plugin_execute(action, input)Executes an action with JSON input/output
aro_plugin_free(ptr)Frees memory allocated by the plugin

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:

  1. Create a Git repository for your package
  2. Ensure plugin.yaml is in the root
  3. Tag releases with semantic versions (e.g., v1.0.0)
  4. 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

Related Documentation

Packages - Installing and using packages
Custom Actions - The ActionImplementation protocol
Feature Sets - Organizing code into feature sets