Skip to content

Commit 1c1a8d7

Browse files
Merge pull request #991 from MarijnS95/enum-functions
Generate `impl` blocks for associated enum functions
2 parents 5e2f7bd + 2915bde commit 1c1a8d7

23 files changed

+774
-209
lines changed

Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/analysis/enums.rs

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use super::{function_parameters::TransformationType, imports::Imports, *};
2+
use crate::{config::gobjects::GObject, env::Env, nameutil::*, traits::*};
3+
4+
use log::info;
5+
6+
#[derive(Debug, Default)]
7+
pub struct Info {
8+
pub full_name: String,
9+
pub type_id: library::TypeId,
10+
pub name: String,
11+
pub functions: Vec<functions::Info>,
12+
pub specials: special_functions::Infos,
13+
}
14+
15+
impl Info {
16+
pub fn type_<'a>(&self, library: &'a library::Library) -> &'a library::Enumeration {
17+
let type_ = library
18+
.type_(self.type_id)
19+
.maybe_ref()
20+
.unwrap_or_else(|| panic!("{} is not an enumeration.", self.full_name));
21+
type_
22+
}
23+
}
24+
25+
pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option<Info> {
26+
info!("Analyzing enumeration {}", obj.name);
27+
28+
if !obj.status.need_generate() {
29+
return None;
30+
}
31+
32+
if !obj
33+
.type_id
34+
.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
35+
{
36+
return None;
37+
}
38+
39+
let enumeration_tid = env.library.find_type(0, &obj.name)?;
40+
let type_ = env.type_(enumeration_tid);
41+
let enumeration: &library::Enumeration = type_.maybe_ref()?;
42+
43+
let name = split_namespace_name(&obj.name).1;
44+
45+
// Mark the type as available within the enum namespace:
46+
imports.add_defined(&format!("crate::{}", name));
47+
48+
let has_get_quark = enumeration.error_domain.is_some();
49+
if has_get_quark {
50+
imports.add("glib::Quark");
51+
imports.add("glib::error::ErrorDomain");
52+
}
53+
54+
let has_get_type = enumeration.glib_get_type.is_some();
55+
if has_get_type {
56+
imports.add("glib::Type");
57+
imports.add("glib::StaticType");
58+
imports.add("glib::value::SetValue");
59+
imports.add("glib::value::FromValue");
60+
imports.add("glib::value::FromValueOptional");
61+
}
62+
63+
if obj.generate_display_trait {
64+
imports.add("std::fmt");
65+
}
66+
67+
let mut functions = functions::analyze(
68+
env,
69+
&enumeration.functions,
70+
enumeration_tid,
71+
false,
72+
false,
73+
obj,
74+
imports,
75+
None,
76+
None,
77+
);
78+
79+
// Gir does not currently mark the first parameter of associated enum functions -
80+
// that are identical to its enum type - as instance parameter since most languages
81+
// do not support this.
82+
for f in &mut functions {
83+
if f.parameters.c_parameters.is_empty() {
84+
continue;
85+
}
86+
87+
let first_param = &mut f.parameters.c_parameters[0];
88+
89+
if first_param.typ == enumeration_tid {
90+
first_param.instance_parameter = true;
91+
92+
let t = f
93+
.parameters
94+
.transformations
95+
.iter_mut()
96+
.find(|t| t.ind_c == 0)
97+
.unwrap();
98+
99+
if let TransformationType::ToGlibScalar { name, .. } = &mut t.transformation_type {
100+
*name = "self".to_owned();
101+
} else {
102+
panic!(
103+
"Enumeration function instance param must be passed as scalar, not {:?}",
104+
t.transformation_type
105+
);
106+
}
107+
}
108+
}
109+
110+
let specials = special_functions::extract(&mut functions, type_, obj);
111+
112+
special_functions::analyze_imports(&specials, imports);
113+
114+
let info = Info {
115+
full_name: obj.name.clone(),
116+
type_id: enumeration_tid,
117+
name: name.to_owned(),
118+
functions,
119+
specials,
120+
};
121+
122+
Some(info)
123+
}

