gglib_core/ports/
download.rs1use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8
9use crate::download::{DownloadError, Quantization};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Resolution {
18 pub quantization: Quantization,
20 pub files: Vec<ResolvedFile>,
22 pub is_sharded: bool,
24}
25
26impl Resolution {
27 pub fn filenames(&self) -> Vec<String> {
29 self.files.iter().map(|f| f.path.clone()).collect()
30 }
31
32 pub fn total_size(&self) -> Option<u64> {
34 let sizes: Option<Vec<u64>> = self.files.iter().map(|f| f.size).collect();
35 sizes.map(|s| s.iter().sum())
36 }
37
38 pub fn first_file(&self) -> Option<&str> {
40 self.files.first().map(|f| f.path.as_str())
41 }
42
43 pub const fn file_count(&self) -> usize {
45 self.files.len()
46 }
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
51pub struct ResolvedFile {
52 pub path: String,
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub size: Option<u64>,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub oid: Option<String>,
60}
61
62impl ResolvedFile {
63 pub fn new(path: impl Into<String>) -> Self {
65 Self {
66 path: path.into(),
67 size: None,
68 oid: None,
69 }
70 }
71
72 pub fn with_size(path: impl Into<String>, size: u64) -> Self {
74 Self {
75 path: path.into(),
76 size: Some(size),
77 oid: None,
78 }
79 }
80
81 pub fn with_size_and_oid(path: impl Into<String>, size: u64, oid: Option<String>) -> Self {
83 Self {
84 path: path.into(),
85 size: Some(size),
86 oid,
87 }
88 }
89}
90
91#[async_trait]
108pub trait QuantizationResolver: Send + Sync {
109 async fn resolve(
114 &self,
115 repo_id: &str,
116 quantization: Quantization,
117 ) -> Result<Resolution, DownloadError>;
118
119 async fn list_available(&self, repo_id: &str) -> Result<Vec<Quantization>, DownloadError>;
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_resolution_methods() {
129 let resolution = Resolution {
130 quantization: Quantization::Q4KM,
131 files: vec![
132 ResolvedFile::with_size("model.gguf", 1000),
133 ResolvedFile::with_size("model-00001-of-00002.gguf", 500),
134 ],
135 is_sharded: true,
136 };
137
138 assert_eq!(resolution.file_count(), 2);
139 assert_eq!(resolution.total_size(), Some(1500));
140 assert_eq!(resolution.first_file(), Some("model.gguf"));
141 assert_eq!(
142 resolution.filenames(),
143 vec!["model.gguf", "model-00001-of-00002.gguf"]
144 );
145 }
146
147 #[test]
148 fn test_resolved_file_creation() {
149 let file = ResolvedFile::new("test.gguf");
150 assert_eq!(file.path, "test.gguf");
151 assert_eq!(file.size, None);
152
153 let file_with_size = ResolvedFile::with_size("test.gguf", 1024);
154 assert_eq!(file_with_size.size, Some(1024));
155 }
156}