Skip to main content

Crate gemstone_rs

Crate gemstone_rs 

Source
Expand description

Safe Rust client API for GemStone/S over GCI.

This crate builds on gemstone-gci, which owns the dynamic libgcirpc loading and raw ABI calls. Session provides RAII login/logout behavior, explicit OOP values, conservative transaction helpers, and basic value marshalling for nil, booleans, small integers, characters, and raw OOPs.

Rust code imports this crate as gemstone_rs:

use gemstone_rs::{Config, Session, Value};

fn main() -> gemstone_rs::Result<()> {
    let config = Config::from_env()?;
    let mut session = Session::login(config)?;

    let value = session.eval("3 + 4")?;
    assert_eq!(value, Value::SmallInt(7));

    session.logout()?;
    Ok(())
}

Build configuration explicitly when you do not want to read process environment:

use gemstone_rs::{Config, Session};

fn main() -> gemstone_rs::Result<()> {
    let config = Config::builder()
        .stone("gs64stone")
        .host("localhost")
        .netldi("netldi")
        .username("DataCurator")
        .password("your-password")
        .build()?;
    let mut session = Session::login(config)?;
    println!("session id: {}", session.session_id());
    Ok(())
}

Store and fetch values through UserGlobals:

use gemstone_rs::{Config, Session};

fn main() -> gemstone_rs::Result<()> {
    let mut session = Session::login(Config::from_env()?)?;
    let text = session.new_string("hello from Rust")?;
    session.global_put("GemStoneRsDocExample", text)?;
    session.commit()?;

    let stored = session.global_get("GemStoneRsDocExample")?;
    assert_eq!(session.fetch_string(stored)?, "hello from Rust");
    Ok(())
}

Store a mapped Rust payload under the default bridge-root dictionary:

use gemstone_rs::{BridgeValue, Config, Session};

fn main() -> gemstone_rs::Result<()> {
    let mut session = Session::login(Config::from_env()?)?;
    let payload = BridgeValue::dictionary([
        ("name".to_string(), BridgeValue::from("Tariq")),
        ("amount".to_string(), BridgeValue::from(100_i64)),
        ("currency".to_string(), BridgeValue::from("GBP")),
    ]);
    let mut bridge_root = session.bridge_root()?;
    bridge_root.put("MyTestDict", payload)?;
    bridge_root.commit()?;
    Ok(())
}

Add a typed manual mapping on top of BridgeValue when you want Rust structs at the application boundary:

use gemstone_rs::{BridgeDictionary, BridgeMapped, BridgeValue, Config, Session};

struct BookingDraft {
    name: String,
    amount: i64,
}

impl BridgeMapped for BookingDraft {
    fn to_bridge_value(&self) -> BridgeValue {
        BridgeValue::dictionary([
            ("name".to_string(), BridgeValue::from(self.name.clone())),
            ("amount".to_string(), BridgeValue::from(self.amount)),
        ])
    }

    fn from_bridge_dictionary(dictionary: &mut BridgeDictionary<'_>) -> gemstone_rs::Result<Self> {
        Ok(Self {
            name: dictionary.at_string("name")?,
            amount: dictionary.at_smallint("amount")?,
        })
    }
}

fn main() -> gemstone_rs::Result<()> {
    let mut session = Session::login(Config::from_env()?)?;
    let mut bridge_root = session.bridge_root()?;
    let draft = BookingDraft { name: "Tariq".to_string(), amount: 100 };
    bridge_root.put_mapped("BookingDraft", &draft)?;
    let loaded: BookingDraft = bridge_root.get_mapped("BookingDraft")?;
    assert_eq!(loaded.amount, 100);
    Ok(())
}

Browse dictionaries, classes, protocols, methods, and source through the same browser API used by the CLI and explorer:

use gemstone_rs::{browser::Browser, Config, Session};

fn main() -> gemstone_rs::Result<()> {
    let mut session = Session::login(Config::from_env()?)?;
    let mut browser = Browser::new(&mut session);

    let dictionaries = browser.dictionaries()?;
    let classes = browser.classes("UserGlobals")?;
    let protocols = browser.protocols("Object", false, "")?;
    let methods = browser.methods("Object", "-- all --", false, "")?;
    let source = browser.source("Object", "printString", false, "")?;

    println!("{dictionaries:?} {classes:?} {protocols:?} {methods:?}");
    println!("{source}");
    Ok(())
}

Call selectors directly when generated wrappers would be too much:

use gemstone_rs::{Config, Session};

fn main() -> gemstone_rs::Result<()> {
    let mut session = Session::login(Config::from_env()?)?;
    let seven = session.smallint_oop(7);
    let printed = session.perform_oop(seven, "printString", &[])?;
    assert_eq!(session.fetch_string(printed)?, "7");
    Ok(())
}

Transactions are explicit. transaction commits on success and aborts on error:

use gemstone_rs::{Config, Session};

fn main() -> gemstone_rs::Result<()> {
    let mut session = Session::login(Config::from_env()?)?;
    session.transaction(|session| {
        let value = session.new_string("hello from Rust")?;
        session.global_put("GemStoneRsExample", value)
    })?;
    Ok(())
}

OOPs and values are explicit:

use gemstone_rs::{Config, Session, Value};

fn main() -> gemstone_rs::Result<()> {
    let mut session = Session::login(Config::from_env()?)?;
    let oop = session.value_to_oop(&Value::SmallInt(7))?;
    let value = session.perform(oop, "printString", &[])?;
    println!("{value:?}");
    Ok(())
}

Codegen configs can be parsed and checked from Rust:

use gemstone_rs::codegen;

let config = codegen::Config::parse(
    "output = generated/gemstone_wrappers.rs\nclass = Object\nmethod = Object>>printString | return=String\n",
    None,
)?;
let generated = codegen::generate(&config);
assert!(generated.source.contains("pub struct Object"));

Check generated wrappers in build or release tooling:

use gemstone_rs::codegen;

fn main() -> codegen::Result<()> {
    let config = codegen::Config::from_file("examples/codegen/gemstone-rs.codegen")?;
    let report = codegen::check(&config)?;
    assert!(report.up_to_date, "{} is stale", report.output.display());
    Ok(())
}

Safety notes:

  • Session is deliberately not Send or Sync; keep it on the thread that logged in.
  • Unsafe C ABI calls are isolated in gemstone-gci.
  • libgcirpc is loaded dynamically at runtime through GS_LIB_PATH, GS_LIB, or the platform loader path.

Roadmap:

  • Broader typed wrapper generation for Rust services and CLIs.
  • A richer local explorer over the same API.
  • Optional VS Code webview integration once the CLI and explorer contracts are stable.

Re-exports§

pub use bridge::BridgeDictionary;
pub use bridge::BridgeFieldRead;
pub use bridge::BridgeFieldWrite;
pub use bridge::BridgeKey;
pub use bridge::BridgeKeyType;
pub use bridge::BridgeMapped;
pub use bridge::BridgeRoot;
pub use bridge::BridgeValue;
pub use bridge::DEFAULT_BRIDGE_ROOT;

Modules§

bridge
browser
codegen

Structs§

Config
ConfigBuilder
Oop
OopHandle
Session

Enums§

Error
TransactionPolicy
Value

Constants§

OOP_FALSE
OOP_ILLEGAL
OOP_NIL
OOP_TRUE

Type Aliases§

Result

Derive Macros§

BridgeMapped