Skip to main content

Use the Lattice SDK for gRPC in Rust

This page shows how you can install, set up, and use the Lattice SDK for gRPC in Rust to get information about an entity in your environment.

Click to go to the gRPC SDK repository. By downloading and/or using, you agree to the terms of use. If you do not agree, do not use the SDK.

Before you begin

  1. Complete the set up steps to generate a valid API bearer token and publish at least one entity to your environment.
  2. Install the latest version of Rust. When you install Rust using rustup, you also get the latest version of Cargo. You can use Cargo to set up your project and manage dependencies.

Set up your Rust project

For this project, you use cargo to set up an example project. In the following steps, you'll replace the starter code with examples that let you connect with your Lattice environment.

  1. Confirm that you have Cargo installed:

    cargo --version
  2. Create a new Rust project:


    cargo new $YOUR_PROJECT

    Cargo creates a project folder consisting of a Cargo.toml to list versioned dependencies, and a main.rs file where you implement the main function for your program.

    Next, you'll replace the starter code.

Get the example files

Copy and paste the following examples into your Rust project:

main.rs
main.rs
 use tonic::metadata::MetadataValue;
use tonic::transport::ClientTlsConfig;
use tonic::transport::Channel;
use tonic::Status;
use tonic::Request;

use anduril_lattice_sdk::anduril::entitymanager::v1::entity_manager_api_client::EntityManagerApiClient;
use anduril_lattice_sdk::anduril::entitymanager::v1::GetEntityRequest;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Replace $YOUR_BEARER_TOKEN with your token. You must keep the word Bearer in the metadata string.
let token = "$YOUR_BEARER_TOKEN";
let bearer_token = format!("Bearer {}", token);

let header_value: MetadataValue<_> = bearer_token.parse().map_err(|_| Status::internal("Invalid Bearer Token"))?;
let tls_config = ClientTlsConfig::new().with_native_roots();
let http_endpoint = format!("$YOUR_LATTICE_URL");

let registration_channel = Channel::from_shared(http_endpoint)
.map_err(|e| Status::invalid_argument(format!("Invalid HTTP endpoint: {}", e)))?
.tls_config(tls_config)
.map_err(|_| Status::internal("TLS configuration failed"))?
.connect()
.await
.map_err(|e| Status::unavailable(format!("Failed to connect: {}", e)))?;

let mut em_client = EntityManagerApiClient::with_interceptor(registration_channel, |mut req: Request<()>| {
req.metadata_mut().insert("authorization", header_value.clone());
Ok(req)
});

let response = em_client.get_entity(GetEntityRequest {
entity_id: String::from("$ENTITY_ID")
}).await?.into_inner();

println!("Response: {:?}", response);

Ok(())
}
info

In main.rs, replace $YOUR_LATTICE_URL, $YOUR_BEARER_TOKEN, and $ENTITY_ID with your information.

Your project structure should now look similar to the following:

├── src 
│ └── main.rs
├── .gitignore
└── Cargo.toml

Install the SDK

To install the gRPC SDK in Rust, do the following:

  1. Add the lattice-sdk-rust dependency to your Cargo.toml file. You will also need a gRPC implementation. We recommend that you use Tonic and Tokio:

    cargo add anduril-lattice-sdk
    # Optional packages.
    cargo add tonic -F features-tls
    cargo add tokio -F full

    After installing the packages, Cargo adds Cargo.lock to your project folder. This file is automatically generated and updated by Cargo when you build or update your project. Cargo.lock ensures reproducible builds of your project by locking dependency versions. This helps you ensure consistency across different developments.

    Your Cargo.toml file should now look similar to the following:

    [package]
    name = "$YOUR_PROJECT"
    version = "0.1.0"
    edition = "2021"

    [dependencies]
    anduril-lattice-sdk = "1.0.0"
    tonic = { version="0.8", features=["tls","tls-roots"] }
    tokio = { version="1.0", features=["full"] }
  2. Verify that you can fetch the required dependencies:

    cargo build

    This command compiles your Rust project and its dependencies. Cargo places the resulting binary files in target/debug.

Run the application

Run your project and connect to Lattice:

cargo run

If the request is successful and you replaced the entity ID then you should see the entity object you created when setting up your development environment. If you did not change the entity ID to a valid, existing entity, then you will get the following error:

ConnectError: [not_found] entity not found ENTITY_ID

This means you successfully authenticated and completed the call to your environment, but the entity does not exist. This is an expected error. If you get any other error then there is a problem with your code. Check that your Lattice environment URL and our bearer token are correct.

To build an optimized binary release of your project, you can build your project using the --release option:

cargo build --release

This creates an optimized build in target/release.

How it works

In main.rs, you first set up the Tokio runtime and the main function. This is the entry point for your program. You then declare and parse the bearer token you previously generated.

main.rs
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Replace $YOUR_BEARER_TOKEN with your token.
// You must keep the word Bearer in the metadata string.
let token = "$YOUR_BEARER_TOKEN";
let bearer_token = format!("Bearer {}", token);
let header_value: MetadataValue<_> = bearer_token.parse().map_err(|_| Status::internal("Invalid Bearer Token"))?;

Next, you create a secure TLS channel using your Lattice environment hostname.

   let tls_config = ClientTlsConfig::new().with_native_roots();
// Replace $YOUR_LATTICE_HOSTNAME with your environment's hostname.
// For example, example.anduril.com.
let lattice_hostname = format!("$YOUR_LATTICE_HOSTNAME");
let registration_channel = Channel::from_shared(lattice_hostname)
.map_err(|e| Status::invalid_argument(format!("Invalid HTTP endpoint: {}", e)))?
.tls_config(tls_config)
.map_err(|_| Status::internal("TLS configuration failed"))?
.connect()
.await
.map_err(|e| Status::unavailable(format!("Failed to connect: {}", e)))?;

Then, you create a new API client, EntityManagerApiClient with an interceptor. The interceptor modifies each outgoing API request and passes your token to request's authorization metadata header.

   let mut em_client = EntityManagerApiClient::with_interceptor(registration_channel, |mut req: Request<()>| {
req.metadata_mut().insert("authorization", header_value.clone());
Ok(req)
});

Finally, using the get_entity method, you pass in your $ENTITY_ID and make an asynchronous API call to the Lattice API.

   let response = em_client.get_entity(GetEntityRequest {
entity_id: String::from("$ENTITY_ID")
}).await?.into_inner();

println!("Response: {:?}", response);

Ok(())
}

What's next