1use std::{collections::HashMap, path::Path};
2
3use xmlserde_derives::XmlDeserialize;
4
5use crate::{namespace::Namespace, version::Version, ParserError};
6
7#[derive(Clone, Debug, XmlDeserialize)]
8#[xmlserde(root = b"include")]
9#[xmlserde(deny_unknown_fields)]
10pub struct NamespaceInclude {
11 #[xmlserde(name = b"name", ty = "attr")]
12 name: String,
13 #[xmlserde(name = b"version", ty = "attr")]
14 version: Version,
15}
16
17impl NamespaceInclude {
18 pub fn as_package(&self) -> String {
19 format!("{}-{}", self.name, self.version)
20 }
21
22 pub fn as_package_file(&self) -> String {
23 format!("{}-{}.gir", self.name, self.version)
24 }
25
26 pub fn name(&self) -> &str {
27 &self.name
28 }
29
30 pub fn version(&self) -> &Version {
31 &self.version
32 }
33}
34
35#[derive(Clone, Debug, XmlDeserialize)]
36#[xmlserde(root = b"c:include")]
37#[xmlserde(deny_unknown_fields)]
38pub struct HeaderInclude {
39 #[xmlserde(name = b"name", ty = "attr")]
40 name: String,
41}
42
43impl HeaderInclude {
44 pub fn name(&self) -> &str {
45 &self.name
46 }
47}
48
49#[derive(Clone, Debug, XmlDeserialize)]
50#[xmlserde(root = b"package")]
51#[xmlserde(deny_unknown_fields)]
52pub struct Package {
53 #[xmlserde(name = b"name", ty = "attr")]
54 name: String,
55}
56
57impl Package {
58 pub fn name(&self) -> &str {
59 &self.name
60 }
61}
62
63#[derive(Clone, Debug)]
64pub enum DocFormat {
65 GtkDocMarkdown,
66 GtkDocDocbook,
67 GiDocgen,
68 HotDoc,
69 Unknown,
70}
71
72impl xmlserde::XmlValue for DocFormat {
73 fn serialize(&self) -> String {
74 match self {
75 Self::GtkDocMarkdown => "gtk-doc-markdown",
76 Self::GtkDocDocbook => "gtk-doc-docbook",
77 Self::GiDocgen => "gi-docgen",
78 Self::HotDoc => "hotdoc",
79 Self::Unknown => "unknown",
80 }
81 .to_owned()
82 }
83
84 fn deserialize(s: &str) -> Result<Self, String> {
85 match s {
86 "gtk-doc-markdown" => Ok(Self::GtkDocMarkdown),
87 "gtk-doc-docbook" => Ok(Self::GtkDocDocbook),
88 "gi-docgen" => Ok(Self::GiDocgen),
89 "hotdoc" => Ok(Self::HotDoc),
90 "unknown" => Ok(Self::Unknown),
91 e => Err(format!("Invalid doc:format {e}")),
92 }
93 }
94}
95
96#[derive(Clone, Debug, XmlDeserialize)]
97#[xmlserde(root = b"repository")]
98#[xmlserde(deny_unknown_fields)]
99pub struct Repository {
100 #[xmlserde(name = b"version", ty = "attr")]
101 version: Option<Version>,
102 #[xmlserde(name = b"c:identifier-prefixes", ty = "attr")]
103 c_identifier_prefixes: Option<String>,
104 #[xmlserde(name = b"c:symbol-prefixes", ty = "attr")]
105 c_symbol_prefixes: Option<String>,
106 #[xmlserde(name = b"xmlns", ty = "attr")]
107 _xmlns: Option<String>,
108 #[xmlserde(name = b"xmlns:c", ty = "attr")]
109 _xmlns_c: Option<String>,
110 #[xmlserde(name = b"xmlns:glib", ty = "attr")]
111 _xmlns_glib: Option<String>,
112 #[xmlserde(name = b"xmlns:doc", ty = "attr")]
113 _xmlns_doc: Option<String>,
114 #[xmlserde(name = b"include", ty = "child")]
115 includes: Vec<NamespaceInclude>,
116 #[xmlserde(name = b"c:include", ty = "child")]
117 c_includes: Vec<HeaderInclude>,
118 #[xmlserde(name = b"package", ty = "child")]
119 packages: Vec<Package>,
120 #[xmlserde(name = b"namespace", ty = "child")]
121 namespace: Namespace,
122 #[xmlserde(name = b"doc:format", ty = "attr")]
123 doc_format: Option<DocFormat>,
124}
125
126impl Repository {
127 pub fn from_path_follow_namespaces_and_cache(
128 cache: &mut HashMap<String, Self>,
129 package_file: &str,
130 girs_dirs: impl AsRef<Path>,
131 ) -> Result<(), ParserError> {
132 let repo = Self::from_path(girs_dirs.as_ref().join(package_file))?;
133 if cache.contains_key(package_file) {
134 return Ok(());
135 }
136 for namespace in repo.namespace_includes() {
137 if !cache.contains_key(&namespace.as_package_file()) {
138 Self::from_path_follow_namespaces_and_cache(
139 cache,
140 &namespace.as_package_file(),
141 girs_dirs.as_ref(),
142 )?;
143 }
144 }
145 debug_assert_eq!(
146 package_file,
147 format!(
148 "{}-{}.gir",
149 repo.namespace().name(),
150 repo.namespace().version()
151 )
152 );
153 cache.insert(package_file.to_owned(), repo);
154 Ok(())
155 }
156
157 pub fn from_path_follow_namespaces(
158 package_file: &str,
159 girs_dirs: impl AsRef<Path>,
160 ) -> Result<HashMap<String, Self>, ParserError> {
161 let mut output = HashMap::new();
162 Self::from_path_follow_namespaces_and_cache(&mut output, package_file, girs_dirs)?;
163 Ok(output)
164 }
165
166 pub fn from_path(path: impl AsRef<Path>) -> Result<Self, ParserError> {
167 let content = std::fs::read_to_string(path)?;
168 let repository = xmlserde::xml_deserialize_from_str(&content).map_err(ParserError::Xml)?;
169 Ok(repository)
170 }
171
172 pub fn version(&self) -> Option<&Version> {
173 self.version.as_ref()
174 }
175
176 pub fn c_identifier_prefixes(&self) -> Option<&str> {
178 self.c_identifier_prefixes.as_deref()
179 }
180
181 pub fn c_symbol_prefixes(&self) -> Option<&str> {
183 self.c_symbol_prefixes.as_deref()
184 }
185
186 pub fn namespace_includes(&self) -> &[NamespaceInclude] {
187 &self.includes
188 }
189
190 pub fn header_includes(&self) -> &[HeaderInclude] {
191 &self.c_includes
192 }
193
194 pub fn packages(&self) -> &[Package] {
195 &self.packages
196 }
197
198 pub fn namespace(&self) -> &Namespace {
199 &self.namespace
200 }
201
202 pub fn doc_format(&self) -> Option<&DocFormat> {
203 self.doc_format.as_ref()
204 }
205}