1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
use std::error::Error;
use std::fmt::{Display, Formatter, Write};
use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::path::Component::Normal;
use log::{debug, info, warn};
use tokio::{fs, io};
use crate::assets::AssetIndex;
use crate::util;
use crate::util::FileVerifyError;
use crate::version::DownloadInfo;
const INDEX_PATH: &'static str = "indexes";
const OBJECT_PATH: &'static str = "objects";
pub struct AssetRepository {
online: bool,
home: PathBuf
}
#[derive(Debug)]
pub enum AssetError {
InvalidId(Option<String>),
IO { what: &'static str, error: io::Error }
}
impl Display for AssetError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AssetError::InvalidId(None) => f.write_str("missing asset index id"),
AssetError::InvalidId(Some(id)) => write!(f, "invalid asset index id: {}", id),
AssetError::IO { what, error } => write!(f, "i/o error ({}): {}", what, error)
}
}
}
impl Error for AssetError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
AssetError::IO { error, .. } => Some(error),
_ => None
}
}
}
impl From<(&'static str, io::Error)> for AssetError {
fn from((what, error): (&'static str, io::Error)) -> Self {
AssetError::IO { what, error }
}
}
impl AssetRepository {
pub async fn new(online: bool, home: impl AsRef<Path>) -> Result<AssetRepository, io::Error> {
let home = home.as_ref().to_owned();
match fs::create_dir_all(&home).await {
Ok(_) => (),
Err(e) => match e.kind() {
ErrorKind::AlreadyExists => (),
_ => return Err(e)
}
};
Ok(AssetRepository {
online,
home
})
}
fn get_index_path(&self, id: &str) -> Result<PathBuf, AssetError> {
let indexes_path: &Path = (&self.home, INDEX_PATH).as_ref();
let Some(Normal(path)) = Path::new(id).components().last() else {
return Err(AssetError::InvalidId(id.into()));
};
let path = path.to_str().ok_or(AssetError::InvalidId(Some(path.to_string_lossy())))?;
// FIXME: change this once "add_extension" is stabilized
Ok((indexes_path, format!("{}.json", path)).into())
}
pub async fn load_index(&self, index: &DownloadInfo, id: Option<&str>) -> Result<AssetIndex, AssetError> {
let Some(id) = index.id.as_ref().map(|s| s.as_str()).or(id) else {
return Err(AssetError::InvalidId(None));
};
info!("Loading asset index {}", id);
let path = self.get_index_path(id)?;
debug!("Asset index {} is located at {}", id, path);
match util::verify_file(&path, index.size, index.sha1).await {
Ok(_) => todo!(), // load local index
Err(FileVerifyError::Open(_, e)) => match e.kind() {
ErrorKind::NotFound => (), // download
_ => return Err(("opening asset index", e).into())
},
Err(FileVerifyError::Integrity(_, e)) => {
info!("Asset index {} has mismatched integrity: {}, must download it.", id, e);
let _ = fs::remove_file(&path).await.map_err(|e| warn!("Error deleting modified index {}: {} (ignored)", id, e));
// download
},
Err(FileVerifyError::Read(_, e)) => return Err(("reading asset index", e).into())
}
if !self.online {
warn!("Must redownload asset index {}, but the launcher is in offline mode. Please try again in online mode.", id);
return todo!();
}
todo!()
}
}
|