libindigo 0.3.2+INDIGO.2.0.300

Core API for developing INDIGO astronomy clients and devices.
docs.rs failed to build libindigo-0.3.2+INDIGO.2.0.300
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: libindigo-0.3.1+INDIGO.2.0.300

libindigo-rs

Rust API for writing client applications and device drivers for astronomy equipment using the INDIGO protocol and architecture.

πŸŽ‰ Refactoring Complete

The project has been successfully refactored into a multi-crate workspace! See REFACTORING_COMPLETE.md for full details.

Architecture

This project is organized as a Cargo workspace with multiple crates:

Crate Purpose FFI Dependencies
libindigo Core API, traits, and types None
libindigo-rs Pure Rust implementation None
libindigo-ffi FFI-based implementation Yes (via libindigo-sys)
libindigo-sys Raw C bindings Yes
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  libindigo      β”‚  ← Core API (traits, types, constants)
β”‚  (root crate)   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
    β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
    β”‚         β”‚
    β–Ό         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚libindigoβ”‚ β”‚ libindigo-ffiβ”‚
β”‚   -rs   β”‚ β”‚              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚
                   β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚ libindigo-sysβ”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Goal

  • A pure Rust API that is 100% compatible with the INDIGO platform and its default C-implementation

Objectives

  • βœ… Provide an API that uses idiomatic Rust for integrating with the INDIGO Bus
  • βœ… Provide a Service Provider Interface (SPI) for decoupling the API from its implementation
  • βœ… Provide a default RS (Rust) implementation of the SPI without any FFI bindings to the INDIGO C-libraries or other non-rust dependencies
  • βœ… Provide an FFI implementation of the SPI that uses the INDIGO C-library with any necessary dependencies

Quick Start

⚠️ Important: Crate Name vs Import Name

The crate libindigo-rs must be imported as libindigo_rs (with underscore) in your code.

Rust automatically converts hyphens to underscores in crate names. Always use:

use libindigo_rs::{...};  // βœ… CORRECT (underscore)
// NOT: use libindigo::{...};  // ❌ WRONG

Pure Rust Client (Recommended - No C Dependencies)

Add to your Cargo.toml:

[dependencies]
libindigo-rs = "0.3"
tokio = { version = "1.35", features = ["full"] }

Example code:

use libindigo_rs::{Client, ClientBuilder, RsClientStrategy};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create client with pure Rust strategy
    let strategy = RsClientStrategy::new();
    let mut client = ClientBuilder::new()
        .with_strategy(strategy)
        .build();

    // Connect to INDIGO server
    client.connect("localhost:7624").await?;

    // Enumerate all properties
    client.enumerate_properties(None).await?;

    // Disconnect
    client.disconnect().await?;

    Ok(())
}

FFI-Based Client (Maximum Compatibility)

Add to your Cargo.toml:

[dependencies]
libindigo-ffi = "0.3"
tokio = { version = "1.35", features = ["full"] }

Example code:

use libindigo_ffi::{Client, ClientBuilder, FfiClientStrategy};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Note: FFI implementation is currently stubbed
    let strategy = FfiClientStrategy::new()?;
    let mut client = ClientBuilder::new()
        .with_strategy(strategy)
        .build();

    client.connect("localhost:7624").await?;
    client.enumerate_properties(None).await?;
    client.disconnect().await?;

    Ok(())
}

Features

Pure Rust Strategy (libindigo-rs)

The pure Rust strategy provides a complete INDIGO client implementation without C FFI dependencies:

  • βœ… Zero FFI: No C dependencies, pure Rust implementation
  • βœ… Async-First: Built on tokio for efficient async I/O
  • βœ… Type Safe: Leverages Rust's type system for protocol correctness
  • βœ… Cross-Platform: Works anywhere Rust compiles
  • βœ… Dual Protocol: Full INDIGO JSON and XML protocol support with automatic negotiation
  • βœ… JSON-First: Defaults to modern JSON protocol with XML fallback
  • βœ… mDNS Discovery: Optional pure Rust server discovery (no FFI)

