Crate suss

source · []
Expand description

suss

Suss is a library to create collections of unix services that can interact with each other and (optionally) start each other on-demand.

Furthermore, it is pluggable into different async frameworks as long as you provide an implementation of socket_shims::UnixSocketInterface . If you don’t care about async at all, either use the standard threadpool implementation (based on blocking ), or write a synchronous API implementation and use a crate like pollster to un-async things.

It is hosted at:

Basic Example

Provide a single interface for a simple echo service, over any compatible unix socket interface - with the ability to start on demand by running a command.

use suss::prelude::*;
use suss::blocking;
use std::io::{Result as IoResult, BufRead};

struct EchoClient<U: UnixSocketInterface>(U::UnixStream);

impl <U: UnixSocketInterface>  EchoClient<U> {
    pub fn new(u: U::UnixStream) -> Self {
        Self(u)
    }

    pub async fn write_and_receive<'b>(&mut self, in_value: &'b [u8]) -> IoResult<Vec<u8>> {
        U::unix_stream_write_all(&mut self.0, &(in_value.len() as u64).to_be_bytes()).await?;
        U::unix_stream_write_all(&mut self.0, in_value).await?;
        let mut out_length_bytes = [0u8;8];
        U::unix_stream_read_exact(&mut self.0, &mut out_length_bytes).await?;
        let mut out_length = u64::from_be_bytes(out_length_bytes);
        let mut out = vec![0u8;out_length.try_into().unwrap()];
        U::unix_stream_read_exact(&mut self.0, &mut out).await?;
        Ok(out)
    }
}

declare_service!{
    /// This is a very nice echo service!
    EchoService <U> = {
        "my-echo-service" "--server" @ "echo-service.socks" as raw |stream| -> Io<EchoClient<U>> { Ok(EchoClient(stream)) } 
    } impl {U: UnixSocketInterface}
}

fn main() { 
    let reified: ReifiedService<_, suss::socket_shims::StdThreadpoolUSocks, _> = EchoService.reify("/var/run/".as_ref());
    
    futures_lite_block_on(async move {
        let mut service_api = reified.connect_to_running().await.expect("No service running");
        let stdin = std::io::stdin();
        let mut stdin = stdin.lock();
        let mut buf = String::new();
        loop {
            stdin.read_line(&mut buf).unwrap();
            if buf.trim() == "die" {
                break
            };
            let received_line = service_api.write_and_receive(buf.as_bytes()).await.unwrap();
            let valid_utf8 = std::str::from_utf8(&received_line).unwrap();
            println!("{valid_utf8}");
        }
    });
}

Uses

This library allows you to:

  • Define a list of services - and unix socket names - in a single location, along with an arbitrary interface wrapping a bare unix stream to act as a client connection, as well as a way to begin the service.
  • Provide implementations of the service in another package, or perhaps the same package in a different module. In theory, this will allow alternate implementations or perhaps test implementations to flourish, as long as the service can keep the clients happy.
  • Automatically attempt to detect running services on initial connection, and then try to start them in case a service was not running, along with an inbuilt mechanism for liveness checking (which is, admittedly, slightly intrusive in that it requires an argument to be passed to a new service in some manner).
  • Uses the asynchronous unix sockets available in your async runtime if present, via optional dependencies (async-std, tokio, and a threadpool-based std fallback are currently available). All of these frameworks can convert to and from standard library unix sockets, so you can use them freely in client and server implementations even as suss remains runtime-agnostic.
  • Provide a runtime directory as a “namespace” for your collection of services - this means that a collection of services can be run systemwide, per user, in some other configuration, all as long as you can provide a different base directory path for each collection of services you want to run.

Re-exports

pub use blocking;
pub use chain_trans;
pub use socket_shims::UnixSocketInterface;

Modules

Combinators for the Future trait.

Module containing utilities for managing the liveness socket.

Simple module for mapping the result of a future in-place.

Module for usually-necessary imports.

Provide semi-unified interface to unix sockets api for arbitrary different async runtimes or perhaps actually-sync-under-the-hood interfaces.

Like crate::socket_shims, but allows the creation of a timeout instead, falling back to an unblock in the worst case.

Macros

A macro that aids in generating the common case of a services with an easy way to add a command to make it startable by running said command.

This macro lets you create a service bundle, for unified initialisation of a collection of services.

Structs

Holds a particular instance of a Service, along with a base context directory and optional executor prefix.

Traits

Server implementation for a Service

Extension trait that lets you run servers well

Trait used to define a single service, with a relative socket path. For a more concise way of implementing services, take a look at the declare_service and declare_service_bundle macros that let you implement this trait far more concisely. For the items emitted by service bundles, have a look at ReifiedService, which embeds an executor prefix and base context directory along with an abstract service implementation encoded by this trait.

Trait implemented by “bundles” of services that all work together and call each other.

An extension trait to Service that provides a means of starting a service automatically when it can’t be connected to.

Attribute Macros

Provide async_trait for convenience.