qt_build_utils/qml/
qmldir.rs1use crate::qml::{QmlFile, QmlUri};
7
8use std::ffi::OsStr;
9use std::io;
10
11pub struct QmlDirBuilder {
15 class_name: Option<String>,
16 depends: Vec<QmlUri>,
17 plugin: Option<(bool, String)>,
18 type_info: Option<String>,
19 uri: QmlUri,
20 qml_files: Vec<QmlFile>,
21}
22
23impl QmlDirBuilder {
24 pub fn new(uri: QmlUri) -> Self {
27 Self {
28 class_name: None,
29 depends: vec![],
30 plugin: None,
31 type_info: None,
32 qml_files: vec![],
33 uri,
34 }
35 }
36
37 pub fn write(self, writer: &mut impl io::Write) -> io::Result<()> {
39 writeln!(writer, "module {}", self.uri.as_dots())?;
41
42 if let Some((optional, name)) = self.plugin {
44 if optional {
45 writeln!(writer, "optional plugin {name}")?;
46 } else {
47 writeln!(writer, "plugin {name}")?;
48 }
49 }
50
51 if let Some(name) = self.class_name {
52 writeln!(writer, "classname {name}")?;
53 }
54
55 if let Some(file) = self.type_info {
56 writeln!(writer, "typeinfo {file}")?;
57 }
58
59 for depend in self.depends {
60 writeln!(writer, "depends {depend}")?;
61 }
62
63 writeln!(writer, "prefer :/qt/qml/{}/", self.uri.as_dirs())?;
65
66 for qml_file in &self.qml_files {
67 let is_qml_file = qml_file
68 .get_path()
69 .extension()
70 .map(|ext| ext.eq_ignore_ascii_case("qml"))
71 .unwrap_or_default();
72
73 if !is_qml_file {
74 panic!(
75 "QML file does not end with .qml: {}",
76 qml_file.get_path().display(),
77 );
78 }
79
80 let path = qml_file.get_path().display();
81 let qml_component_name = qml_file
82 .get_path()
83 .file_stem()
84 .and_then(OsStr::to_str)
85 .expect("Could not get qml file stem");
86
87 let singleton = if qml_file.is_singleton() {
88 "singleton "
89 } else {
90 ""
91 };
92
93 let version = if let Some((major, minor)) = qml_file.get_version() {
95 format!("{}.{}", major, minor)
96 } else {
97 "254.0".to_string()
98 };
99
100 writeln!(writer, "{singleton}{qml_component_name} {version} {path}",)
101 .expect("Could not write qmldir file");
102 }
103
104 Ok(())
105 }
106
107 pub fn class_name(mut self, class_name: impl Into<String>) -> Self {
116 self.class_name = Some(class_name.into());
117 self
118 }
119
120 pub fn depend(mut self, depend: impl Into<QmlUri>) -> Self {
122 self.depends.push(depend.into());
123 self
124 }
125
126 pub fn depends<T: Into<QmlUri>>(mut self, depends: impl IntoIterator<Item = T>) -> Self {
128 self.depends.extend(depends.into_iter().map(Into::into));
129 self
130 }
131
132 pub fn plugin(mut self, name: impl Into<String>, optional: bool) -> Self {
146 if self.plugin.is_some() {
149 panic!("Only zero or one plugin is supported currently");
150 }
151
152 self.plugin = Some((optional, name.into()));
153 self
154 }
155
156 pub fn qml_files(mut self, qml_files: impl IntoIterator<Item = impl Into<QmlFile>>) -> Self {
158 self.qml_files
159 .extend(qml_files.into_iter().map(|p| p.into()));
160 self
161 }
162
163 pub fn type_info(mut self, file: impl Into<String>) -> Self {
168 self.type_info = Some(file.into());
169 self
170 }
171
172 }
179
180#[cfg(test)]
181mod test {
182 use super::*;
183
184 #[test]
185 fn qml_dir() {
186 let mut result = Vec::new();
187 QmlDirBuilder::new(QmlUri::new(["com", "kdab"]))
188 .class_name("C")
189 .depends(["QtQuick", "com.kdab.a"])
190 .plugin("P", true)
191 .type_info("T")
192 .qml_files(["qml/Test.qml"])
193 .qml_files([QmlFile::from("qml/MySingleton.qml")
194 .singleton(true)
195 .version(1, 0)])
196 .qml_files([QmlFile::from("../AnotherFile.qml").version(2, 123)])
197 .write(&mut result)
198 .unwrap();
199 assert_eq!(
200 String::from_utf8(result).unwrap(),
201 "module com.kdab
202optional plugin P
203classname C
204typeinfo T
205depends QtQuick
206depends com.kdab.a
207prefer :/qt/qml/com/kdab/
208Test 254.0 qml/Test.qml
209singleton MySingleton 1.0 qml/MySingleton.qml
210AnotherFile 2.123 ../AnotherFile.qml
211"
212 );
213 }
214}