Skip to content

chrsoo/libindigo-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

163 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

About

Rust API for developing INDIGO astronomy clients and devices.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors