1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::*;
use uniffi_meta::TraitKind;
pub fn protocol(int: &general::Interface, context: &Context) -> Result<Protocol> {
Ok(match &int.imp {
ObjectImpl::Struct | ObjectImpl::Trait(TraitKind::RustOnly) => {
// Interface that's only implemented in Rust:
// - Give the interface the main name and append the `Protocol` suffix to the protocol
// - Make the protocol inherit from `typing.Protocol`, since the #2264 doesn't affect
// these interfaces
Protocol {
name: format!("{}Protocol", names::type_name(&int.name)),
base_classes: vec!["typing.Protocol".to_string()],
methods: int.methods.clone().map_node(context)?,
docstring: int.docstring.clone(),
}
}
ObjectImpl::Trait(TraitKind::Both | TraitKind::ForeignOnly) => {
// Trait interface that can be implemented in Python.
// - Give the protocol the main name and append the `Impl` suffix to the interface
// - Don't make the protocol inherit from `typing.Protocol`. We're going to inherit
// from it so it's not a typical Python protocol
// (http://github.com/mozilla/uniffi-rs/issues/2264).
Protocol {
name: names::type_name(&int.name),
base_classes: vec![],
methods: int.methods.clone().map_node(context)?,
docstring: int.docstring.clone(),
}
}
})
}
pub fn name(int: &general::Interface) -> String {
// Interface name, see `protocol` for a discussion of the logic here
match &int.imp {
ObjectImpl::Struct | ObjectImpl::Trait(TraitKind::RustOnly) => names::type_name(&int.name),
ObjectImpl::Trait(TraitKind::Both | TraitKind::ForeignOnly) => {
names::type_name(&format!("{}Impl", int.name))
}
}
}
pub fn base_classes(int: &general::Interface, context: &Context) -> Result<Vec<String>> {
let mut base_classes = vec![];
base_classes.push(protocol(int, context)?.name);
if int.self_type.is_used_as_error {
base_classes.push("Exception".to_string());
}
for t in int.trait_impls.iter() {
let (name, namespace) = match &t.trait_ty.ty {
Type::Interface {
name,
namespace,
imp,
..
} => {
// For trait interfaces implement in Rust-only, the protocol has `Protocol` appended.
// Trait interfaces with foreign implementations don't have that
match imp {
ObjectImpl::Trait(TraitKind::RustOnly) => {
(format!("{name}Protocol"), namespace)
}
ObjectImpl::Trait(TraitKind::Both | TraitKind::ForeignOnly) => {
(name.to_string(), namespace)
}
ObjectImpl::Struct => {
bail!("Objects can only inherit from traits, not other objects")
}
}
}
Type::CallbackInterface {
name, namespace, ..
} => (name.to_string(), namespace),
_ => bail!("trait_ty {:?} isn't a trait", t),
};
let name = names::type_name(&name);
let fq = match context.external_package_name(namespace)? {
None => name.clone(),
Some(package) => format!("{package}.{name}"),
};
base_classes.push(fq);
}
Ok(base_classes)
}
pub fn map_constructors(
interface_name: &str,
constructors: Vec<general::Constructor>,
context: &Context,
) -> Result<Vec<Constructor>> {
constructors
.into_iter()
.map(|c| {
if c.callable.is_primary_constructor() && c.callable.is_async() {
bail!("Async primary constructors not supported but {interface_name} has one");
}
c.map_node(context)
})
.collect()
}