1use serde::{Deserialize, Serialize};
2use std::collections::HashSet;
3use std::path::Path;
4
5#[derive(Clone, Debug)]
6pub struct ContentTypesBuilder {
7 ext: HashSet<String>,
8 inner: Option<ContentTypes>,
9}
10
11impl ContentTypesBuilder {
12 pub fn add(&mut self, path: &Path) {
13 if let Some(ext) = path.extension() {
14 if let Some(ext) = ext.to_str() {
15 if !self.ext.contains(ext) {
16 let mime = mime_guess::from_ext(ext).first_or_octet_stream();
17 self.inner.as_mut().unwrap().rules.push(Rule::Default {
18 ext: ext.into(),
19 mime: mime.to_string(),
20 });
21 self.ext.insert(ext.to_string());
22 }
23 }
24 }
25 }
26
27 pub fn finish(&mut self) -> ContentTypes {
28 self.inner.take().unwrap()
29 }
30}
31
32impl Default for ContentTypesBuilder {
33 fn default() -> Self {
34 Self {
35 ext: Default::default(),
36 inner: Some(Default::default()),
37 }
38 }
39}
40
41#[derive(Clone, Debug, Deserialize, Serialize)]
42#[serde(rename(serialize = "Types"))]
43pub struct ContentTypes {
44 #[serde(default = "default_namespace")]
45 xmlns: String,
46 pub rules: Vec<Rule>,
47}
48
49impl Default for ContentTypes {
50 fn default() -> Self {
51 Self {
52 xmlns: default_namespace(),
53 rules: vec![
54 Rule::Override {
55 part_name: "/AppxBlockMap.xml".into(),
56 mime: "application/vnd.ms-appx.blockmap+xml".into(),
57 },
58 Rule::Override {
59 part_name: "/AppxSignature.p7x".into(),
60 mime: "application/vnd.ms-appx.signature".into(),
61 },
62 ],
63 }
64 }
65}
66
67#[derive(Clone, Debug, Deserialize, Serialize)]
68pub enum Rule {
69 Default {
70 #[serde(rename(serialize = "Extension"))]
71 ext: String,
72 #[serde(rename(serialize = "ContentType"))]
73 mime: String,
74 },
75 Override {
76 #[serde(rename(serialize = "PartName"))]
77 part_name: String,
78 #[serde(rename(serialize = "ContentType"))]
79 mime: String,
80 },
81}
82
83fn default_namespace() -> String {
84 "http://schemas.openxmlformats.org/package/2006/content-types".into()
85}