liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
//! The existing Lys placement contract.

use super::{APP, Placer};
use crate::services::lys_http::LysHttp;
use std::sync::Arc;

/// [`Placer`] over Lys's placement route with this liminal's app credential.
pub struct LysPlacer {
    http: Arc<dyn LysHttp>,
    credential: String,
}

impl LysPlacer {
    /// A placer asking through `http` with `credential`.
    #[must_use]
    pub fn new(http: Arc<dyn LysHttp>, credential: String) -> Self {
        Self { http, credential }
    }
}

impl std::fmt::Debug for LysPlacer {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter.debug_struct("LysPlacer").finish_non_exhaustive()
    }
}

impl Placer for LysPlacer {
    fn place(
        &self,
        operation: &str,
        child: (&str, &str),
        parent: (&str, &str),
    ) -> Result<(), String> {
        let route = format!("apps/{APP}/placements");
        let body = serde_json::json!({
            "operation": operation,
            "child": {"kind": child.0, "id": child.1},
            "parent": {"kind": parent.0, "id": parent.1},
        });
        let answer = self
            .http
            .post(&self.credential, &route, &body)
            .map_err(|error| error.0)?;
        // Lys's exact refusal of an existing placement is the placement standing, as the
        // exchange's other placers read it: a template is placed before Lys is asked about it.
        if answer.succeeded()
            || answer.body["reason"]
                == "placement_invalid: the resource is already placed in a parent"
        {
            Ok(())
        } else {
            Err(answer.refusal())
        }
    }
}