drawbridge_server/store/
repo.rs1use 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 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}