src/analysis/flags.rs

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use super::{function_parameters::TransformationType, imports::Imports, *};
2+
use crate::{config::gobjects::GObject, env::Env, nameutil::*, traits::*};
3+
4+
use log::info;
5+
6+
#[derive(Debug, Default)]
7+
pub struct Info {
8+
pub full_name: String,
9+
pub type_id: library::TypeId,
10+
pub name: String,
11+
pub functions: Vec<functions::Info>,
12+
pub specials: special_functions::Infos,
13+
}
14+
15+
impl Info {
16+
pub fn type_<'a>(&self, library: &'a library::Library) -> &'a library::Bitfield {
17+
let type_ = library
18+
.type_(self.type_id)
19+
.maybe_ref()
20+
.unwrap_or_else(|| panic!("{} is not an flags.", self.full_name));
21+
type_
22+
}
23+
}
24+
25+
pub fn new(env: &Env, obj: &GObject, imports: &mut Imports) -> Option<Info> {
26+
info!("Analyzing flags {}", obj.name);
27+
28+
if !obj.status.need_generate() {
29+
return None;
30+
}
31+
32+
if !obj
33+
.type_id
34+
.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
35+
{
36+
return None;
37+
}
38+
39+
let flags_tid = env.library.find_type(0, &obj.name)?;
40+
let type_ = env.type_(flags_tid);
41+
let flags: &library::Bitfield = type_.maybe_ref()?;
42+
43+
let name = split_namespace_name(&obj.name).1;
44+
45+
// Mark the type as available within the bitfield namespace:
46+
imports.add_defined(&format!("crate::{}", name));
47+
48+
let has_get_type = flags.glib_get_type.is_some();
49+
if has_get_type {
50+
imports.add("glib::Type");
51+
imports.add("glib::StaticType");
52+
imports.add("glib::value::SetValue");
53+
imports.add("glib::value::FromValue");
54+
imports.add("glib::value::FromValueOptional");
55+
}
56+
57+
if obj.generate_display_trait {
58+
imports.add("std::fmt");
59+
}
60+
61+
let mut functions = functions::analyze(
62+
env,
63+
&flags.functions,
64+
flags_tid,
65+
false,
66+
false,
67+
obj,
68+
imports,
69+
None,
70+
None,
71+
);
72+
73+
// Gir does not currently mark the first parameter of associated bitfield functions -
74+
// that are identical to its bitfield type - as instance parameter since most languages
75+
// do not support this.
76+
for f in &mut functions {
77+
if f.parameters.c_parameters.is_empty() {
78+
continue;
79+
}
80+
81+
let first_param = &mut f.parameters.c_parameters[0];
82+
83+
if first_param.typ == flags_tid {
84+
first_param.instance_parameter = true;
85+
86+
let t = f
87+
.parameters
88+
.transformations
89+
.iter_mut()
90+
.find(|t| t.ind_c == 0)
91+
.unwrap();
92+
93+
if let TransformationType::ToGlibScalar { name, .. } = &mut t.transformation_type {
94+
*name = "self".to_owned();
95+
} else {
96+
panic!(
97+
"Bitfield function instance param must be passed as scalar, not {:?}",
98+
t.transformation_type
99+
);
100+
}
101+
}
102+
}
103+
104+
let specials = special_functions::extract(&mut functions, type_, obj);
105+
106+
special_functions::analyze_imports(&specials, imports);
107+
108+
let info = Info {
109+
full_name: obj.name.clone(),
110+
type_id: flags_tid,
111+
name: name.to_owned(),
112+
functions,
113+
specials,
114+
};
115+
116+
Some(info)
117+
}

src/analysis/function_parameters.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -375,11 +375,8 @@ fn is_length(par: &library::Parameter) -> bool {
375375
if len >= 3 && &par.name[len - 3..len] == "len" {
376376
return true;
377377
}
378-
if par.name.find("length").is_some() {
379-
return true;
380-
}
381378

382-
false
379+
par.name.contains("length")
383380
}
384381

