1use std::{io::Cursor, path::Path};
2
3use anyhow::{anyhow, Result};
4use serde::Deserialize;
5use tracing::info;
6
7use crate::Submission;
8
9#[derive(Debug, Clone)]
10pub struct FileSubmission {
11 user_id: u64,
12 assignment_id: u64,
13 attempt: u64,
14 file: CanvasFile,
15}
16
17impl std::fmt::Display for FileSubmission {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 write!(
20 f,
21 "{}_{}_{}_{}",
22 self.user_id,
23 self.assignment_id,
24 self.attempt,
25 self.file.filename()
26 )
27 }
28}
29
30impl FileSubmission {
31 pub fn new(submission: &Submission, file: CanvasFile) -> Self {
32 Self {
33 user_id: submission.user(),
34 assignment_id: submission.assignment(),
35 attempt: submission.attempt(),
36 file,
37 }
38 }
39
40 pub async fn download(&self, directiory: &Path) -> Result<()> {
41 let path = directiory.join(self.to_string());
42
43 info!(
44 "Downloading \"{}\" to {}",
45 self.file.url(),
46 path.to_str().unwrap()
47 );
48 self.file.download(&path).await
49 }
50}
51
52#[derive(Debug, Clone, Deserialize)]
53pub struct CanvasFile {
54 url: String,
55 filename: String,
56}
57
58impl CanvasFile {
59 pub fn url(&self) -> &str {
60 &self.url
61 }
62
63 pub fn filename(&self) -> &str {
64 &self.filename
65 }
66
67 pub async fn download(&self, path: &Path) -> Result<()> {
68 let response = reqwest::get(&self.url).await?;
69
70 std::fs::create_dir_all(
72 path.parent()
73 .ok_or(anyhow!("Path does not have a parent directory!"))?,
74 )?;
75
76 let mut file = std::fs::File::create(path)?;
77 let mut contents = Cursor::new(response.bytes().await?);
78 std::io::copy(&mut contents, &mut file)?;
79
80 Ok(())
81 }
82}