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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use serde::{Deserialize, Serialize};

use crate::types;

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct Opf {
    pub metadata: Metadata,
    pub manifest: Manifest,
    pub spine: Spine,
    pub guide: Guide,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Metadata {
    #[serde(rename = "contributor")]
    pub contributor: Contributor,
    #[serde(rename = "creator")]
    pub creator: Creator,
    #[serde(rename = "date", default)]
    pub date: String,
    #[serde(rename = "title", default)]
    pub title: String,
    #[serde(rename = "language", default)]
    pub language: String,
    #[serde(rename = "publisher", default)]
    pub publisher: String,
    #[serde(rename = "identifier", default)]
    pub identifiers: Vec<Identifier>,
    #[serde(rename = "meta", default)]
    pub metas: Vec<types::Meta>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Contributor {
    #[serde(rename = "@role")]
    pub role: String,
    #[serde(rename = "$text")]
    pub name: String,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Creator {
    #[serde(rename = "@file-as")]
    pub file_as: String,
    #[serde(rename = "@role")]
    pub role: String,
    #[serde(rename = "$text")]
    pub name: String,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Identifier {
    #[serde(rename = "@scheme")]
    pub scheme: String,
    #[serde(rename = "$value")]
    pub value: String,
    #[serde(rename = "@id", default)]
    pub id: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Manifest {
    #[serde(rename = "item", default)]
    pub items: Vec<Item>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Item {
    #[serde(rename = "@id")]
    pub id: String,
    #[serde(rename = "@href")]
    pub href: String,
    #[serde(rename = "@media-type")]
    pub media_type: String,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Spine {
    #[serde(rename = "@toc")]
    pub toc: String,
    #[serde(rename = "itemref", default)]
    pub itemrefs: Vec<ItemRef>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct ItemRef {
    #[serde(rename = "@idref")]
    pub id_ref: String,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Guide {
    #[serde(default)]
    pub reference: Vec<Reference>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Reference {
    #[serde(rename = "@type")]
    pub type_: String,
    #[serde(rename = "@title")]
    pub title: String,
    #[serde(rename = "@href")]
    pub href: String,
}

impl Opf {
    pub fn toc_path(&self) -> String {
        let toc_id = &self.spine.toc;
        let toc_metadata = self
            .manifest
            .items
            .iter()
            .find(|i| i.id.eq(toc_id))
            .unwrap();
        toc_metadata.href.clone()
    }

    /// Get the book's cover href.
    pub fn cover_path(&self) -> Option<String> {
        if let Some(first) = self.manifest.items.first() {
            if first.media_type.contains("image") {
                return Some(first.href.clone());
            }
        }

        None
    }

    /// Get item by id
    pub fn get_item(&self, id: &str) -> Option<&Item> {
        if let Some(item) = self.manifest.items.iter().find(|i| i.id == id) {
            return Some(item);
        }

        None
    }
}