drawbridge_server/store/
repo.rs

1// SPDX-FileCopyrightText: 2022 Profian Inc. <opensource@profian.com>
2// SPDX-License-Identifier: Apache-2.0
3
4use super::{CreateError, Entity, GetError, Tag};
5
6use std::ops::Deref;
7
8use drawbridge_type::digest::{Algorithms, ContentDigest};
9use drawbridge_type::{Meta, RepositoryConfig, TagEntry, TagName};
10
11use anyhow::{anyhow, Context};
12use camino::{Utf8Path, Utf8PathBuf};
13
14#[repr(transparent)]
15#[derive(Copy, Clone, Debug)]
16pub struct Repository<'a, P = Utf8PathBuf>(Entity<'a, P>);
17
18impl<'a, P> Deref for Repository<'a, P> {
19    type Target = Entity<'a, P>;
20
21    fn deref(&self) -> &Self::Target {
22        &self.0
23    }
24}
25
26impl<'a, P> From<Entity<'a, P>> for Repository<'a, P> {
27    fn from(entity: Entity<'a, P>) -> Self {
28        Self(entity)
29    }
30}
31
32impl<'a, P: AsRef<Utf8Path>> Repository<'a, P> {
33    pub async fn get_json(&self) -> Result<RepositoryConfig, GetError<anyhow::Error>> {
34        self.get_content_json().await
35    }
36
37    pub async fn is_public(&self) -> Result<bool, GetError<anyhow::Error>> {
38        let conf = self.get_json().await?;
39        Ok(conf.public)
40    }
41
42    pub async fn tags(&self) -> Result<Vec<TagName>, GetError<anyhow::Error>> {
43        self.read_dir("tags")
44            .await?
45            .try_fold(vec![], |mut names, entry| {
46                let name = entry?
47                    .file_name()
48                    .context("failed to read tag name")?
49                    .parse()
50                    .context("failed to parse tag name")?;
51                names.push(name);
52                Ok(names)
53            })
54            .map_err(GetError::Internal)
55    }
56
57    pub async fn tags_json(&self) -> Result<(ContentDigest, Vec<u8>), GetError<anyhow::Error>> {
58        // TODO: Optimize hash computation
59        let tags = self.tags().await?;
60        let buf = serde_json::to_vec(&tags)
61            .context("failed to encode tags as JSON")
62            .map_err(GetError::Internal)?;
63        let (n, hash) = Algorithms::default()
64            .read_sync(&buf[..])
65            .context("failed to compute tag digest")
66            .map_err(GetError::Internal)?;
67        if n != buf.len() as u64 {
68            return Err(GetError::Internal(anyhow!(
69                "invalid amount of bytes read, expected: {}, got {n}",
70                buf.len(),
71            )));
72        }
73        Ok((hash, buf))
74    }
75
76    pub fn tag(&self, name: &TagName) -> Tag<'a, Utf8PathBuf> {
77        self.child(format!("tags/{name}")).into()
78    }
79
80    pub async fn create_tag(
81        &self,
82        name: &TagName,
83        meta: Meta,
84        entry: &TagEntry,
85    ) -> Result<Tag<'a, Utf8PathBuf>, CreateError<anyhow::Error>> {
86        let tag = self.tag(name);
87        tag.create_dir("").await?;
88        tag.create_json(meta, entry).await?;
89        Ok(tag)
90    }
91}