dpp_digital_link/linktype/
request.rs1use dpp_domain::AccessTier;
4
5use super::media_type::DppMediaType;
6use super::vocabulary::Gs1LinkType;
7
8#[derive(Debug, Clone)]
10pub struct ResolutionRequest {
11 pub link_type: Option<Gs1LinkType>,
13 pub media_type: Option<DppMediaType>,
15 pub access_tier: Option<AccessTier>,
18}
19
20impl ResolutionRequest {
21 pub fn from_accept_header(accept: &str) -> Self {
27 Self {
28 link_type: None,
29 media_type: parse_best_media_type(accept),
30 access_tier: None,
31 }
32 }
33}
34
35fn parse_best_media_type(accept: &str) -> Option<DppMediaType> {
38 let mut entries: Vec<(String, f32)> = accept
39 .split(',')
40 .filter_map(|entry| {
41 let mut parts = entry.trim().splitn(2, ';');
42 let media = parts.next()?.trim().to_owned();
43 if media.is_empty() {
44 return None;
45 }
46 let q = parts
47 .next()
48 .and_then(|p| p.trim().strip_prefix("q="))
49 .and_then(|q| q.parse::<f32>().ok())
50 .unwrap_or(1.0);
51 Some((media, q))
52 })
53 .collect();
54
55 entries.sort_by(|a, b| {
57 b.1.partial_cmp(&a.1)
58 .unwrap_or(std::cmp::Ordering::Equal)
59 .then_with(|| {
60 let a_wild = a.0.contains('*');
61 let b_wild = b.0.contains('*');
62 match (a_wild, b_wild) {
63 (false, true) => std::cmp::Ordering::Less,
64 (true, false) => std::cmp::Ordering::Greater,
65 _ => std::cmp::Ordering::Equal,
66 }
67 })
68 });
69
70 entries
71 .iter()
72 .filter(|(m, q)| *q > 0.0 && !m.contains('*'))
73 .map(|(m, _)| DppMediaType::parse(m))
74 .next()
75}