]> luflow.net public git repositories - flow-web.git/blob - src/site/screenshots.rs
Initial commit.
[flow-web.git] / src / site / screenshots.rs
1 // luflow.net web site
2 // AGPL-3.0 License (see LICENSE)
3
4 use sailfish::Template;
5 use std::collections::HashMap;
6 use std::sync::{Arc, Mutex};
7
8 use crate::site::helper::Helper;
9 use crate::site::screenshot::Screenshot;
10
11 #[derive(Clone, Debug)]
12 pub struct ScreenshotsInfo {
13 pub title: String,
14 pub url: String,
15 }
16
17 impl ScreenshotsInfo {
18 pub fn new() -> Self {
19 Self {
20 title: String::new(),
21 url: String::new(),
22 }
23 }
24 }
25
26 pub struct ScreenshotsShared {
27 state: Mutex<ScreenshotsState>,
28 }
29
30 impl ScreenshotsShared {
31 pub fn new() -> Self {
32 Self {
33 state: Mutex::new(ScreenshotsState::new()),
34 }
35 }
36
37 pub fn shared(self) -> Arc<Self> {
38 Arc::new(self)
39 }
40
41 pub fn get_screenshots(&self, num_screenshots: usize, key: &str) -> Vec<Screenshot> {
42 let lock = self.state.lock().unwrap();
43 let mut screenshots = Vec::new();
44
45 if let Some(value) = lock.screenshots.get(key) {
46 for i in 0..num_screenshots {
47 if let Some(screenshot) = value.get(i) {
48 screenshots.push(screenshot.clone());
49 }
50 }
51 } else {
52 panic!("Did not find any screenshots for key: {}", key);
53 }
54
55 return screenshots;
56 }
57 }
58
59 #[derive(Template)]
60 #[template(path = "screenshots.stpl")]
61 #[derive(Clone, Debug)]
62 struct ScreenshotsState {
63 screenshots: HashMap<String, Vec<Screenshot>>,
64 screenshot_urls: HashMap<String, String>,
65 title: String,
66 url: String,
67 }
68
69 impl ScreenshotsState {
70 pub fn new() -> Self {
71 Self {
72 screenshots: HashMap::new(),
73 screenshot_urls: HashMap::new(),
74 title: String::new(),
75 url: String::new(),
76 }
77 }
78 }
79
80 pub async fn generate_screenshots(shared: Arc<ScreenshotsShared>) {
81 parse_files(shared.clone(), "screenshots").await;
82 }
83
84 async fn parse_files(shared: Arc<ScreenshotsShared>, base_dir: &str) {
85 let mut reader = tokio::fs::read_dir(base_dir).await.unwrap();
86 loop {
87 if let Some(f) = reader.next_entry().await.unwrap() {
88 let contents = tokio::fs::read_to_string(f.path()).await.unwrap();
89
90 parse_file(shared.clone(), contents);
91 update_screenshots(shared.clone());
92 generate(shared.clone());
93 } else {
94 break;
95 }
96 }
97 }
98
99 fn parse_file(shared: Arc<ScreenshotsShared>, contents: String) {
100 let mut screenshots_info = ScreenshotsInfo::new();
101 let mut screenshot = Screenshot::new();
102
103 for line in contents.lines() {
104 if line.len() == 0 {
105 continue;
106 }
107
108 let v: Vec<&str> = line.splitn(2, ':').collect();
109 assert_eq!(v.len(), 2);
110
111 if let Some(key) = v.get(0) {
112 if *key == "screenshots_title" {
113 if let Some(value) = v.get(1) {
114 screenshots_info.title = String::from(value.trim());
115 } else {
116 panic!("Unable to parse field: 'screenshots_title'.");
117 }
118 } else if *key == "screenshots_url" {
119 if let Some(value) = v.get(1) {
120 screenshots_info.url = String::from(value.trim());
121 } else {
122 panic!("Unable to parse field: 'screenshots_url'.");
123 }
124 } else if *key == "title" {
125 if let Some(value) = v.get(1) {
126 screenshot.title = String::from(value.trim());
127 } else {
128 panic!("Unable to parse field: 'title'.");
129 }
130 } else if *key == "image_min" {
131 if let Some(value) = v.get(1) {
132 screenshot.image_min = String::from(value.trim());
133 } else {
134 panic!("Unable to parse field: 'image_min'.");
135 }
136 } else if *key == "image_big" {
137 if let Some(value) = v.get(1) {
138 screenshot.image_big = String::from(value.trim());
139 } else {
140 panic!("Unable to parse field: 'image_big'.");
141 }
142 } else if *key == "url" {
143 if let Some(value) = v.get(1) {
144 screenshot.url = String::from(value.trim());
145 } else {
146 panic!("Unable to parse field: 'url'.");
147 }
148 }
149 }
150
151 // all info needed for one screenshot:
152 if screenshot.title.len() > 0
153 && screenshot.image_min.len() > 0
154 && screenshot.image_big.len() > 0
155 && screenshot.url.len() > 0
156 && screenshots_info.title.len() > 0
157 && screenshots_info.url.len() > 0
158 {
159 screenshot.screenshots_title = screenshots_info.title.clone();
160 screenshot.screenshots_url = screenshots_info.url.clone();
161
162 {
163 let mut lock = shared.state.lock().unwrap();
164
165 if let Some(value) = lock.screenshots.get_mut(&screenshots_info.title) {
166 value.push(screenshot);
167 } else {
168 lock.screenshot_urls
169 .insert(screenshots_info.title.clone(), screenshots_info.url.clone());
170 lock.screenshots
171 .insert(screenshots_info.title.clone(), vec![screenshot]);
172 }
173 }
174
175 // reset for new screenshot:
176 screenshot = Screenshot::new();
177 }
178 }
179
180 let mut lock = shared.state.lock().unwrap();
181
182 lock.title = screenshots_info.title.clone();
183 lock.url = screenshots_info.url.clone();
184 }
185
186 fn update_screenshots(shared: Arc<ScreenshotsShared>) {
187 // TODO: this could be done more efficiently:
188 // (need screenshots for each screenshot as they are linked below)
189 let mut lock = shared.state.lock().unwrap();
190 for (_key, value) in &mut lock.screenshots {
191 let v = value.clone();
192 for s in value {
193 s.screenshots = v.clone();
194 }
195 }
196 }
197
198 fn generate(shared: Arc<ScreenshotsShared>) {
199 let lock = shared.state.lock().unwrap();
200
201 for (_key, screenshots) in &lock.screenshots {
202 // create output dir needed:
203 Helper::create_dir_all(&Helper::get_output_dir().join(&lock.url));
204
205 // write page to disk:
206 Helper::write_file_sync(
207 &Helper::get_output_dir().join(&lock.url).join("index.html"),
208 &lock.render().unwrap().as_bytes(),
209 )
210 .unwrap();
211
212 // generate all individual screenshot pages:
213 for screenshot in screenshots {
214 screenshot.generate();
215 }
216 }
217 }