Rust Bindings: Add initial documentation for Rust UNO bindings

Change-Id: Ibb2978ebf1bbca8afddd13bdf8fba5e070c9037f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198115
Reviewed-by: Stephan Bergmann <stephan.bergmann@collabora.com>
Tested-by: Jenkins
This commit is contained in:
Mohamed Ali
2026-01-27 08:53:19 +01:00
committed by Stephan Bergmann
parent 2b27652d2d
commit a7ef6f04e9
2 changed files with 408 additions and 0 deletions
+206
View File
@@ -0,0 +1,206 @@
# Rust UNO Developer Guide
<!--toc:start-->
- [Rust UNO Developer Guide](#rust-uno-developer-guide)
- [Table of Contents](#table-of-contents)
- [Project Overview](#project-overview)
- [Codebase Structure](#codebase-structure)
- [Code Generator (`codemaker`)](#code-generator-codemaker)
- [Runtime Library (`rust_uno`)](#runtime-library-rustuno)
- [The Rustmaker Generator](#the-rustmaker-generator)
- [Generation Flow](#generation-flow)
- [Key Implementation Details](#key-implementation-details)
- [Development Workflow](#development-workflow)
- [Prerequisites](#prerequisites)
- [Rebuilding Bindings](#rebuilding-bindings)
- [Testing Strategy](#testing-strategy)
- [Integration Tests](#integration-tests)
- [Unit Tests](#unit-tests)
- [Future Work: Embindtest](#future-work-embindtest)
<!--toc:end-->
This guide is intended for contributors working on the **Rust UNO bindings** implementation itself. If you are a user looking to *use* the bindings, please see the [User Guide](Rust_UNO_User_Guide.md).
## Table of Contents
1. [Project Overview](#project-overview)
2. [Codebase Structure](#codebase-structure)
3. [The Rustmaker Generator](#the-rustmaker-generator)
4. [Development Workflow](#development-workflow)
5. [Testing Strategy](#testing-strategy)
---
## Project Overview
The Rust UNO project enables LibreOffice to be scripted and extended using Rust. It works by inspecting the UNO IDL (Interface Definition Language) types and generating:
1. **Rust Wrappers**: Safe, idiomatic Rust structs.
2. **C++ Bridges**: `extern "C"` functions that handle the raw UNO C++ API.
The system is fully integrated into the LibreOffice build system via `autogen.sh` and `make`.
---
## Codebase Structure
The project is split across the core LibreOffice codebase:
### Code Generator (`codemaker`)
Located in `codemaker/source/rustmaker/`. This tool reads the binary type library (RDB files) and outputs source code.
| File | Purpose |
|------|---------|
| `rustproduce.cxx` | Generates Rust source files (structs, enums, interfaces). |
| `cpproduce.cxx` | Generates the C++ side of the bridge. |
| `unoproduce.cxx` | Coordinator that drives the generation process. |
| `rustfile.cxx` | Helper for writing Rust source files (indentation, imports). |
### Runtime Library (`rust_uno`)
Located in `rust_uno/`. This is the crate that users depend on.
- `src/lib.rs`: Crate root, exports `generated` modules.
- `src/generated/`: Destination for generated Rust code.
- `src/core/`: Handwritten core utilities (`OUString` wrapper, primitive types).
---
## The Rustmaker Generator
The heart of the project is the `rustmaker` binary. It runs during the build process to produce the bindings.
### Generation Flow
```mermaid
graph LR
IDL["UNO IDL Files"] -->|idlc| RDB["Binary RDB"]
RDB -->|read by| RustMaker["rustmaker (codemaker)"]
RustMaker -->|writes| RustFiles["Rust Wrappers (.rs)"]
RustMaker -->|writes| CppFiles["C++ Bridge (.cxx)"]
RustFiles -->|compiled by| Rustc["rustc"]
CppFiles -->|compiled by| Gxx["g++"]
Rustc -->|links| SharedLib["lib_rust_uno.so"]
Gxx -->|links| SharedLib
```
### Key Implementation Details
- **Typed Parameters**: Methods are generated with native types (e.g. `i32`, `bool`) where possible.
- **Opaque Pointers**: All UNO objects are held as `*mut c_void` in Rust, pointing to a `Ref<Interface>` in C++.
- **Memory Management**: The `Drop` trait implementation calls the C++ destructor, which releases the UNO reference.
---
## Development Workflow
### Prerequisites
Ensure you have a full LibreOffice build environment and have run `./autogen.sh --enable-rust-uno`.
### Rebuilding Bindings
When you modify `codemaker`, you need to regenerate the bindings:
```bash
# Rebuild the generator
make codemaker
# Regenerate the source files
make rust_uno
# Rebuild the final library
make
```
> [!TIP]
> If you see bizarre errors after changing `rustmaker`, try `make rust_uno.clean` first to force a fresh generation.
---
## Testing Strategy
Testing is integrated into the LibreOffice startup sequence for basic sanity checks.
### Integration Tests
The entry point `run_rust_uno_test` in `rust_uno/src/lib.rs` is called by `soffice` on startup.
It runs scenarios defined in `rust_uno/src/examples/`.
To run the tests:
```bash
./instdir/program/soffice --norestore
```
Watch the console output for `=== Rust UNO Bridge Test ===`.
### Unit Tests
Pure Rust unit tests (for `OUString` logic, etc.) can be run via Cargo:
```bash
cd rust_uno
cargo test
```
### Future Work: Embindtest
We plan to integrate with `embindtest`, the comprehensive UNO test suite. This is a high-priority task to ensure full coverage of generated types.
## Roadmap and Missing Features
This section details the current implementation status and future development tasks, ordered by architectural dependency and impact.
### Implementation Status
The following table summarizes the generation status of UNO types in `codemaker`.
| UNO Type | Status | Generator Function | Notes |
|----------|--------|-------------------|-------|
| **Module** | Implemented | `produceModule()` | Generates directory structure and `mod.rs` files. |
| **Enum** | Implemented | `produceEnum()` | Full support including validation and bridge functions. |
| **Struct** | Implemented | `produceStruct()` | Generates PlainStructs with getters, setters, and constructors. |
| **Interface** | Implemented | `produceInterface()` | Generates traits, wrappers, and reference-counting logic. |
| **Service** | Partial | `produceService()` | Supports single-interface services; accumulation-based services are deprecated. |
| **Sequence** | Partial | `rustproduce.cxx` | Currently handled as `void*` in FFI; lacks safe Rust wrappers. |
| **ConstantGroup** | Not Implemented | `produceConstantGroup()` | Function exists but body contains only a log message; content is skipped. |
| **Exception** | Not Implemented | `produceException()` | Function exists but body contains only a log message; code generation is missing. |
### Development Tasks
The following tasks are ordered by their foundational importance to the binding quality.
#### 1. ConstantGroup Generation
**Goal**: Generate Rust constants for UNO ConstantGroups (e.g., `com.sun.star.awt.FontWeight`).
**Files**: `codemaker/source/rustmaker/rustproduce.cxx`, `cpproduce.cxx`
**Details**: Currently, `produceConstantGroup` prints a skipping message. This needs to look up the constants in the IDL entity and generate a Rust `module` with `const` items.
#### 2. Exception Mapping
**Goal**: Integrate UNO exceptions with Rust's `Result` type.
**Files**: `codemaker/source/rustmaker/`
**Details**:
* Generate structs for UNO Exception types (`produceException`).
* Update method signatures to return `Result<T, UnoError>` instead of `Option<T>`.
* The C++ bridge currently catches exceptions but does not fully propagate type information to Rust.
#### 3. Typedef Resolution
**Goal**: Resolve UNO typedefs to their concrete Rust types.
**Files**: `codemaker/source/rustmaker/unoproduce.cxx`
**Details**: `produceTypedef` currently prints a log message. It should resolve the aliased type and generate a `type Alias = ConcreteType;` definition in Rust.
#### 4. Singleton Generation
**Goal**: Generate accessors for UNO singletons.
**Files**: `codemaker/source/rustmaker/rustproduce.cxx`
**Details**: `produceSingleton` is currently a placeholder. It should generate helper methods to retrieve the singleton instance (typically via component context), avoiding manual `createInstance` calls.
#### 5. Type-Safe Sequences
**Goal**: Replace `void*` usage for Sequences with a proper `Sequence<T>` wrapper.
**Files**: `codemaker/source/rustmaker/rustproduce.cxx`, `rust_uno/src/core/`
**Details**: Implement a `Sequence` struct that manages the memory layout compatible with UNO sequences and implements `Iterator`.
---
+202
View File
@@ -0,0 +1,202 @@
# Rust UNO User Guide
<!--toc:start-->
- [Rust UNO User Guide](#rust-uno-user-guide)
- [Table of Contents](#table-of-contents)
- [Introduction](#introduction)
- [Prerequisites & Setup](#prerequisites-setup)
- [Requirements](#requirements)
- [Enabling Rust Support](#enabling-rust-support)
- [Building](#building)
- [Architecture Overview](#architecture-overview)
- [Quick Start](#quick-start)
- [1. Dependencies](#1-dependencies)
- [2. Loading a Document](#2-loading-a-document)
- [Core Concepts](#core-concepts)
- [Interfaces](#interfaces)
- [Strings](#strings)
- [Memory Management](#memory-management)
- [Troubleshooting](#troubleshooting)
<!--toc:end-->
Welcome to the **Rust UNO Bindings** user guide. This document provides comprehensive instructions for using Rust to interact with LibreOffice via the UNO (Universal Network Objects) API.
## Table of Contents
1. [Introduction](#introduction)
2. [Prerequisites & Setup](#prerequisites--setup)
3. [Architecture Overview](#architecture-overview)
4. [Quick Start](#quick-start)
5. [Core Concepts](#core-concepts)
6. [Troubleshooting](#troubleshooting)
---
## Introduction
The Rust UNO bindings provide a safe, high-performance interface to automate LibreOffice. Unlike Python or Java bindings which rely heavily on runtime reflection, Rust UNO uses **compile-time generated wrappers** ensuring:
- **Type Safety**: Errors are caught at compile time.
- **Performance**: Zero-cost abstractions over the C++ bridge.
- **Memory Safety**: Automatic resource management via RAII (Drop trait).
- **Typed Parameters**: Methods take native Rust types (`bool`, `i32`) instead of unsafe `void*` pointers.
---
## Prerequisites & Setup
To use Rust UNO, you must build it as part of LibreOffice.
### Requirements
- **Rust**: Stable toolchain (install via `rustup`).
- **LibreOffice Build Deps**: Standard development environment for LibreOffice.
- **Disk Space**: Sufficient space for a full LibreOffice build.
### Enabling Rust Support
Add the following to your `autogen.input` file or pass it to `autogen.sh`:
```bash
--enable-rust-uno
```
### Building
Run the following commands to generate bindings and build LibreOffice:
```bash
./autogen.sh
make codemaker # Builds the IDL compiler
make rust_uno # Generates Rust/C++ bridge code
make # Final build
```
> [!NOTE]
> The generated Rust unit tests run automatically during the build process to verify the bridge.
---
## Architecture Overview
The bindings utilize a 3-layer architecture to safely bridge safe Rust with C++ UNO.
```mermaid
graph TD
UserCode["Your Rust Code"] -->|Calls| RustWrapper["Rust Wrappers"]
RustWrapper -->|FFI| CppBridge["C++ Bridge (extern C)"]
CppBridge -->|Calls| NativeUNO["Native LibreOffice C++ API"]
subgraph "Layer 1: Safe Rust"
RustWrapper
end
subgraph "Layer 2: FFI Bridge"
CppBridge
end
subgraph "Layer 3: Core"
NativeUNO
end
```
1. **Rust Wrappers** (`rust_uno/src/generated/`): Safe structs holding opaque pointers. Methods are fully typed.
2. **C++ Bridge** (`workdir/CustomTarget/rust_uno/rustmaker/cpp/`): Handles exception catching and reference counting.
3. **Native UNO**: The actual LibreOffice implementation.
---
## Quick Start
### 1. Dependencies
Ensure your `Cargo.toml` includes the dependencies. Since `rust_uno` is currently built in-tree, you point to it using a path dependency:
```toml
[dependencies]
rust_uno = { path = "rust_uno" }
```
### 2. Loading a Document
Here is a complete example of bootstrapping UNO and loading a Writer document. This demonstrates the **Typed Parameter API**.
```rust
use rust_uno::generated::rustmaker::com::sun::star::frame::{Desktop, XComponentLoader};
use rust_uno::generated::rustmaker::com::sun::star::text::XTextDocument;
use rust_uno::core::{OUString, uno_wrapper::defaultBootstrap_InitialComponentContext};
use std::ptr;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Bootstrap the component context
let context = defaultBootstrap_InitialComponentContext()?;
// 2. Create the Desktop service
// Wrappers take raw pointers for low-level compatibility, or other wrappers
let desktop = Desktop::create(context.as_ptr())
.ok_or("Failed to create Desktop service")?;
// 3. Query the XComponentLoader interface
// Interface conversion is explicit and safe
let loader = XComponentLoader::from_ptr(desktop.as_ptr())
.ok_or("Desktop does not support XComponentLoader")?;
// 4. Load a blank Writer document
let url = OUString::from("private:factory/swriter");
let target = OUString::from("_blank");
// Note: Typed parameters! No casting to void* needed.
let component = loader.loadComponentFromURL(
url, // Passed by value (moved) or reference depending on signature
target,
0, // i32
ptr::null_mut() // Any/Sequence arguments
).ok_or("Failed to load component")?;
// 5. Use the document
if !component.as_ptr().is_null() {
let text_doc = XTextDocument::from_ptr(component.as_ptr())
.ok_or("Loaded component is not a TextDocument")?;
if let Some(text) = text_doc.getText() {
println!("Document loaded successfully!");
// text.setString(OUString::from("Hello Rust!"));
}
}
Ok(())
}
```
---
## Core Concepts
### Interfaces
UNO interfaces map directly to Rust structs.
- **C++**: `Reference<XInterface>`
- **Rust**: `pub struct XInterface { ptr: *mut c_void }`
### Strings
LibreOffice uses `OUString` (UTF-16). The bindings provide efficient conversion:
- `OUString::from("Hello")`: Rust string slice -> OUString
- `ou_string.to_string()`: OUString -> Rust String
### Memory Management
Rust's `Drop` trait automatically calls `acquire` and `release` on the underlying UNO objects via the C++ bridge. You do not need to manually manage reference counts.
### Troubleshooting
| Issue | Cause | Solution |
|-------|-------|----------|
| `make rust_uno` fails | Codemaker out of date | Run `make rust_uno.clean && make codemaker && make rust_uno` |
| Runtime panic "Symbol not found" | LD_LIBRARY_PATH missing | Run your binary via `instdir/program/soffice --norestore` environment or set paths manually. |
| `Option::None` from query | Interface not supported | Check API docs to ensure the object supports the interface you are querying. |
> [!TIP]
> Always check `instdir/program/soffice` execution logs. The rust bridge prints initialization status to stdout/stderr with `SAL_WARN` channels if enabled.