radicle/node/policy/
config.rs1use core::fmt;
2use std::collections::HashSet;
3use std::ops;
4
5use log::error;
6use thiserror::Error;
7
8use crate::crypto::PublicKey;
9use crate::prelude::RepoId;
10use crate::storage::{Namespaces, ReadRepository as _, ReadStorage, RepositoryError};
11
12pub use crate::node::policy::store;
13pub use crate::node::policy::store::Error;
14pub use crate::node::policy::store::Store;
15pub use crate::node::policy::{Alias, FollowPolicy, Policy, Scope, SeedPolicy, SeedingPolicy};
16
17#[derive(Debug, Error)]
18pub enum NamespacesError {
19 #[error("failed to find policy for {rid}")]
20 FailedPolicy {
21 rid: RepoId,
22 #[source]
23 err: Error,
24 },
25 #[error("cannot fetch {rid} as it is not seeded")]
26 BlockedPolicy { rid: RepoId },
27 #[error("failed to get node policies for {rid}")]
28 FailedNodes {
29 rid: RepoId,
30 #[source]
31 err: Error,
32 },
33 #[error("failed to get delegates for {rid}")]
34 FailedDelegates {
35 rid: RepoId,
36 #[source]
37 err: RepositoryError,
38 },
39 #[error(transparent)]
40 Git(#[from] crate::git::raw::Error),
41 #[error("could not find any followed nodes for {rid}")]
42 NoFollowed { rid: RepoId },
43}
44
45pub struct Config<T> {
47 policy: SeedingPolicy,
49 store: Store<T>,
51}
52
53impl<T> fmt::Debug for Config<T> {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.debug_struct("Config")
58 .field("policy", &self.policy)
59 .field("store", &self.store)
60 .finish()
61 }
62}
63
64impl<T> Config<T> {
65 pub fn new(policy: SeedingPolicy, store: Store<T>) -> Self {
67 Self { policy, store }
68 }
69
70 pub fn is_seeding(&self, rid: &RepoId) -> Result<bool, Error> {
72 self.seed_policy(rid).map(|entry| entry.policy.is_allow())
73 }
74
75 pub fn seed_policy(&self, rid: &RepoId) -> Result<SeedPolicy, Error> {
78 Ok(self.store.seed_policy(rid)?.unwrap_or(SeedPolicy {
79 rid: *rid,
80 policy: self.policy,
81 }))
82 }
83
84 pub fn namespaces_for<S>(
85 &self,
86 storage: &S,
87 rid: &RepoId,
88 ) -> Result<Namespaces, NamespacesError>
89 where
90 S: ReadStorage,
91 {
92 use NamespacesError::*;
93
94 let entry = self
95 .seed_policy(rid)
96 .map_err(|err| FailedPolicy { rid: *rid, err })?;
97 match entry.policy {
98 SeedingPolicy::Block => {
99 error!(target: "service", "Attempted to fetch untracked repo {rid}");
100 Err(NamespacesError::BlockedPolicy { rid: *rid })
101 }
102 SeedingPolicy::Allow { scope: Scope::All } => Ok(Namespaces::All),
103 SeedingPolicy::Allow {
104 scope: Scope::Followed,
105 } => {
106 let nodes = self
107 .follow_policies()
108 .map_err(|err| FailedNodes { rid: *rid, err })?;
109
110 let mut followed: HashSet<_> = HashSet::new();
111 for node in nodes {
112 let node = match node {
113 Ok(node) => node,
114 Err(err) => {
115 log::warn!(target: "service", "Failed to read follow policy: {err}");
116 continue;
117 }
118 };
119 if node.policy == Policy::Allow {
120 followed.insert(node.nid);
121 }
122 }
123
124 if let Ok(repo) = storage.repository(*rid) {
125 let delegates = repo
126 .delegates()
127 .map_err(|err| FailedDelegates { rid: *rid, err })?
128 .map(PublicKey::from);
129 followed.extend(delegates);
130 };
131 if followed.is_empty() {
132 Ok(Namespaces::All)
136 } else {
137 Ok(Namespaces::Followed(followed))
138 }
139 }
140 }
141 }
142}
143
144impl<T> ops::Deref for Config<T> {
145 type Target = Store<T>;
146
147 fn deref(&self) -> &Self::Target {
148 &self.store
149 }
150}
151
152impl<T> ops::DerefMut for Config<T> {
153 fn deref_mut(&mut self) -> &mut Self::Target {
154 &mut self.store
155 }
156}