wayver's git archive


a simple self-hosted git server
git clone https://git.wayver.dev/bile

src/utils/extractor.rs@7016b1e2e8158b6496a17a520c7f6da5dc38e9ee

raw
Date Commit Message Author Files + -
2026-02-17 21:07 initial mvp wayverd 74 10800 0
...

1use axum::{
2    extract,
3    http::{self, StatusCode, request},
4    response::{self, IntoResponse as _},
5};
6
7use crate::utils::{Error, Result};
8
9fn get_param<'x>(ext: &'x http::Extensions, name: &str) -> Option<&'x str> {
10    let raw = ext.get::<extract::RawPathParams>()?;
11
12    for (key, value) in raw {
13        if key == name {
14            return Some(value);
15        }
16    }
17
18    None
19}
20
21pub struct Commit(pub String);
22
23impl<S> extract::FromRequestParts<S> for Commit
24where
25    S: Sync,
26{
27    type Rejection = response::Response;
28
29    async fn from_request_parts(
30        parts: &mut request::Parts,
31        _state: &S,
32    ) -> Result<Self, Self::Rejection> {
33        let Some(raw) = get_param(&parts.extensions, "commit") else {
34            return Err(Error::new(StatusCode::NOT_FOUND, "page not found").into_response());
35        };
36
37        if raw.is_empty() {
38            return Err(
39                Error::new(StatusCode::NOT_FOUND, "commit does not exist in repo").into_response(),
40            );
41        }
42
43        for c in raw.bytes() {
44            if !c.is_ascii_hexdigit() {
45                return Err(
46                    Error::new(StatusCode::NOT_FOUND, "commit does not exist in repo")
47                        .into_response(),
48                );
49            }
50        }
51
52        Ok(Self(raw.to_string()))
53    }
54}
55
56pub struct Obj(pub String);
57
58impl<S> extract::FromRequestParts<S> for Obj
59where
60    S: Sync,
61{
62    type Rejection = response::Response;
63
64    async fn from_request_parts(
65        parts: &mut request::Parts,
66        _state: &S,
67    ) -> Result<Self, Self::Rejection> {
68        let Some(raw) = get_param(&parts.extensions, "obj") else {
69            return Err(Error::new(StatusCode::NOT_FOUND, "page not found").into_response());
70        };
71
72        if raw.is_empty() {
73            return Err(
74                Error::new(StatusCode::NOT_FOUND, "object does not exist in repo").into_response(),
75            );
76        }
77
78        Ok(Self(raw.to_string()))
79    }
80}
81
82pub struct ObjectName(pub String);
83
84impl<S> extract::FromRequestParts<S> for ObjectName
85where
86    S: Sync,
87{
88    type Rejection = response::Response;
89
90    async fn from_request_parts(
91        parts: &mut request::Parts,
92        _state: &S,
93    ) -> Result<Self, Self::Rejection> {
94        let Some(raw) = get_param(&parts.extensions, "object_name") else {
95            return Err(Error::new(StatusCode::NOT_FOUND, "page not found").into_response());
96        };
97
98        if raw.is_empty() {
99            return Err(
100                Error::new(StatusCode::NOT_FOUND, "object does not exist in repo").into_response(),
101            );
102        }
103
104        Ok(Self(raw.to_string()))
105    }
106}
107
108pub struct Ref(pub String);
109
110impl<S> extract::FromRequestParts<S> for Ref
111where
112    S: Sync,
113{
114    type Rejection = response::Response;
115
116    async fn from_request_parts(
117        parts: &mut request::Parts,
118        _state: &S,
119    ) -> Result<Self, Self::Rejection> {
120        let Some(raw) = get_param(&parts.extensions, "ref") else {
121            return Err(Error::new(StatusCode::NOT_FOUND, "page not found").into_response());
122        };
123
124        if raw.is_empty() {
125            return Err(
126                Error::new(StatusCode::NOT_FOUND, "ref does not exist in repo").into_response(),
127            );
128        }
129
130        Ok(Self(raw.to_string()))
131    }
132}
133
134pub struct RepoName(pub String);
135
136impl<S> extract::FromRequestParts<S> for RepoName
137where
138    S: Sync,
139{
140    type Rejection = response::Response;
141
142    async fn from_request_parts(
143        parts: &mut request::Parts,
144        _state: &S,
145    ) -> Result<Self, Self::Rejection> {
146        let Some(raw) = get_param(&parts.extensions, "repo_name") else {
147            return Err(Error::new(StatusCode::NOT_FOUND, "page not found").into_response());
148        };
149
150        if raw.is_empty() {
151            return Err(Error::new(StatusCode::NOT_FOUND, "repo does not exist").into_response());
152        }
153
154        Ok(Self(raw.to_string()))
155    }
156}
157
158pub struct Tag(pub String);
159
160impl<S> extract::FromRequestParts<S> for Tag
161where
162    S: Sync,
163{
164    type Rejection = response::Response;
165
166    async fn from_request_parts(
167        parts: &mut request::Parts,
168        _state: &S,
169    ) -> Result<Self, Self::Rejection> {
170        let Some(raw) = get_param(&parts.extensions, "tag") else {
171            return Err(Error::new(StatusCode::NOT_FOUND, "page not found").into_response());
172        };
173
174        if raw.is_empty() {
175            return Err(
176                Error::new(StatusCode::NOT_FOUND, "tag does not exist in repo").into_response(),
177            );
178        }
179
180        Ok(Self(raw.to_string()))
181    }
182}
183
184pub fn repo_name_checks(name: &str) -> Result<()> {
185    let name = name.trim();
186
187    if name.is_empty() {
188        return Err(Error::new(StatusCode::NOT_FOUND, "repo does not exist"));
189    }
190
191    Ok(())
192}
193
194pub fn commit_checks(name: &str) -> Result<()> {
195    let name = name.trim();
196
197    if name.is_empty() {
198        return Err(Error::new(StatusCode::NOT_FOUND, "repo does not exist"));
199    }
200
201    for c in name.bytes() {
202        if !c.is_ascii_hexdigit() {
203            return Err(Error::new(StatusCode::NOT_FOUND, "repo does not exist"));
204        }
205    }
206
207    Ok(())
208}
209