Skip to main content

gir_parser/
repository.rs

1use std::{collections::HashMap, path::Path, str::FromStr};
2
3use xmlserde_derives::XmlDeserialize;
4
5use crate::{namespace::Namespace, version::Version, ParserError};
6
7#[derive(Clone, Debug, PartialEq, Eq, Hash, 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, PartialEq, Eq, Hash, 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, PartialEq, Eq, Hash, 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, XmlDeserialize, Copy, Default)]
64#[xmlserde(root = b"doc:format")]
65#[xmlserde(deny_unknown_fields)]
66struct DocFormatChild {
67    #[xmlserde(name = b"name", ty = "attr")]
68    format: DocFormat,
69}
70
71#[derive(Clone, Debug, PartialEq, Eq, Default, Copy)]
72pub enum DocFormat {
73    GtkDocMarkdown,
74    GtkDocDocbook,
75    GiDocgen,
76    Hotdoc,
77    #[default]
78    Unknown,
79}
80
81impl xmlserde::XmlValue for DocFormat {
82    fn serialize(&self) -> String {
83        match self {
84            Self::GtkDocMarkdown => "gtk-doc-markdown",
85            Self::GtkDocDocbook => "gtk-doc-docbook",
86            Self::GiDocgen => "gi-docgen",
87            Self::Hotdoc => "hotdoc",
88            Self::Unknown => "unknown",
89        }
90        .to_owned()
91    }
92
93    fn deserialize(s: &str) -> Result<Self, String> {
94        match s {
95            "gtk-doc-markdown" => Ok(Self::GtkDocMarkdown),
96            "gtk-doc-docbook" => Ok(Self::GtkDocDocbook),
97            "gi-docgen" => Ok(Self::GiDocgen),
98            "hotdoc" => Ok(Self::Hotdoc),
99            "unknown" => Ok(Self::Unknown),
100            e => Err(format!("Invalid doc:format {e}")),
101        }
102    }
103}
104
105#[derive(Clone, Debug, XmlDeserialize)]
106#[xmlserde(root = b"repository")]
107#[xmlserde(deny_unknown_fields)]
108pub struct Repository {
109    #[xmlserde(name = b"version", ty = "attr")]
110    version: Option<Version>,
111    #[xmlserde(name = b"c:identifier-prefixes", ty = "attr")]
112    c_identifier_prefixes: Option<String>,
113    #[xmlserde(name = b"c:symbol-prefixes", ty = "attr")]
114    c_symbol_prefixes: Option<String>,
115    #[xmlserde(name = b"xmlns", ty = "attr")]
116    _xmlns: Option<String>,
117    #[xmlserde(name = b"xmlns:c", ty = "attr")]
118    _xmlns_c: Option<String>,
119    #[xmlserde(name = b"xmlns:glib", ty = "attr")]
120    _xmlns_glib: Option<String>,
121    #[xmlserde(name = b"xmlns:doc", ty = "attr")]
122    _xmlns_doc: Option<String>,
123    #[xmlserde(name = b"include", ty = "child")]
124    includes: Vec<NamespaceInclude>,
125    #[xmlserde(name = b"c:include", ty = "child")]
126    c_includes: Vec<HeaderInclude>,
127    #[xmlserde(name = b"package", ty = "child")]
128    packages: Vec<Package>,
129    #[xmlserde(name = b"namespace", ty = "child")]
130    namespace: Namespace,
131    #[xmlserde(name = b"doc:format", ty = "child")]
132    doc_format_child: Option<DocFormatChild>,
133}
134
135impl Repository {
136    pub fn from_path_follow_namespaces_and_cache(
137        cache: &mut HashMap<String, Self>,
138        package_file: &str,
139        girs_dirs: impl AsRef<Path>,
140    ) -> Result<(), ParserError> {
141        let repo = Self::from_path(girs_dirs.as_ref().join(package_file))?;
142        if cache.contains_key(package_file) {
143            return Ok(());
144        }
145        for namespace in repo.namespace_includes() {
146            if !cache.contains_key(&namespace.as_package_file()) {
147                Self::from_path_follow_namespaces_and_cache(
148                    cache,
149                    &namespace.as_package_file(),
150                    girs_dirs.as_ref(),
151                )?;
152            }
153        }
154        debug_assert_eq!(
155            package_file,
156            format!(
157                "{}-{}.gir",
158                repo.namespace().name(),
159                repo.namespace().version()
160            )
161        );
162        cache.insert(package_file.to_owned(), repo);
163        Ok(())
164    }
165
166    pub fn from_path_follow_namespaces(
167        package_file: &str,
168        girs_dirs: impl AsRef<Path>,
169    ) -> Result<HashMap<String, Self>, ParserError> {
170        let mut output = HashMap::new();
171        Self::from_path_follow_namespaces_and_cache(&mut output, package_file, girs_dirs)?;
172        Ok(output)
173    }
174
175    pub fn from_path(path: impl AsRef<Path>) -> Result<Self, ParserError> {
176        let content = std::fs::read_to_string(path)?;
177        let repository = xmlserde::xml_deserialize_from_str(&content).map_err(ParserError::Xml)?;
178        Ok(repository)
179    }
180
181    pub fn version(&self) -> Option<&Version> {
182        self.version.as_ref()
183    }
184
185    pub fn c_identifier_prefixes(&self) -> impl Iterator<Item = &str> {
186        self.c_identifier_prefixes
187            .as_ref()
188            .filter(|ps| !ps.is_empty())
189            .map(|ps| ps.split(','))
190            .into_iter()
191            .flatten()
192    }
193
194    pub fn c_symbol_prefixes(&self) -> impl Iterator<Item = &str> {
195        self.c_symbol_prefixes
196            .as_ref()
197            .filter(|ps| !ps.is_empty())
198            .map(|ps| ps.split(','))
199            .into_iter()
200            .flatten()
201    }
202
203    pub fn namespace_includes(&self) -> &[NamespaceInclude] {
204        &self.includes
205    }
206
207    pub fn header_includes(&self) -> &[HeaderInclude] {
208        &self.c_includes
209    }
210
211    pub fn packages(&self) -> &[Package] {
212        &self.packages
213    }
214
215    pub fn namespace(&self) -> &Namespace {
216        &self.namespace
217    }
218
219    pub fn doc_format(&self) -> DocFormat {
220        self.doc_format_child.map(|c| c.format).unwrap_or_default()
221    }
222}
223
224impl FromStr for Repository {
225    type Err = ParserError;
226
227    fn from_str(s: &str) -> Result<Self, Self::Err> {
228        let repository = xmlserde::xml_deserialize_from_str(s).map_err(ParserError::Xml)?;
229        Ok(repository)
230    }
231}