Skip to content

Commit aa718f6

Browse files
Create ParsedAttribute struct and module, and move attributes into there
1 parent cd1d58b commit aa718f6

File tree

11 files changed

+119
-83
lines changed

11 files changed

+119
-83
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
2+
// SPDX-FileContributor: Ben Ford <[email protected]>
3+
//
4+
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
6+
use crate::{parser, syntax::path::path_compare_str};
7+
use std::collections::BTreeMap;
8+
use syn::{spanned::Spanned, Attribute, Error, Result};
9+
10+
pub struct ParsedAttribute<'a> {
11+
pub cxx_qt_attrs: BTreeMap<&'a str, &'a Attribute>,
12+
pub passthrough_attrs: BTreeMap<&'a str, &'a Attribute>,
13+
}
14+
15+
/// Iterate the attributes of the method to extract cfg attributes
16+
pub fn extract_cfgs(attrs: &[Attribute]) -> Vec<Attribute> {
17+
attrs
18+
.iter()
19+
.filter(|attr| path_compare_str(attr.meta.path(), &["cfg"]))
20+
.cloned()
21+
.collect()
22+
}
23+
24+
/// Iterate the attributes of the method to extract Doc attributes (doc comments are parsed as this)
25+
pub fn extract_docs(attrs: &[Attribute]) -> Vec<Attribute> {
26+
attrs
27+
.iter()
28+
.filter(|attr| path_compare_str(attr.meta.path(), &["doc"]))
29+
.cloned()
30+
.collect()
31+
}
32+
33+
impl<'a> ParsedAttribute<'a> {
34+
/// Collects a Map of all attributes found from the allowed list
35+
/// Will error if an attribute which is not in the allowed list is found
36+
pub fn require_attributes(
37+
attrs: &'a [Attribute],
38+
allowed: &'a [&str],
39+
) -> Result<ParsedAttribute<'a>> {
40+
let mut output = BTreeMap::default();
41+
for attr in attrs {
42+
let index = allowed
43+
.iter()
44+
.position(|string| path_compare_str(attr.meta.path(), &parser::split_path(string)));
45+
if let Some(index) = index {
46+
output.insert(allowed[index], attr); // TODO: Doesn't error on duplicates
47+
} else {
48+
return Err(Error::new(
49+
attr.span(),
50+
format!(
51+
"Unsupported attribute! The only attributes allowed on this item are\n{}",
52+
allowed.join(", ")
53+
),
54+
));
55+
}
56+
}
57+
Ok(Self {
58+
cxx_qt_attrs: output,
59+
passthrough_attrs: Default::default(),
60+
})
61+
}
62+
63+
// Wrapper methods for the internal BTreeMaps
64+
// TODO: Refactor usage to use more specialised methods / rename
65+
66+
/// Search in first the CXX-Qt, and then passthrough attributes by key
67+
pub fn get(&self, key: &str) -> Option<&Attribute> {
68+
self.cxx_qt_attrs
69+
.get(key)
70+
.or(self.passthrough_attrs.get(key))
71+
.map(|attr| &**attr)
72+
}
73+
74+
/// Check if CXX-Qt or passthrough attributes contains a particular key
75+
pub fn contains_key(&self, key: &str) -> bool {
76+
self.cxx_qt_attrs.contains_key(key) || self.passthrough_attrs.contains_key(key)
77+
}
78+
}

crates/cxx-qt-gen/src/parser/externcxxqt.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

