rust_uno/doc/Rust_UNO_Developer_Guide.md | 206 +++++++++++++++++++++++++++++++ rust_uno/doc/Rust_UNO_User_Guide.md | 202 ++++++++++++++++++++++++++++++ 2 files changed, 408 insertions(+)
New commits: commit a7ef6f04e92d7b89d6fa10b1e4766b444312c0a0 Author: Mohamed Ali <[email protected]> AuthorDate: Mon Jan 26 07:06:01 2026 +0200 Commit: Stephan Bergmann <[email protected]> CommitDate: Tue Jan 27 08:53:19 2026 +0100 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 <[email protected]> Tested-by: Jenkins diff --git a/rust_uno/doc/Rust_UNO_Developer_Guide.md b/rust_uno/doc/Rust_UNO_Developer_Guide.md new file mode 100644 index 000000000000..114228248461 --- /dev/null +++ b/rust_uno/doc/Rust_UNO_Developer_Guide.md @@ -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`. + +--- + diff --git a/rust_uno/doc/Rust_UNO_User_Guide.md b/rust_uno/doc/Rust_UNO_User_Guide.md new file mode 100644 index 000000000000..afea64e6cc1a --- /dev/null +++ b/rust_uno/doc/Rust_UNO_User_Guide.md @@ -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.
