1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
use std::ops::Deref;

use anyhow::Result;
use cid::Cid;
use libipld_cbor::DagCborCodec;
use noosphere_storage::BlockStore;
use tokio::sync::OnceCell;

use crate::{
    data::AuthorityIpld,
    view::{Delegations, Revocations},
};

/// A view in to the authorizations (and associated revocations) that pertain
/// to sphere access
pub struct Authority<S: BlockStore> {
    cid: Cid,
    store: S,
    body: OnceCell<AuthorityIpld>,
}

impl<S> Authority<S>
where
    S: BlockStore,
{
    pub fn cid(&self) -> &Cid {
        &self.cid
    }

    pub fn at(cid: &Cid, store: &S) -> Self {
        Authority {
            cid: *cid,
            store: store.clone(),
            body: OnceCell::new(),
        }
    }

    /// Loads the underlying IPLD (if it hasn't been loaded already) and returns
    /// an owned copy of it
    pub async fn to_body(&self) -> Result<AuthorityIpld> {
        Ok(self
            .body
            .get_or_try_init(|| async { self.store.load::<DagCborCodec, _>(self.cid()).await })
            .await?
            .clone())
    }

    pub async fn at_or_empty<C>(cid: Option<C>, store: &mut S) -> Result<Authority<S>>
    where
        C: Deref<Target = Cid>,
    {
        Ok(match cid {
            Some(cid) => Self::at(&cid, store),
            None => Self::empty(store).await?,
        })
    }

    pub async fn empty(store: &mut S) -> Result<Self> {
        let ipld = AuthorityIpld::empty(store).await?;
        let cid = store.save::<DagCborCodec, _>(ipld).await?;

        Ok(Authority {
            cid,
            store: store.clone(),
            body: OnceCell::new(),
        })
    }

    pub async fn get_delegations(&self) -> Result<Delegations<S>> {
        let ipld = self.to_body().await?;

        Delegations::at_or_empty(Some(ipld.delegations), &mut self.store.clone()).await
    }

    pub async fn get_revocations(&self) -> Result<Revocations<S>> {
        let ipld = self.to_body().await?;

        Revocations::at_or_empty(Some(ipld.revocations), &mut self.store.clone()).await
    }
}