6+
use crate::parser::attribute::ParsedAttribute;
67
use crate::{
7-
parser::{
8-
externqobject::ParsedExternQObject, require_attributes, signals::ParsedSignal,
9-
CaseConversion,
10-
},
8+
parser::{externqobject::ParsedExternQObject, signals::ParsedSignal, CaseConversion},
119
syntax::{attribute::attribute_get_path, expr::expr_to_string},
1210
};
1311
use syn::{
@@ -36,12 +34,12 @@ impl ParsedExternCxxQt {
3634
parent_namespace: Option<&str>,
3735
) -> Result<Self> {
3836
// TODO: support cfg on foreign mod blocks
39-
let attrs = require_attributes(
37+
let attrs = ParsedAttribute::require_attributes(
4038
&foreign_mod.attrs,
4139
&["namespace", "auto_cxx_name", "auto_rust_name"],
4240
)?;
4341

44-
let auto_case = CaseConversion::from_attrs(&attrs)?;
42+
let auto_case = CaseConversion::from_attrs(&attrs.cxx_qt_attrs)?;
4543

4644
let namespace = attrs
4745
.get("namespace")

crates/cxx-qt-gen/src/parser/externqobject.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55
use crate::naming::Name;
6-
use crate::parser::{parse_base_type, require_attributes, CaseConversion};
6+
use crate::parser::attribute::ParsedAttribute;
7+
use crate::parser::{parse_base_type, CaseConversion};
78
use syn::{ForeignItemType, Ident, Result};
89

910
/// A representation of a QObject to be generated in an extern C++ block
@@ -32,9 +33,9 @@ impl ParsedExternQObject {
3233
module_ident: &Ident,
3334
parent_namespace: Option<&str>,
3435
) -> Result<ParsedExternQObject> {
35-
let attributes = require_attributes(&ty.attrs, &Self::ALLOWED_ATTRS)?;
36+
let attrs = ParsedAttribute::require_attributes(&ty.attrs, &Self::ALLOWED_ATTRS)?;
3637

37-
let base_class = parse_base_type(&attributes)?;
38+
let base_class = parse_base_type(&attrs.cxx_qt_attrs)?;
3839

3940
Ok(Self {
4041
name: Name::from_ident_and_attrs(

crates/cxx-qt-gen/src/parser/externrustqt.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

66
use crate::naming::cpp::err_unsupported_item;
7+
use crate::parser::attribute::ParsedAttribute;
78
use crate::parser::inherit::ParsedInheritedMethod;
89
use crate::parser::method::ParsedMethod;
910
use crate::parser::qobject::ParsedQObject;
1011
use crate::parser::signals::ParsedSignal;
11-
use crate::parser::{require_attributes, CaseConversion};
12+
use crate::parser::CaseConversion;
1213
use crate::syntax::attribute::attribute_get_path;
1314
use crate::syntax::expr::expr_to_string;
1415
use crate::syntax::foreignmod::ForeignTypeIdentAlias;
@@ -38,12 +39,12 @@ impl ParsedExternRustQt {
3839
parent_namespace: Option<&str>,
3940
) -> Result<Self> {
4041
// TODO: support cfg on foreign mod blocks
41-
let attrs = require_attributes(
42+
let attrs = ParsedAttribute::require_attributes(
4243
&foreign_mod.attrs,
4344
&["namespace", "auto_cxx_name", "auto_rust_name"],
4445
)?;
4546

46-
let auto_case = CaseConversion::from_attrs(&attrs)?;
47+
let auto_case = CaseConversion::from_attrs(&attrs.cxx_qt_attrs)?;
4748

4849
let mut extern_rustqt_block = Self {
4950
unsafety: foreign_mod.unsafety,

crates/cxx-qt-gen/src/parser/inherit.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

6-
use crate::parser::{
7-
extract_cfgs, extract_docs, method::MethodFields, require_attributes, CaseConversion,
8-
};
6+
use crate::parser::attribute::{extract_cfgs, extract_docs, ParsedAttribute};
7+
use crate::parser::{method::MethodFields, CaseConversion};
98
use core::ops::Deref;
109
use quote::format_ident;
1110
use std::ops::DerefMut;
@@ -32,7 +31,7 @@ impl ParsedInheritedMethod {
3231
];
3332

3433
pub fn parse(method: ForeignItemFn, auto_case: CaseConversion) -> Result<Self> {
35-
require_attributes(&method.attrs, &Self::ALLOWED_ATTRS)?;
34+
ParsedAttribute::require_attributes(&method.attrs, &Self::ALLOWED_ATTRS)?;
3635
let docs = extract_docs(&method.attrs);
3736
let cfgs = extract_cfgs(&method.attrs);
3837

crates/cxx-qt-gen/src/parser/method.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// SPDX-FileContributor: Andrew Hayzen <[email protected]>
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
5+
use crate::parser::attribute::{extract_cfgs, ParsedAttribute};
56
use crate::parser::CaseConversion;
67
use crate::{
78
naming::Name,
8-
parser::{extract_cfgs, parameter::ParsedFunctionParameter, require_attributes},
9+
parser::parameter::ParsedFunctionParameter,
910
syntax::{foreignmod, types},
1011
};
1112
use core::ops::Deref;
@@ -121,13 +122,14 @@ impl ParsedMethod {
121122
unsafe_block: bool,
122123
) -> Result<Self> {
123124
let fields = MethodFields::parse(method, auto_case)?;
124-
let attrs = require_attributes(&fields.method.attrs, &Self::ALLOWED_ATTRS)?;
125+
let attrs =
126+
ParsedAttribute::require_attributes(&fields.method.attrs, &Self::ALLOWED_ATTRS)?;
125127
let cfgs = extract_cfgs(&fields.method.attrs);
126128

127129
// Determine if the method is invokable
128130
let is_qinvokable = attrs.contains_key("qinvokable");
129131
let is_pure = attrs.contains_key("cxx_pure");
130-
let specifiers = ParsedQInvokableSpecifiers::from_attrs(attrs);
132+
let specifiers = ParsedQInvokableSpecifiers::from_attrs(attrs.cxx_qt_attrs);
131133

132134
Ok(Self {
133135
method_fields: fields,

crates/cxx-qt-gen/src/parser/mod.rs

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

6+
pub mod attribute;
67
pub mod constructor;
78
pub mod cxxqtdata;
89
pub mod externcxxqt;
@@ -18,10 +19,8 @@ pub mod qobject;
1819
pub mod signals;
1920
pub mod trait_impl;
2021

21-
use crate::{
22-
naming::TypeNames,
23-
syntax::{expr::expr_to_string, path::path_compare_str},
24-
};
22+
use crate::parser::attribute::ParsedAttribute;
23+
use crate::{naming::TypeNames, syntax::expr::expr_to_string};
2524
use convert_case::Case;
2625
use cxxqtdata::ParsedCxxQtData;
2726
use std::collections::BTreeMap;
@@ -87,24 +86,6 @@ impl CaseConversion {
8786
}
8887
}
8988

90-
/// Iterate the attributes of the method to extract cfg attributes
91-
pub fn extract_cfgs(attrs: &[Attribute]) -> Vec<Attribute> {
92-
attrs
93-
.iter()
94-
.filter(|attr| path_compare_str(attr.meta.path(), &["cfg"]))
95-
.cloned()
96-
.collect()
97-
}
98-
99-
/// Iterate the attributes of the method to extract Doc attributes (doc comments are parsed as this)
100-
pub fn extract_docs(attrs: &[Attribute]) -> Vec<Attribute> {
101-
attrs
102-
.iter()
103-
.filter(|attr| path_compare_str(attr.meta.path(), &["doc"]))
104-
.cloned()
105-
.collect()
106-
}
107-
10889
/// Splits a path by :: separators e.g. "cxx_qt::bridge" becomes ["cxx_qt", "bridge"]
10990
fn split_path(path_str: &str) -> Vec<&str> {
11091
let path = if path_str.contains("::") {
@@ -115,32 +96,6 @@ fn split_path(path_str: &str) -> Vec<&str> {
11596
path
11697
}
11798

118-
/// Collects a Map of all attributes found from the allowed list
119-
/// Will error if an attribute which is not in the allowed list is found
120-
pub fn require_attributes<'a>(
121-
attrs: &'a [Attribute],
122-
allowed: &'a [&str],
123-
) -> Result<BTreeMap<&'a str, &'a Attribute>> {
124-
let mut output = BTreeMap::default();
125-
for attr in attrs {
126-
let index = allowed
127-
.iter()
128-
.position(|string| path_compare_str(attr.meta.path(), &split_path(string)));
129-
if let Some(index) = index {
130-
output.insert(allowed[index], attr); // Doesn't error on duplicates
131-
} else {
132-
return Err(Error::new(
133-
attr.span(),
134-
format!(
135-
"Unsupported attribute! The only attributes allowed on this item are\n{}",
136-
allowed.join(", ")
137-
),
138-
));
139-
}
140-
}
141-
Ok(output)
142-
}
143-
14499
// Extract base identifier from attribute
145100
pub fn parse_base_type(attributes: &BTreeMap<&str, &Attribute>) -> Result<Option<Ident>> {
146101
attributes
@@ -174,7 +129,7 @@ impl PassthroughMod {
174129

175130
Self {
176131
items,
177-
docs: extract_docs(&module.attrs),
132+
docs: attribute::extract_docs(&module.attrs),
178133
module_ident: module.ident,
179134
vis: module.vis,
180135
}
@@ -196,7 +151,7 @@ pub struct Parser {
196151

197152
impl Parser {
198153
fn parse_mod_attributes(module: &mut ItemMod) -> Result<Option<String>> {
199-
let attrs = require_attributes(&module.attrs, &["doc", "cxx_qt::bridge"])?;
154+
let attrs = ParsedAttribute::require_attributes(&module.attrs, &["doc", "cxx_qt::bridge"])?;
200155
let mut namespace = None;
201156

202157
// Check for the cxx_qt::bridge attribute

crates/cxx-qt-gen/src/parser/qenum.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

6-
use crate::parser::{extract_cfgs, extract_docs, CaseConversion};
7-
use crate::{naming::Name, parser::require_attributes, syntax::path::path_compare_str};
6+
use crate::parser::attribute::{extract_cfgs, extract_docs, ParsedAttribute};
7+
use crate::parser::CaseConversion;
8+
use crate::{naming::Name, syntax::path::path_compare_str};
89
use quote::ToTokens;
910
use syn::{Attribute, Ident, ItemEnum, Result, Variant};
1011

@@ -60,7 +61,7 @@ impl ParsedQEnum {
6061
parent_namespace: Option<&str>,
6162
module: &Ident,
6263
) -> Result<Self> {
63-
require_attributes(&qenum.attrs, &Self::ALLOWED_ATTRS)?;
64+
ParsedAttribute::require_attributes(&qenum.attrs, &Self::ALLOWED_ATTRS)?;
6465
let cfgs = extract_cfgs(&qenum.attrs);
6566
let docs = extract_docs(&qenum.attrs);
6667

crates/cxx-qt-gen/src/parser/qnamespace.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

6-
use crate::parser::require_attributes;
6+
use crate::parser::attribute::ParsedAttribute;
77
use syn::{ItemMacro, LitStr, Result};
88

99
pub struct ParsedQNamespace {
@@ -15,7 +15,7 @@ pub struct ParsedQNamespace {
1515

1616
impl ParsedQNamespace {
1717
pub fn parse(mac: ItemMacro) -> Result<Self> {
18-
let attrs = require_attributes(&mac.attrs, &["qml_element"])?;
18+
let attrs = ParsedAttribute::require_attributes(&mac.attrs, &["qml_element"])?;
1919
let namespace_literal: LitStr = syn::parse2(mac.mac.tokens)?;
2020
let namespace = namespace_literal.value();
2121
if namespace.contains(char::is_whitespace) {

crates/cxx-qt-gen/src/parser/qobject.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55

66
use crate::{
77
naming::Name,
8-
parser::{extract_cfgs, property::ParsedQProperty, require_attributes},
8+
parser::property::ParsedQProperty,
99
syntax::{expr::expr_to_string, foreignmod::ForeignTypeIdentAlias, path::path_compare_str},
1010
};
1111
#[cfg(test)]
1212
use quote::format_ident;
1313

14+
use crate::parser::attribute::{extract_cfgs, ParsedAttribute};
1415
use crate::parser::{parse_base_type, CaseConversion};
1516
use syn::{Attribute, Error, Ident, Meta, Result};
1617

@@ -85,13 +86,14 @@ impl ParsedQObject {
8586
module: &Ident,
8687
auto_case: CaseConversion,
8788
) -> Result<Self> {
88-
let attributes = require_attributes(&declaration.attrs, &Self::ALLOWED_ATTRS)?;
89+
let attributes =
90+
ParsedAttribute::require_attributes(&declaration.attrs, &Self::ALLOWED_ATTRS)?;
8991
// TODO: handle docs through to generation
9092
let cfgs = extract_cfgs(&declaration.attrs);
9193

9294
let has_qobject_macro = attributes.contains_key("qobject");
9395

94-
let base_class = parse_base_type(&attributes)?;
96+
let base_class = parse_base_type(&attributes.cxx_qt_attrs)?;
9597

9698
// Ensure that if there is no qobject macro that a base class is specificed
9799
if !has_qobject_macro && base_class.is_none() {
@@ -130,7 +132,7 @@ impl ParsedQObject {
130132
}
131133

132134
fn parse_qml_metadata(name: &Name, attrs: &[Attribute]) -> Result<Option<QmlElementMetadata>> {
133-
let attributes = require_attributes(attrs, &Self::ALLOWED_ATTRS)?;
135+
let attributes = ParsedAttribute::require_attributes(attrs, &Self::ALLOWED_ATTRS)?;
134136
if let Some(attr) = attributes.get("qml_element") {
135137
// Extract the name of the qml_element from macro, else use the c++ name
136138
// This will use the name provided by cxx_name if that attr was present

0 commit comments

Comments
 (0)