Feature Flags:

  • client (default): Client functionality
  • device: Device driver support (stub for future)
  • discovery: mDNS server discovery via pure Rust mdns-sd crate

FFI Strategy (libindigo-ffi)

The FFI strategy wraps the official C INDIGO library:

  • ⚠️ Status: Structure in place, implementation pending
  • βœ… Maximum Compatibility: Uses the official INDIGO C library
  • βœ… Async Support: Async wrappers around synchronous FFI calls
  • βœ… Battle-Tested: Leverages mature C implementation
  • βœ… Feature Complete: Access to all INDIGO features (when implemented)

Feature Flags:

  • client (default): Client functionality
  • device: Device driver support (stub for future)
  • async: Async wrapper for non-blocking operations

JSON Protocol Support βœ…

libindigo-rs supports both INDIGO JSON and XML protocols with intelligent negotiation:

Automatic Protocol Negotiation (Default)

The client automatically negotiates the best protocol with the server:

use libindigo_rs::{Client, ClientBuilder, RsClientStrategy};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // JSON-first with XML fallback (default behavior)
    let strategy = RsClientStrategy::new();
    let mut client = ClientBuilder::new()
        .with_strategy(strategy)
        .build();

    client.connect("localhost:7624").await?;
    // Client automatically negotiates protocol with server
    // Prefers JSON, falls back to XML if server doesn't support JSON

    client.enumerate_properties(None).await?;
    client.disconnect().await?;
    Ok(())
}

Protocol Comparison

Feature JSON Protocol XML Protocol
Version 512 (numeric) "2.0" (string)
Switch Values true/false On/Off
Number Format Native JSON numbers String with format
BLOBs URL only URL or BASE64
Parsing Speed ⚑ Faster Slightly slower
Size πŸ“¦ More compact More verbose
Use Case Modern clients, web apps Legacy compatibility
Server Support INDIGO 2.0+ All INDIGO versions

Protocol Selection Examples

See rs/src/lib.rs documentation for advanced protocol negotiation options.

Server Discovery

The pure Rust implementation includes optional mDNS server discovery:

[dependencies]
libindigo-rs = { version = "0.3", features = ["discovery"] }
use libindigo_rs::discovery::{DiscoveryBuilder, DiscoveryEvent};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut discovery = DiscoveryBuilder::new()
        .with_timeout(std::time::Duration::from_secs(5))
        .build()?;

    let mut receiver = discovery.start().await?;

    while let Some(event) = receiver.recv().await {
        match event {
            DiscoveryEvent::ServerFound { name, address, port } => {
                println!("Found server: {} at {}:{}", name, address, port);
            }
            DiscoveryEvent::ServerLost { name } => {
                println!("Lost server: {}", name);
            }
        }
    }

    Ok(())
}

See rs/PHASE5_DISCOVERY_MIGRATION.md for migration details.

Strategy Comparison

Feature libindigo-rs libindigo-ffi
C Dependencies ❌ None βœ… Required
Async Support βœ… Native βœ… Wrapped
Cross-Platform βœ… Excellent ⚠️ Limited
Performance βœ… Fast βœ… Fast
JSON Protocol βœ… Yes ⚠️ Via C lib
XML Protocol βœ… Yes βœ… Yes
Protocol Negotiation βœ… Automatic ❌ No
mDNS Discovery βœ… Pure Rust ❌ No
Maturity βœ… Production ⚠️ Stub
Use Case Modern apps Legacy compat

Usage Examples

Receiving Property Updates

use libindigo_rs::{Client, ClientBuilder, RsClientStrategy};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut strategy = RsClientStrategy::new();

    // Connect to server
    strategy.connect("localhost:7624").await?;

    // Get property receiver
    let mut rx = strategy.property_receiver().await.unwrap();

    // Spawn task to receive properties
    tokio::spawn(async move {
        while let Some(property) = rx.recv().await {
            println!("Property: {}.{} = {:?}",
                property.device,
                property.name,
                property.values
            );
        }
    });

    // Enumerate properties
    strategy.enumerate_properties(None).await?;

    // Keep running...
    tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;

    strategy.disconnect().await?;
    Ok(())
}

