Skip to content

Commit 011efa8

Browse files
committed
qt-build-utils: add a QResourceBuilder
1 parent 58836c4 commit 011efa8

File tree

3 files changed

+184
-35
lines changed

3 files changed

+184
-35
lines changed

crates/qt-build-utils/src/builder/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ pub use qmldir::QmlDirBuilder;
88

99
mod qmluri;
1010
pub use qmluri::QmlUriBuilder;
11+
12+
mod qresource;
13+
pub use qresource::{QResource, QResourceBuilder, QResourceFile};
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
2+
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
3+
//
4+
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
/// An individial `<file>` line within a [QResourceBuilder]
7+
pub struct QResourceFile {
8+
alias: Option<String>,
9+
// TODO: compression
10+
// TODO: empty
11+
path: String,
12+
}
13+
14+
impl QResourceFile {
15+
/// Construct a [QResourceFile]
16+
fn new(path: String) -> Self {
17+
Self { alias: None, path }
18+
}
19+
20+
/// Specify an alias for the [QResourceFile]
21+
pub fn with_alias(mut self, alias: String) -> Self {
22+
self.alias = Some(alias);
23+
self
24+
}
25+
26+
fn build(self) -> String {
27+
// TODO: add slashes / sanitize
28+
let alias = self
29+
.alias
30+
.map(|alias| format!("alias=\"{alias}\""))
31+
.unwrap_or_default();
32+
let path = self.path;
33+
format!("<file {alias}>{path}</file>")
34+
}
35+
}
36+
37+
/// A `<qresource>` block within a [QResourceBuilder]
38+
pub struct QResource {
39+
language: Option<String>,
40+
prefix: Option<String>,
41+
files: Vec<QResourceFile>,
42+
}
43+
44+
impl Default for QResource {
45+
fn default() -> Self {
46+
Self::new()
47+
}
48+
}
49+
50+
impl QResource {
51+
/// Construct a [QResource]
52+
fn new() -> Self {
53+
Self {
54+
language: None,
55+
prefix: None,
56+
files: vec![],
57+
}
58+
}
59+
60+
/// Add a [QResourceFile] with the given path
61+
pub fn file(self, path: String) -> Self {
62+
self.file_with_opts(path, |file| file)
63+
}
64+
65+
/// Add a [QResourceFile] with the given path and apply additional options
66+
pub fn file_with_opts(
67+
mut self,
68+
path: String,
69+
opts: impl FnOnce(QResourceFile) -> QResourceFile,
70+
) -> Self {
71+
self.files.push(opts(QResourceFile::new(path)));
72+
self
73+
}
74+
75+
/// Specify a language for the `<qresource>`
76+
pub fn with_language(mut self, language: String) -> Self {
77+
self.language = Some(language);
78+
self
79+
}
80+
81+
/// Specify a prefix for the `<qresource>`
82+
pub fn with_prefix(mut self, prefix: String) -> Self {
83+
self.prefix = Some(prefix);
84+
self
85+
}
86+
87+
fn build(self) -> String {
88+
// TODO: add slashes / sanitise
89+
let language = self
90+
.language
91+
.map(|language| format!("language=\"{language}\""))
92+
.unwrap_or_default();
93+
let prefix = self
94+
.prefix
95+
.map(|prefix| format!("prefix=\"{prefix}\""))
96+
.unwrap_or_default();
97+
let files = self
98+
.files
99+
.into_iter()
100+
.map(QResourceFile::build)
101+
.collect::<Vec<_>>()
102+
.join("");
103+
format!("<qresource {language} {prefix}>{files}</qresource>")
104+
}
105+
}
106+
107+
/// A helper for building Qt resource collection files
108+
pub struct QResourceBuilder {
109+
resources: Vec<QResource>,
110+
}
111+
112+
impl Default for QResourceBuilder {
113+
fn default() -> Self {
114+
Self::new()
115+
}
116+
}
117+
118+
impl QResourceBuilder {
119+
/// Construct a [QResourceBuilder]
120+
pub fn new() -> Self {
121+
Self { resources: vec![] }
122+
}
123+
124+
/// Add a [QResource] to the builder with given options
125+
pub fn with_resource_opts(mut self, opts: impl FnOnce(QResource) -> QResource) -> Self {
126+
self.resources.push(opts(QResource::new()));
127+
self
128+
}
129+
130+
/// Convert to a string representation
131+
pub fn build(self) -> String {
132+
let resources = self
133+
.resources
134+
.into_iter()
135+
.map(QResource::build)
136+
.collect::<Vec<_>>()
137+
.join("");
138+
format!("<RCC>{resources}</RCC>")
139+
}
140+
}

