Skip to main content

liminal_server/services/site/
placement.rs

1//! The existing Lys placement contract.
2
3use super::{APP, Placer};
4use crate::services::lys_http::LysHttp;
5use std::sync::Arc;
6
7/// [`Placer`] over Lys's placement route with this liminal's app credential.
8pub struct LysPlacer {
9    http: Arc<dyn LysHttp>,
10    credential: String,
11}
12
13impl LysPlacer {
14    /// A placer asking through `http` with `credential`.
15    #[must_use]
16    pub fn new(http: Arc<dyn LysHttp>, credential: String) -> Self {
17        Self { http, credential }
18    }
19}
20
21impl std::fmt::Debug for LysPlacer {
22    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        formatter.debug_struct("LysPlacer").finish_non_exhaustive()
24    }
25}
26
27impl Placer for LysPlacer {
28    fn place(
29        &self,
30        operation: &str,
31        child: (&str, &str),
32        parent: (&str, &str),
33    ) -> Result<(), String> {
34        let route = format!("apps/{APP}/placements");
35        let body = serde_json::json!({
36            "operation": operation,
37            "child": {"kind": child.0, "id": child.1},
38            "parent": {"kind": parent.0, "id": parent.1},
39        });
40        let answer = self
41            .http
42            .post(&self.credential, &route, &body)
43            .map_err(|error| error.0)?;
44        // Lys's exact refusal of an existing placement is the placement standing, as the
45        // exchange's other placers read it: a template is placed before Lys is asked about it.
46        if answer.succeeded()
47            || answer.body["reason"]
48                == "placement_invalid: the resource is already placed in a parent"
49        {
50            Ok(())
51        } else {
52            Err(answer.refusal())
53        }
54    }
55}