Skip to content

Commit 9ba7a4b

Browse files
committed
add placeholders
1 parent 12add9c commit 9ba7a4b

File tree

3 files changed

+115
-11
lines changed

3 files changed

+115
-11
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ tower-http = { version = "0.6.6", features = ["trace", "fs"] }
2222
tower-sessions = "0.14.0"
2323
tracing = "=0.1"
2424
tracing-subscriber = "=0.3"
25+
svg = "0.18.0"

src/image.rs

Lines changed: 107 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ use axum::{
1111
response::{IntoResponse, Response},
1212
};
1313

14+
use rand::{Rng, SeedableRng, rngs::SmallRng, seq::IndexedRandom};
1415
use reqwest::header::CONTENT_TYPE;
16+
use svg::{
17+
Document,
18+
node::element::{Definitions, Group, Mask, Polygon, Polyline, Rectangle, Use, tag::Rectangle},
19+
};
1520
use tokio::{fs::File, process::Command, task::JoinSet};
1621
use tokio_util::io::ReaderStream;
1722

@@ -35,7 +40,7 @@ pub struct DataImage {
3540

3641
pub enum ResponseImage {
3742
File(File),
38-
Placeholder,
43+
Placeholder(u32),
3944
}
4045

4146
impl ProfileImage {
@@ -70,7 +75,7 @@ impl ProfileImage {
7075

7176
pub async fn get_with_placeholder(&self, size: u32) -> Result<ResponseImage, AppError> {
7277
match self.get(size).await {
73-
Err(AppError::ImageNotFound) => Ok(ResponseImage::Placeholder),
78+
Err(AppError::ImageNotFound) => Ok(ResponseImage::Placeholder(self.user_id)),
7479
other => other,
7580
}
7681
}
@@ -158,17 +163,108 @@ impl DataImage {
158163

159164
impl IntoResponse for ResponseImage {
160165
fn into_response(self) -> Response {
161-
let mut resp = match self {
162-
Self::Placeholder => Body::from(PLACEHOLDER),
163-
Self::File(file) => Body::from_stream(ReaderStream::new(file)),
166+
match self {
167+
Self::Placeholder(user_id) => {
168+
let mut body = Body::from(make_placeholder(user_id)).into_response();
169+
body.headers_mut()
170+
.insert(CONTENT_TYPE, HeaderValue::from_static("image/svg+xml"));
171+
body
172+
}
173+
Self::File(file) => {
174+
let mut body = Body::from_stream(ReaderStream::new(file)).into_response();
175+
body.headers_mut().insert(
176+
CONTENT_TYPE,
177+
HeaderValue::from_static(IMAGE_SAVE_TYPE.mime_type()),
178+
);
179+
body
180+
}
164181
}
165-
.into_response();
182+
}
183+
}
166184

167-
resp.headers_mut().insert(
168-
CONTENT_TYPE,
169-
HeaderValue::from_static(IMAGE_SAVE_TYPE.mime_type()),
170-
);
185+
fn make_placeholder(user_id: u32) -> Vec<u8> {
186+
let mut rand_gen = SmallRng::seed_from_u64(user_id as u64);
187+
let polygon_points = "9.0,0.0 4.5,7.794 -4.5,7.794 -9.0,0 -4.5,-7.794 4.5,-7.794";
188+
189+
let mask_polygon = Polygon::new()
190+
.set("points", polygon_points)
191+
.set("fill", "white");
192+
193+
let mask = Mask::new()
194+
.set("id", "poly")
195+
.set("mask-type", "luminance")
196+
.set("x", -100)
197+
.set("y", -100)
198+
.set("width", 200)
199+
.set("height", 200)
200+
.set("maskUnits", "userSpaceOnUse")
201+
.add(mask_polygon);
202+
203+
let cube_polygon = Polygon::new().set("points", polygon_points);
204+
205+
let cube_polyline = Polyline::new()
206+
.set("mask", "url(#poly)")
207+
.set("points", "9,0 0,0 -4.5,7.794 0,0 -4.5,-7.794")
208+
.set("style", "fill:none;stroke:#02020244;stroke-width:.6");
209+
210+
let cube_group = Group::new()
211+
.set("id", "cube")
212+
.add(cube_polygon)
213+
.add(cube_polyline);
171214

172-
resp
215+
let defs = Definitions::new().add(cube_group);
216+
217+
// TODO randomize rotation
218+
let mut main_group = Group::new().set("transform", "rotate(0 32 32)");
219+
220+
let use_data = vec![
221+
(22.0, 14.68),
222+
(42.0, 14.68),
223+
(12.0, 32.0),
224+
(52.0, 32.0),
225+
(22.0, 49.32),
226+
(42.0, 49.32),
227+
];
228+
229+
let colors = ["#FFBE0B", "#FF4037", "#FF006E", "#8338EC", "#3A86FF"];
230+
231+
for (x, y) in use_data {
232+
let use_element = Use::new()
233+
.set("xlink:href", "#cube")
234+
.set("x", x)
235+
.set("y", y)
236+
.set("fill", *colors.choose(&mut rand_gen).unwrap());
237+
main_group = main_group.add(use_element);
173238
}
239+
240+
// make the middle one zeus orange
241+
let use_element = Use::new()
242+
.set("xlink:href", "#cube")
243+
.set("x", 32)
244+
.set("y", 32)
245+
.set("fill", "#FF7F00");
246+
main_group = main_group.add(use_element);
247+
248+
let background = Rectangle::new()
249+
.set("width", "64")
250+
.set("height", "64")
251+
.set("x", "0")
252+
.set("y", "0")
253+
.set("fill", "#EEE");
254+
255+
let document = Document::new()
256+
.set("width", 64)
257+
.set("height", 64)
258+
.set("fill", "#ff7f00")
259+
.set("xmlns:xlink", "http://www.w3.org/1999/xlink")
260+
.set("id", "flynn")
261+
.add(background)
262+
.add(mask)
263+
.add(defs)
264+
.add(main_group);
265+
266+
let mut buffer = Vec::new();
267+
svg::write(&mut buffer, &document).unwrap();
268+
269+
buffer
174270
}

0 commit comments

Comments
 (0)