homestar_invocation/authority/
issuer.rs

1//! Issuer refers to the issuer of the invocation.
2
3use crate::{Error, Unit};
4use const_format::formatcp;
5#[cfg(feature = "diesel")]
6use diesel::{
7    backend::Backend,
8    deserialize::{self, FromSql, FromSqlRow},
9    expression::AsExpression,
10    serialize::{self, IsNull, Output, ToSql},
11    sql_types::Text,
12    sqlite::Sqlite,
13};
14use libipld::{serde::from_ipld, Ipld};
15use schemars::{
16    gen::SchemaGenerator,
17    schema::{InstanceType, Metadata, Schema, SchemaObject, SingleOrVec},
18    JsonSchema,
19};
20use serde::{Deserialize, Serialize};
21use std::{borrow::Cow, fmt, module_path, str::FromStr};
22use ucan::ipld::Principle as Principal;
23
24/// [Principal] issuer of the [Invocation]. If omitted issuer is
25/// inferred from the [invocation] [task] audience.
26///
27/// [invocation]: crate::Invocation
28/// [task]: crate::Task
29/// [Principal]: Principal
30#[cfg(feature = "diesel")]
31#[cfg_attr(docsrs, doc(cfg(feature = "diesel")))]
32#[derive(Clone, Debug, Deserialize, Serialize, AsExpression, FromSqlRow, PartialEq)]
33#[diesel(sql_type = Text)]
34#[repr(transparent)]
35pub struct Issuer(Principal);
36
37/// [Principal] issuer of the invocation. If omitted issuer is
38/// inferred from the [invocation] [task] audience.
39///
40/// [invocation]: crate::Invocation
41/// [task]: crate::Task
42/// [Principal]: Principal
43#[cfg(not(feature = "diesel"))]
44#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
45#[repr(transparent)]
46pub struct Issuer(Principal);
47
48impl Issuer {
49    /// Create a new [Issuer], wrapping a [Principal].
50    pub fn new(principal: Principal) -> Self {
51        Issuer(principal)
52    }
53}
54
55impl fmt::Display for Issuer {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        let did_as_string = self.0.to_string();
58        write!(f, "{did_as_string}")
59    }
60}
61
62impl From<Issuer> for Ipld {
63    fn from(issuer: Issuer) -> Self {
64        let principal = issuer.0.to_string();
65        Ipld::String(principal)
66    }
67}
68
69impl TryFrom<Ipld> for Issuer {
70    type Error = Error<Unit>;
71
72    fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
73        let s = from_ipld::<String>(ipld)?;
74        Ok(Issuer(Principal::from_str(&s)?))
75    }
76}
77
78#[cfg(feature = "diesel")]
79#[cfg_attr(docsrs, doc(cfg(feature = "diesel")))]
80impl ToSql<Text, Sqlite> for Issuer {
81    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
82        out.set_value(self.0.to_string());
83        Ok(IsNull::No)
84    }
85}
86
87#[cfg(feature = "diesel")]
88#[cfg_attr(docsrs, doc(cfg(feature = "diesel")))]
89impl<DB> FromSql<Text, DB> for Issuer
90where
91    DB: Backend,
92    String: FromSql<Text, DB>,
93{
94    fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
95        let s = String::from_sql(bytes)?;
96        Ok(Issuer(Principal::from_str(&s)?))
97    }
98}
99
100impl JsonSchema for Issuer {
101    fn schema_name() -> String {
102        "iss".to_owned()
103    }
104
105    fn schema_id() -> Cow<'static, str> {
106        Cow::Borrowed(formatcp!("{}::Issuer", module_path!()))
107    }
108
109    fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
110        let schema = SchemaObject {
111            instance_type: Some(SingleOrVec::Single(InstanceType::String.into())),
112            metadata: Some(Box::new(Metadata {
113                title: Some("Issuer".to_string()),
114                description: Some("Principal that issued the receipt".to_string()),
115                ..Default::default()
116            })),
117            ..Default::default()
118        };
119        schema.into()
120    }
121}
122
123#[cfg(test)]
124mod test {
125    use super::*;
126
127    #[test]
128    fn ser_de() {
129        let issuer = Issuer::new(Principal::from_str("did:example:alice").unwrap());
130        let ser = serde_json::to_string(&issuer).unwrap();
131        let de = serde_json::from_str(&ser).unwrap();
132
133        assert_eq!(issuer, de);
134    }
135}