orchestra-toolkit 0.6.1

Client to interract with Orchestra system, uses HGTP protocol
Documentation
/* Copyright 2024-2025 LEDR Technologies Inc.
* This file is part of the Orchestra library, which helps developer use our Orchestra technology which is based on AvesTerra, owned and developped by Georgetown University, under license agreement with LEDR Technologies Inc.
*
* The Orchestra library is a free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.
*
* The Orchestra library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with the Orchestra library. If not, see <https://www.gnu.org/licenses/>.
*
* If you have any questions, feedback or issues about the Orchestra library, you can contact us at support@ledr.io.
*/

use std::{str::FromStr, thread};

use anyhow::Context;
use orchestra_toolkit::*;

fn sync_world(session: Session, outlet: Entity, auth: Token) {
    let s1 = session.clone();
    let res = session
        .adapt_outlet(
            outlet,
            auth,
            Box::new(|args: AdapterArgs| sync_callback_ok(s1, args)),
        )
        .call();

    match res {
        Ok(_) => println!(" sync adapt returned, no error"),
        Err(e) => println!(" sync adapt returned with error {e}"),
    }

    let s2 = session.clone();
    let res = session
        .adapt_outlet(
            outlet,
            auth,
            Box::new(|args: AdapterArgs| sync_callback_err(s2, args)),
        )
        .call();

    match res {
        Ok(_) => println!(" sync adapt returned, no error"),
        Err(e) => println!(" sync adapt returned with error {e}"),
    }
}

fn sync_callback_ok(_session: Session, _args: AdapterArgs) -> Result<Value, AvesterraError> {
    Ok(Value::Text(" Sync Hello world!".to_string()))
}

fn sync_callback_err(_session: Session, _args: AdapterArgs) -> Result<Value, AvesterraError> {
    Err(AvesterraError {
        errcode: HGTPError::Adapter,
        message: String255::unchecked(" Sync Test error"),
    })
}

fn asynchronous_world(session: Session, outlet: Entity, auth: Token) {
    session.run_async(|s| _asynchronous_world(s, outlet, auth))
}

async fn _asynchronous_world(session: &SessionAsync, outlet: Entity, auth: Token) {
    let s1 = session.clone();
    let res = session
        .adapt_outlet(
            outlet,
            auth,
            Box::new(|args: AdapterArgs| Box::pin(async_callback_ok(s1, args))),
        )
        .await;

    match res {
        Ok(_) => println!("async adapt returned, no error"),
        Err(e) => println!("async adapt returned with error {e}"),
    }

    let s2 = session.clone();
    let res = session
        .adapt_outlet(
            outlet,
            auth,
            Box::new(|args: AdapterArgs| Box::pin(async_callback_err(s2, args))),
        )
        .await;

    match res {
        Ok(_) => println!("async adapt returned, no error"),
        Err(e) => println!("async adapt returned with error {e}"),
    }
}

async fn async_callback_ok(
    _session: SessionAsync,
    _args: AdapterArgs,
) -> Result<Value, AvesterraError> {
    Ok(Value::Text("Async Hello world!".to_string()))
}

async fn async_callback_err(
    _session: SessionAsync,
    _args: AdapterArgs,
) -> Result<Value, AvesterraError> {
    Err(AvesterraError {
        errcode: HGTPError::Adapter,
        message: String255::unchecked("Async Test error"),
    })
}

fn main() -> anyhow::Result<()> {
    dotenv::dotenv().ok();

    let mut config = SessionConfig::default();

    if let Ok(host) = std::env::var("AVESTERRA_HOST") {
        config.address = host;
    }

    if let Ok(port_str) = std::env::var("AVESTERRA_PORT") {
        config.port = u16::from_str(&port_str).context("Parsing `AVESTERRA_PORT`")?;
    }

    if let Ok(cert_dir_path) = std::env::var("AVESTERRA_CERTIFICATE_DIR_PATH") {
        config.pem_filepath = (cert_dir_path + "/avesterra.pem").try_into()?;
    }

    let auth = if let Ok(auth_str) = std::env::var("AVESTERRA_AUTH") {
        Token::from_str(&auth_str).context("Parsing authorization token given from env")?
    } else {
        Token::NULL
    };

    let session = Session::initialize(config)?;

    let outlet = session
        .create_outlet(String255::unchecked("Test outlet"), auth)
        .call()?;

    // self-connecting to be able to test by directly invoking the outlet
    session.connect_method(outlet, outlet, auth).call()?;

    let s1 = session.clone();
    let s2 = session.clone();
    let s3 = session.clone();

    let t1 = thread::spawn(move || sync_world(s1, outlet, auth));
    let t2 = thread::spawn(move || asynchronous_world(s2, outlet, auth));
    let t3 = thread::spawn(move || {
        for i in 0..4 {
            let res = s3.invoke_entity(outlet, Method::Null, auth).call();
            println!("Invoke result #{i}: {res:?}");
        }
    });

    t1.join().unwrap();
    t2.join().unwrap();
    t3.join().unwrap();

    session.finalize();

    Ok(())
}