Sending Property Updates

use libindigo_rs::{Client, ClientBuilder, RsClientStrategy};
use libindigo_rs::types::{Property, PropertyType, PropertyValue, SwitchState};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let strategy = RsClientStrategy::new();
    let mut client = ClientBuilder::new()
        .with_strategy(strategy)
        .build();

    client.connect("localhost:7624").await?;

    // Create a switch property to connect a device
    let mut property = Property::new(
        "CCD Simulator".to_string(),
        "CONNECTION".to_string(),
        PropertyType::Switch,
    );

    property.values.push(PropertyValue::Switch {
        name: "CONNECTED".to_string(),
        label: Some("Connected".to_string()),
        value: SwitchState::On,
    });

    // Send the property update
    client.send_property(property).await?;

    client.disconnect().await?;
    Ok(())
}

Documentation

Crate Documentation

Architecture & Planning

Phase Documentation

Additional Documentation

Testing

Run All Tests

cargo test --workspace

Run Pure Rust Tests

# All pure Rust tests (including JSON protocol tests)
cargo test -p libindigo-rs

# JSON protocol tests only (61 tests)
cargo test -p libindigo-rs --test json_protocol_tests

# Protocol negotiation tests only (59 tests)
cargo test -p libindigo-rs --test protocol_negotiation_tests

Run Discovery Tests

cargo test -p libindigo-rs --features discovery

Integration Tests

Integration tests require a running INDIGO server:

# Start INDIGO server (in another terminal)
indigo_server

# Run integration tests
cargo test --test discovery_tests --features discovery

Test Coverage Summary

Test Suite Tests Coverage
JSON Protocol 61 All PROTOCOLS.md examples + edge cases
Protocol Negotiation 59 Auto-detection, fallback, preferences
Rust Client ~50 Connection, properties, lifecycle
Discovery ~20 mDNS discovery, filtering
Total ~190 Comprehensive coverage

Examples

The examples/ directory contains usage examples:

Run examples:

# Server discovery
cargo run --example discover_servers --features discovery

# Discovery with filter
cargo run --example discovery_with_filter --features discovery

Note: Some examples use deprecated features and need updating. See REFACTORING_COMPLETE.md for details.

Project Structure

libindigo-rs/
β”œβ”€β”€ Cargo.toml                    # Workspace root
β”œβ”€β”€ README.md                     # This file
β”œβ”€β”€ REFACTORING_COMPLETE.md       # Refactoring summary
β”œβ”€β”€ src/                          # libindigo (core API)
β”‚   β”œβ”€β”€ lib.rs                    # Main library entry point
β”‚   β”œβ”€β”€ error.rs                  # Error types
β”‚   β”œβ”€β”€ constants.rs              # INDIGO protocol constants
β”‚   β”œβ”€β”€ client/                   # Client API
β”‚   β”‚   β”œβ”€β”€ mod.rs
β”‚   β”‚   β”œβ”€β”€ builder.rs            # Client builder
β”‚   β”‚   └── strategy.rs           # ClientStrategy trait (SPI)
β”‚   └── types/                    # Core types
β”‚       β”œβ”€β”€ mod.rs
β”‚       β”œβ”€β”€ property.rs           # Property types
β”‚       β”œβ”€β”€ device.rs             # Device types
β”‚       └── value.rs              # Value types
β”œβ”€β”€ rs/                           # libindigo-rs (pure Rust)
β”‚   β”œβ”€β”€ Cargo.toml
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ lib.rs                # Re-exports + RS strategy
β”‚   β”‚   β”œβ”€β”€ client.rs             # RsClientStrategy
β”‚   β”‚   β”œβ”€β”€ protocol.rs           # XML protocol parser
β”‚   β”‚   β”œβ”€β”€ protocol_json.rs      # JSON protocol parser
β”‚   β”‚   β”œβ”€β”€ protocol_negotiation.rs
β”‚   β”‚   β”œβ”€β”€ transport.rs          # TCP transport layer
β”‚   β”‚   └── discovery/            # mDNS discovery (optional)
β”‚   β”‚       β”œβ”€β”€ mod.rs
β”‚   β”‚       β”œβ”€β”€ api.rs
β”‚   β”‚       β”œβ”€β”€ error.rs
β”‚   β”‚       └── mdns_impl.rs
β”œβ”€β”€ ffi/                          # libindigo-ffi (FFI-based)
β”‚   β”œβ”€β”€ Cargo.toml
β”‚   β”œβ”€β”€ README.md
β”‚   └── src/
β”‚       β”œβ”€β”€ lib.rs                # Re-exports + FFI strategy
β”‚       β”œβ”€β”€ ffi.rs                # FfiClientStrategy
β”‚       └── async_ffi.rs          # AsyncFfiStrategy
β”œβ”€β”€ sys/                          # libindigo-sys (raw bindings)
β”‚   β”œβ”€β”€ Cargo.toml
β”‚   β”œβ”€β”€ README.md
β”‚   β”œβ”€β”€ build.rs                  # C library build
β”‚   └── src/lib.rs                # bindgen output
β”œβ”€β”€ relm/                         # libindigo-relm (GTK demo, excluded)
β”œβ”€β”€ examples/                     # Usage examples
β”œβ”€β”€ tests/                        # Integration tests
β”œβ”€β”€ doc/                          # Documentation
β”œβ”€β”€ plans/                        # Planning documents
└── scripts/                      # Utility scripts
    └── update_constants.sh       # Update INDIGO constants

