Skip to content

Commit 422ad56

Browse files
committed
Implement Swift Bindings
1 parent 52a15a0 commit 422ad56

File tree

5 files changed

+448
-0
lines changed

5 files changed

+448
-0
lines changed

capi/bind_gen/src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod kotlin;
1010
mod node;
1111
mod python;
1212
mod ruby;
13+
mod swift;
1314

1415
use std::path::Path;
1516
use syntex_syntax::abi::Abi;
@@ -245,5 +246,10 @@ fn write_files(classes: &BTreeMap<String, Class>) -> Result<()> {
245246
python::write(BufWriter::new(File::create(&path)?), classes)?;
246247
path.pop();
247248

249+
path.push("swift");
250+
create_dir_all(&path)?;
251+
swift::write(&path, classes)?;
252+
path.pop();
253+
248254
Ok(())
249255
}

capi/bind_gen/src/swift/code.rs

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
use std::io::{Write, Result};
2+
use {Class, Function, Type, TypeKind};
3+
use heck::MixedCase;
4+
use std::collections::BTreeMap;
5+
6+
fn get_hl_type(ty: &Type) -> String {
7+
if ty.is_custom {
8+
match ty.kind {
9+
TypeKind::Ref => format!("{}Ref", ty.name),
10+
TypeKind::RefMut => format!("{}RefMut", ty.name),
11+
TypeKind::Value => ty.name.clone(),
12+
}
13+
} else if ty.name == "bool" {
14+
"Bool".to_string()
15+
} else if ty.name == "usize" {
16+
"size_t".to_string()
17+
} else {
18+
get_ll_type(ty).to_string()
19+
}
20+
}
21+
22+
fn get_ll_type(ty: &Type) -> &str {
23+
match (ty.kind, ty.name.as_str()) {
24+
(TypeKind::Ref, "c_char") => "String",
25+
(TypeKind::Ref, _) |
26+
(TypeKind::RefMut, _) => "UnsafeMutableRawPointer?",
27+
(_, t) if !ty.is_custom => {
28+
match t {
29+
"i8" => "Int8",
30+
"i16" => "Int16",
31+
"i32" => "Int32",
32+
"i64" => "Int64",
33+
"u8" => "UInt8",
34+
"u16" => "UInt16",
35+
"u32" => "UInt32",
36+
"u64" => "UInt64",
37+
"usize" => "size_t",
38+
"f32" => "Float",
39+
"f64" => "Double",
40+
"bool" => "Bool",
41+
"()" => "()",
42+
"c_char" => "UInt8",
43+
"Json" => "String",
44+
x => x,
45+
}
46+
}
47+
_ => "UnsafeMutableRawPointer?",
48+
}
49+
}
50+
51+
fn write_fn<W: Write>(mut writer: W, function: &Function) -> Result<()> {
52+
let is_static = function.is_static();
53+
let has_return_type = function.has_return_type();
54+
let return_type = get_hl_type(&function.output);
55+
let is_constructor = function.method == "new";
56+
57+
if is_constructor {
58+
write!(writer,
59+
r#"
60+
public init?("#)?;
61+
} else {
62+
write!(writer,
63+
r#"
64+
public{} func {}("#,
65+
if is_static { " static" } else { "" },
66+
function.method.to_mixed_case())?;
67+
}
68+
69+
for (i, &(ref name, ref typ)) in
70+
function
71+
.inputs
72+
.iter()
73+
.skip(if is_static { 0 } else { 1 })
74+
.enumerate() {
75+
if i != 0 {
76+
write!(writer, ", ")?;
77+
}
78+
write!(writer, "_ {}: {}", name.to_mixed_case(), get_hl_type(typ))?;
79+
}
80+
81+
if is_constructor {
82+
write!(writer,
83+
r#") {{
84+
super.init(ptr: Optional.none)
85+
"#)?;
86+
} else if has_return_type {
87+
write!(writer,
88+
r#") -> {}{} {{
89+
"#,
90+
return_type,
91+
if function.output.is_custom { "?" } else { "" })?;
92+
} else {
93+
write!(writer,
94+
r#") {{
95+
"#)?;
96+
}
97+
98+
for &(ref name, ref typ) in function.inputs.iter() {
99+
if typ.is_custom {
100+
write!(writer,
101+
r#"assert({name}.ptr != Optional.none)
102+
"#,
103+
name = if name == "this" {
104+
"self".to_string()
105+
} else {
106+
name.to_mixed_case()
107+
})?;
108+
}
109+
}
110+
111+
if has_return_type {
112+
if !is_constructor && function.output.is_custom {
113+
write!(writer, r#"let result = {}(ptr: "#, return_type)?;
114+
} else {
115+
write!(writer, "let result = ")?;
116+
}
117+
}
118+
119+
write!(writer, r#"LiveSplitCoreNative.{}("#, &function.name)?;
120+
121+
for (i, &(ref name, ref typ)) in function.inputs.iter().enumerate() {
122+
if i != 0 {
123+
write!(writer, ", ")?;
124+
}
125+
let ty_name = get_ll_type(typ);
126+
write!(writer,
127+
"{}",
128+
if name == "this" {
129+
"self.ptr".to_string()
130+
} else if typ.is_custom {
131+
format!("{}.ptr", name.to_mixed_case())
132+
} else if ty_name == "Bool" {
133+
format!("{} ? 1 : 0", name.to_mixed_case())
134+
} else {
135+
name.to_mixed_case()
136+
})?;
137+
}
138+
139+
write!(writer,
140+
"){}",
141+
if return_type == "Bool" { " != 0" } else { "" })?;
142+
143+
if !is_constructor && has_return_type && function.output.is_custom {
144+
write!(writer, r#")"#)?;
145+
}
146+
147+
for &(ref name, ref typ) in function.inputs.iter() {
148+
if typ.is_custom && typ.kind == TypeKind::Value {
149+
write!(writer,
150+
r#"
151+
{}.ptr = Optional.none"#,
152+
if name == "this" {
153+
"self".to_string()
154+
} else {
155+
name.to_mixed_case()
156+
})?;
157+
}
158+
}
159+
160+
if has_return_type {
161+
if function.output.is_custom {
162+
if is_constructor {
163+
write!(writer,
164+
r#"
165+
if result == Optional.none {{
166+
return nil
167+
}}"#)?;
168+
} else {
169+
write!(writer,
170+
r#"
171+
if result.ptr == Optional.none {{
172+
return Optional.none
173+
}}"#)?;
174+
}
175+
176+
}
177+
if return_type == "String" {
178+
write!(writer,
179+
r#"
180+
return String(cString: result!)"#)?;
181+
} else if is_constructor {
182+
write!(writer,
183+
r#"
184+
self.ptr = result"#)?;
185+
} else {
186+
write!(writer,
187+
r#"
188+
return result"#)?;
189+
}
190+
}
191+
192+
write!(writer,
193+
r#"
194+
}}"#)?;
195+
196+
Ok(())
197+
}
198+
199+
pub fn write<W: Write>(mut writer: W, classes: &BTreeMap<String, Class>) -> Result<()> {
200+
write!(writer,
201+
r#"import LiveSplitCoreNative
202+
"#)?;
203+
204+
for (class_name, class) in classes {
205+
let class_name_ref = format!("{}Ref", class_name);
206+
let class_name_ref_mut = format!("{}RefMut", class_name);
207+
208+
write!(writer,
209+
r#"
210+
public class {class} {{
211+
var ptr: UnsafeMutableRawPointer?"#,
212+
class = class_name_ref)?;
213+
214+
for function in &class.shared_fns {
215+
write_fn(&mut writer, function)?;
216+
}
217+
218+
if class_name == "SharedTimer" {
219+
write!(writer,
220+
"{}",
221+
r#"
222+
public func readWith(_ block: (TimerRef) -> ()) {
223+
let lock = self.read()!
224+
block(lock.timer()!)
225+
lock.dispose()
226+
}
227+
public func writeWith(_ block: (TimerRefMut) -> ()) {
228+
let lock = self.write()!
229+
block(lock.timer()!)
230+
lock.dispose()
231+
}"#)?;
232+
}
233+
234+
write!(writer,
235+
r#"
236+
init(ptr: UnsafeMutableRawPointer?) {{
237+
self.ptr = ptr
238+
}}
239+
}}
240+
241+
public class {class}: {base_class} {{"#,
242+
class = class_name_ref_mut,
243+
base_class = class_name_ref)?;
244+
245+
for function in &class.mut_fns {
246+
write_fn(&mut writer, function)?;
247+
}
248+
249+
write!(writer,
250+
r#"
251+
override init(ptr: UnsafeMutableRawPointer?) {{
252+
super.init(ptr: ptr)
253+
}}
254+
}}"#)?;
255+
256+
write!(writer,
257+
r#"
258+
259+
public class {class} : {base_class} {{
260+
private func drop() {{
261+
if self.ptr != Optional.none {{"#,
262+
class = class_name,
263+
base_class = class_name_ref_mut)?;
264+
265+
if let Some(function) = class.own_fns.iter().find(|f| f.method == "drop") {
266+
write!(writer,
267+
r#"
268+
LiveSplitCoreNative.{}(self.ptr)"#,
269+
function.name)?;
270+
}
271+
272+
write!(writer,
273+
r#"
274+
self.ptr = Optional.none
275+
}}
276+
}}
277+
deinit {{
278+
self.drop()
279+
}}
280+
public func dispose() {{
281+
self.drop()
282+
}}"#)?;
283+
284+
for function in class.static_fns.iter().chain(class.own_fns.iter()) {
285+
if function.method != "drop" {
286+
write_fn(&mut writer, function)?;
287+
}
288+
}
289+
290+
writeln!(writer,
291+
r#"
292+
override init(ptr: UnsafeMutableRawPointer?) {{
293+
super.init(ptr: ptr)
294+
}}
295+
}}"#)?;
296+
}
297+
298+
Ok(())
299+
}

0 commit comments

Comments
 (0)