crates/qt-build-utils/src/lib.rs

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#![allow(clippy::too_many_arguments)]
1717

1818
mod builder;
19-
pub use builder::{QmlDirBuilder, QmlUriBuilder};
19+
pub use builder::{QResource, QResourceBuilder, QResourceFile, QmlDirBuilder, QmlUriBuilder};
2020

2121
mod error;
2222
pub use error::QtBuildError;
@@ -185,41 +185,47 @@ impl QtBuild {
185185
let qrc_path =
186186
qml_module_dir.join(format!("qml_module_resources_{qml_uri_underscores}.qrc"));
187187
{
188-
fn qrc_file_line(file_path: &impl AsRef<Path>) -> String {
189-
let path_display = file_path.as_ref().display();
190-
format!(
191-
" <file alias=\"{}\">{}</file>\n",
192-
path_display,
193-
std::fs::canonicalize(file_path)
194-
.unwrap_or_else(|_| panic!("Could not canonicalize path {path_display}"))
195-
.display()
196-
)
197-
}
198-
199-
let mut qml_files_qrc = String::new();
200-
for file_path in qml_files {
201-
qml_files_qrc.push_str(&qrc_file_line(file_path));
202-
}
203-
for file_path in qrc_files {
204-
qml_files_qrc.push_str(&qrc_file_line(file_path));
205-
}
206-
207-
let mut qrc = File::create(&qrc_path).expect("Could not create qrc file");
208188
let qml_module_dir_str = qml_module_dir.to_str().unwrap();
209-
write!(
210-
qrc,
211-
r#"<RCC>
212-
<qresource prefix="/">
213-
<file alias="/qt/qml/{qml_uri_dirs}">{qml_module_dir_str}</file>
214-
</qresource>
215-
<qresource prefix="/qt/qml/{qml_uri_dirs}">
216-
{qml_files_qrc}
217-
<file alias="qmldir">{qml_module_dir_str}/qmldir</file>
218-
</qresource>
219-
</RCC>
220-
"#
221-
)
222-
.expect("Could note write qrc file");
189+
let qml_uri_dirs_prefix = format!("/qt/qml/{qml_uri_dirs}");
190+
let qrc_contents = QResourceBuilder::new()
191+
.with_resource_opts(|resource| {
192+
resource
193+
.with_prefix("/".to_string())
194+
.file_with_opts(qml_module_dir_str.to_string(), |file| {
195+
file.with_alias(qml_uri_dirs_prefix.clone())
196+
})
197+
})
198+
.with_resource_opts(|resource| {
199+
let mut resource = resource
200+
.with_prefix(qml_uri_dirs_prefix.clone())
201+
.file_with_opts(format!("{qml_module_dir_str}/qmldir"), |file| {
202+
file.with_alias("qmldir".to_string())
203+
});
204+
205+
fn resource_add_path(resource: QResource, path: &Path) -> QResource {
206+
let resolved = std::fs::canonicalize(path)
207+
.unwrap_or_else(|_| {
208+
panic!("Could not canonicalize path {}", path.display())
209+
})
210+
.display()
211+
.to_string();
212+
resource.file_with_opts(resolved, |file| {
213+
file.with_alias(path.display().to_string())
214+
})
215+
}
216+
217+
for path in qml_files {
218+
resource = resource_add_path(resource, path.as_ref());
219+
}
220+
for path in qrc_files {
221+
resource = resource_add_path(resource, path.as_ref());
222+
}
223+
224+
resource
225+
})
226+
.build();
227+
let mut qrc = File::create(&qrc_path).expect("Could not create qrc file");
228+
write!(qrc, "{}", qrc_contents).expect("Could note write qrc file");
223229
}
224230

225231
// Run qmlcachegen

0 commit comments

Comments
 (0)