snix_store/proto/
grpc_pathinfoservice_wrapper.rs1use crate::nar::{NarCalculationService, RenderError};
2use crate::pathinfoservice::{PathInfo, PathInfoService};
3use crate::proto;
4use futures::{TryStreamExt, stream::BoxStream};
5use snix_castore::proto as castorepb;
6use std::ops::Deref;
7use tonic::{Request, Response, Result, Status, async_trait};
8use tracing::{instrument, warn};
9
10pub struct GRPCPathInfoServiceWrapper<PS, NS> {
11 path_info_service: PS,
12 nar_calculation_service: NS,
14}
15
16impl<PS, NS> GRPCPathInfoServiceWrapper<PS, NS> {
17 pub fn new(path_info_service: PS, nar_calculation_service: NS) -> Self {
18 Self {
19 path_info_service,
20 nar_calculation_service,
21 }
22 }
23}
24
25#[async_trait]
26impl<PS, NS> proto::path_info_service_server::PathInfoService for GRPCPathInfoServiceWrapper<PS, NS>
27where
28 PS: Deref<Target = dyn PathInfoService> + Send + Sync + 'static,
29 NS: NarCalculationService + Send + Sync + 'static,
30{
31 type ListStream = BoxStream<'static, tonic::Result<proto::PathInfo, Status>>;
32
33 #[instrument(skip_all)]
34 async fn get(
35 &self,
36 request: Request<proto::GetPathInfoRequest>,
37 ) -> Result<Response<proto::PathInfo>> {
38 match request.into_inner().by_what {
39 None => Err(Status::unimplemented("by_what needs to be specified")),
40 Some(proto::get_path_info_request::ByWhat::ByOutputHash(output_digest)) => {
41 let digest: [u8; 20] = output_digest
42 .to_vec()
43 .try_into()
44 .map_err(|_e| Status::invalid_argument("invalid output digest length"))?;
45 match self.path_info_service.get(digest).await {
46 Ok(None) => Err(Status::not_found("PathInfo not found")),
47 Ok(Some(path_info)) => Ok(Response::new(proto::PathInfo::from(path_info))),
48 Err(e) => {
49 warn!(err = %e, "failed to get PathInfo");
50 Err(e.into())
51 }
52 }
53 }
54 }
55 }
56
57 #[instrument(skip_all)]
58 async fn put(&self, request: Request<proto::PathInfo>) -> Result<Response<proto::PathInfo>> {
59 let path_info_proto = request.into_inner();
60
61 let path_info = PathInfo::try_from(path_info_proto)
62 .map_err(|e| Status::invalid_argument(format!("Invalid path info: {e}")))?;
63
64 match self.path_info_service.put(path_info).await {
67 Ok(path_info_new) => Ok(Response::new(proto::PathInfo::from(path_info_new))),
68 Err(e) => {
69 warn!(err = %e, "failed to put PathInfo");
70 Err(e.into())
71 }
72 }
73 }
74
75 #[instrument(skip_all)]
76 async fn calculate_nar(
77 &self,
78 request: Request<castorepb::Entry>,
79 ) -> Result<Response<proto::CalculateNarResponse>> {
80 let root_node = request
81 .into_inner()
82 .try_into_anonymous_node()
83 .map_err(|e| {
84 warn!(err = %e, "invalid root node");
85 Status::invalid_argument("invalid root node")
86 })?;
87
88 match self.nar_calculation_service.calculate_nar(&root_node).await {
89 Ok((nar_size, nar_sha256)) => Ok(Response::new(proto::CalculateNarResponse {
90 nar_size,
91 nar_sha256: nar_sha256.to_vec().into(),
92 })),
93 Err(e) => {
94 warn!(err = %e, "error during NAR calculation");
95 Err(e.into())
96 }
97 }
98 }
99
100 #[instrument(skip_all, err)]
101 async fn list(
102 &self,
103 _request: Request<proto::ListPathInfoRequest>,
104 ) -> Result<Response<Self::ListStream>, Status> {
105 let stream = Box::pin(
106 self.path_info_service
107 .list()
108 .map_ok(proto::PathInfo::from)
109 .map_err(|e| Status::internal(e.to_string())),
110 );
111
112 Ok(Response::new(Box::pin(stream)))
113 }
114}
115
116impl From<RenderError> for tonic::Status {
117 fn from(value: RenderError) -> Self {
118 match value {
119 RenderError::BlobNotFound(_, _) => Self::not_found(value.to_string()),
120 RenderError::DirectoryNotFound(_, _) => Self::not_found(value.to_string()),
121 RenderError::NARWriterError(_) => Self::internal(value.to_string()),
122 RenderError::StoreError(_) => Self::internal(value.to_string()),
123 RenderError::UnexpectedBlobMeta(_, _, _, _) => Self::internal(value.to_string()),
124 }
125 }
126}