Migration from Old API

If you're upgrading from the old monolithic API, see the migration guide in REFACTORING_COMPLETE.md.

Quick summary:

# Cargo.toml
- libindigo = { version = "0.1", features = ["rs"] }
+ libindigo-rs = "0.3"

# Code
- use libindigo::strategies::RsClientStrategy;
- use libindigo::client::ClientBuilder;
+ use libindigo_rs::{RsClientStrategy, ClientBuilder};

Troubleshooting: "unresolved module libindigo" Error

If you see this error after adding libindigo-rs to your dependencies:

error[E0432]: unresolved import `libindigo`

Solution: The crate name uses a hyphen (libindigo-rs) but imports must use an underscore (libindigo_rs).

// ❌ WRONG - causes "unresolved module" error
use libindigo::{Client, ClientBuilder};

// βœ… CORRECT - use underscore in imports
use libindigo_rs::{Client, ClientBuilder};

This is a Rust convention: hyphens in crate names are automatically converted to underscores for imports.

Contributing

Contributions are welcome! Please:

  1. Read doc/ways-of-working.md
  2. Follow doc/roo-workflow-scheme.md
  3. Use appropriate issue templates
  4. Write tests for new features
  5. Update documentation

Known Issues & Future Work

See REFACTORING_COMPLETE.md for:

  • FFI implementation status
  • BLOB handling improvements
  • Device driver support (future)
  • Example updates needed

License

This project is licensed under the MIT License - see the LICENSE.md file for details.

Acknowledgments

  • INDIGO Astronomy - The INDIGO protocol and C library
  • The Rust community for excellent async ecosystem tools

Related Projects

  • INDIGO - Official C implementation
  • libindigo-sys - Low-level FFI bindings to INDIGO C library

Status

Current Version: 0.3.0

Status: Production-ready for pure Rust client applications

  • βœ… libindigo-rs: Complete and production-ready
  • ⚠️ libindigo-ffi: Structure in place, implementation pending
  • βœ… Multi-crate refactoring: Complete (see REFACTORING_COMPLETE.md)

For production use, we recommend:

  • Pure Rust Strategy (libindigo-rs) for new projects without C dependencies
  • FFI Strategy (libindigo-ffi) for maximum compatibility (when implementation is complete)

Support

For questions, issues, or contributions:

  • Open an issue on GitHub
  • Check existing documentation
  • Review the architecture plan