385382
fn has_length(env: &Env, typ: TypeId) -> bool {

src/analysis/functions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ fn analyze_async(
845845
}) = callback_info
846846
{
847847
// Checks for /*Ignored*/ or other error comments
848-
*commented |= callback_type.find("/*").is_some();
848+
*commented |= callback_type.contains("/*");
849849
let func_name = func.c_identifier.as_ref().unwrap();
850850
let finish_func_name = finish_function_name(func_name);
851851
let mut output_params = vec![];

src/analysis/imports.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::{library::Library, nameutil::crate_name, version::Version};
33
use std::borrow::Cow;
44
use std::cmp::Ordering;
55
use std::collections::btree_map::BTreeMap;
6+
use std::collections::HashSet;
67
use std::ops::{Deref, DerefMut};
78
use std::vec::IntoIter;
89

@@ -70,10 +71,8 @@ fn compare_imports(a: &(&String, &ImportConditions), b: &(&String, &ImportCondit
7071
pub struct Imports {
7172
/// Name of the current crate.
7273
crate_name: String,
73-
/// Name defined within current module. It doesn't need use declaration.
74-
///
75-
/// NOTE: Currently we don't need to support more than one such name.
76-
defined: Option<String>,
74+
/// Names defined within current module. It doesn't need use declaration.
75+
defined: HashSet<String>,
7776
defaults: ImportConditions,
7877
map: BTreeMap<String, ImportConditions>,
7978
}
@@ -82,7 +81,7 @@ impl Imports {
8281
pub fn new(gir: &Library) -> Imports {
8382
Imports {
8483
crate_name: make_crate_name(gir),
85-
defined: None,
84+
defined: HashSet::new(),
8685
defaults: ImportConditions::default(),
8786
map: BTreeMap::new(),
8887
}
@@ -91,7 +90,7 @@ impl Imports {
9190
pub fn with_defined(gir: &Library, name: &str) -> Imports {
9291
Imports {
9392
crate_name: make_crate_name(gir),
94-
defined: Some(name.to_owned()),
93+
defined: std::iter::once(name.to_owned()).collect(),
9594
defaults: ImportConditions::default(),
9695
map: BTreeMap::new(),
9796
}
@@ -120,19 +119,32 @@ impl Imports {
120119
self.defaults.clear();
121120
}
122121

123-
/// The goals of this function is to discard unwanted imports like "ffi" and "crate". It
122+
/// The goals of this function is to discard unwanted imports like "crate". It
124123
/// also extends the checks in case you are implementing "X". For example, you don't want to
125124
/// import "X" or "crate::X" in this case.
126125
fn common_checks(&self, name: &str) -> bool {
127-
if name == "crate::ffi" || (!name.contains("::") && name != "xlib") {
126+
// The ffi namespace is used directly, including it is a programmer error.
127+
assert_ne!(name, "crate::ffi");
128+
129+
if (!name.contains("::") && name != "xlib") || self.defined.contains(name) {
128130
false
129-
} else if let Some(ref defined) = self.defined {
130-
!((name.starts_with("crate::") && &name[7..] == defined) || name == defined)
131+
} else if let Some(name) = name.strip_prefix("crate::") {
132+
!self.defined.contains(name)
131133
} else {
132134
true
133135
}
134136
}
135137

138+
/// Declares that `name` is defined in scope
139+
///
140+
/// Removes existing imports from `self.map` and marks `name` as
141+
/// available to counter future import "requests".
142+
pub fn add_defined(&mut self, name: &str) {
143+
if self.defined.insert(name.to_owned()) {
144+
self.map.remove(name);
145+
}
146+
}
147+
136148
/// Declares that name should be available through its last path component.
137149
///
138150
/// For example, if name is `X::Y::Z` then it will be available as `Z`.

0 commit comments

Comments
 (0)