mirror of
https://github.com/aljazceru/breez-sdk-liquid.git
synced 2026-01-03 14:24:19 +01:00
Add code generation of React Native bindings
This commit is contained in:
@@ -8,6 +8,7 @@ rpath = true
|
||||
[workspace]
|
||||
members = [
|
||||
"ls-sdk-bindings",
|
||||
"ls-sdk-bindings/bindings-react-native",
|
||||
"ls-sdk-core",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
15
lib/ls-sdk-bindings/bindings-react-native/.gitignore
vendored
Normal file
15
lib/ls-sdk-bindings/bindings-react-native/.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# Generated files
|
||||
android/
|
||||
ios/
|
||||
ts/
|
||||
28
lib/ls-sdk-bindings/bindings-react-native/Cargo.toml
Normal file
28
lib/ls-sdk-bindings/bindings-react-native/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "bindings-react-native"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1.0.57", features = ["backtrace"] }
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
uniffi = { version = "0.23.0", features = ["bindgen-tests", "cli"] }
|
||||
uniffi_bindgen = "0.23.0"
|
||||
uniffi_macros = "0.23.0"
|
||||
camino = "1.1.1"
|
||||
log = "*"
|
||||
serde = "*"
|
||||
askama = { version = "0.11.1", default-features = false, features = ["config"] }
|
||||
toml = "0.5"
|
||||
clap = { version = "3.2.22", features = ["derive"] }
|
||||
heck = "0.4"
|
||||
paste = "1.0"
|
||||
once_cell = "1.12"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = { version = "0.23.0" }
|
||||
uniffi_bindgen = "0.23.0"
|
||||
anyhow = { version = "1.0.57", features = ["backtrace"] }
|
||||
19
lib/ls-sdk-bindings/bindings-react-native/README.md
Normal file
19
lib/ls-sdk-bindings/bindings-react-native/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# breez-sdk-rn-generator
|
||||
|
||||
This utility generates the liquid-swap-sdk React Native package code.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
brew install ktlint kotlin swiftformat
|
||||
```
|
||||
|
||||
```bash
|
||||
yarn global add tslint typescript
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
```
|
||||
cargo run
|
||||
```
|
||||
6
lib/ls-sdk-bindings/bindings-react-native/askama.toml
Normal file
6
lib/ls-sdk-bindings/bindings-react-native/askama.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[general]
|
||||
# Directories to search for templates, relative to the crate root.
|
||||
dirs = [ "src/gen_kotlin/templates", "src/gen_swift/templates", "src/gen_typescript/templates" ]
|
||||
|
||||
[[syntax]]
|
||||
name = "rn"
|
||||
5
lib/ls-sdk-bindings/bindings-react-native/makefile
Normal file
5
lib/ls-sdk-bindings/bindings-react-native/makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
SOURCES=$(sort $(wildcard ./src/*.rs ./src/**/*.rs))
|
||||
|
||||
codegen: $(SOURCES)
|
||||
cargo run
|
||||
|
||||
334
lib/ls-sdk-bindings/bindings-react-native/src/gen_kotlin/mod.rs
Normal file
334
lib/ls-sdk-bindings/bindings-react-native/src/gen_kotlin/mod.rs
Normal file
@@ -0,0 +1,334 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
|
||||
use askama::Template;
|
||||
use once_cell::sync::Lazy;
|
||||
use uniffi_bindgen::interface::*;
|
||||
|
||||
pub use uniffi_bindgen::bindings::kotlin::gen_kotlin::*;
|
||||
|
||||
use crate::generator::RNConfig;
|
||||
|
||||
static IGNORED_FUNCTIONS: Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
let list: Vec<&str> = vec!["init"];
|
||||
HashSet::from_iter(list.into_iter().map(|s| s.to_string()))
|
||||
});
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(syntax = "rn", escape = "none", path = "mapper.kt")]
|
||||
#[allow(dead_code)]
|
||||
pub struct MapperGenerator<'a> {
|
||||
config: RNConfig,
|
||||
ci: &'a ComponentInterface,
|
||||
// Track types used in sequences with the `add_sequence_type()` macro
|
||||
sequence_types: RefCell<BTreeSet<String>>,
|
||||
}
|
||||
|
||||
impl<'a> MapperGenerator<'a> {
|
||||
pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self {
|
||||
Self {
|
||||
config,
|
||||
ci,
|
||||
sequence_types: RefCell::new(BTreeSet::new()),
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to add a sequence type
|
||||
//
|
||||
// Call this inside your template to add a type used in a sequence.
|
||||
// This type is then added to the pushToArray helper.
|
||||
// Imports will be sorted and de-deuped.
|
||||
//
|
||||
// Returns an empty string so that it can be used inside an askama `{{ }}` block.
|
||||
fn add_sequence_type(&self, type_name: &str) -> &str {
|
||||
self.sequence_types
|
||||
.borrow_mut()
|
||||
.insert(type_name.to_owned());
|
||||
""
|
||||
}
|
||||
|
||||
pub fn sequence_types(&self) -> Vec<String> {
|
||||
let sequence_types = self.sequence_types.clone().into_inner();
|
||||
sequence_types.into_iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(syntax = "rn", escape = "none", path = "module.kt")]
|
||||
#[allow(dead_code)]
|
||||
pub struct ModuleGenerator<'a> {
|
||||
config: RNConfig,
|
||||
ci: &'a ComponentInterface,
|
||||
}
|
||||
|
||||
impl<'a> ModuleGenerator<'a> {
|
||||
pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self {
|
||||
Self { config, ci }
|
||||
}
|
||||
}
|
||||
|
||||
pub mod filters {
|
||||
use heck::*;
|
||||
use uniffi_bindgen::backend::CodeOracle;
|
||||
use uniffi_bindgen::backend::{CodeType, TypeIdentifier};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn oracle() -> &'static KotlinCodeOracle {
|
||||
&KotlinCodeOracle
|
||||
}
|
||||
|
||||
pub fn type_name(codetype: &impl CodeType) -> Result<String, askama::Error> {
|
||||
Ok(codetype.type_label(oracle()))
|
||||
}
|
||||
|
||||
pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(oracle().fn_name(nm))
|
||||
}
|
||||
|
||||
pub fn render_to_array(
|
||||
type_name: &str,
|
||||
ci: &ComponentInterface,
|
||||
) -> Result<String, askama::Error> {
|
||||
let res: Result<String, askama::Error> = match type_name {
|
||||
"Boolean" => Ok("array.pushBoolean(value)".to_string()),
|
||||
"Double" => Ok("array.pushDouble(value)".to_string()),
|
||||
"Int" => Ok("array.pushInt(value)".to_string()),
|
||||
"ReadableArray" => Ok("array.pushArray(value)".to_string()),
|
||||
"ReadableMap" => Ok("array.pushMap(value)".to_string()),
|
||||
"String" => Ok("array.pushString(value)".to_string()),
|
||||
"UByte" => Ok("array.pushInt(value.toInt())".to_string()),
|
||||
"UInt" => Ok("array.pushInt(value.toInt())".to_string()),
|
||||
"UShort" => Ok("array.pushInt(value.toInt())".to_string()),
|
||||
"ULong" => Ok("array.pushDouble(value.toDouble())".to_string()),
|
||||
_ => match ci.get_type(type_name) {
|
||||
Some(t) => match t {
|
||||
Type::Enum(inner) => {
|
||||
let enum_def = ci.get_enum_definition(&inner).unwrap();
|
||||
match enum_def.is_flat() {
|
||||
true => Ok("array.pushString(value.name.lowercase())".to_string()),
|
||||
false => Ok("array.pushMap(readableMapOf(value))".to_string()),
|
||||
}
|
||||
}
|
||||
_ => Ok("array.pushMap(readableMapOf(value))".to_string()),
|
||||
},
|
||||
None => unimplemented!("known type: {type_name}"),
|
||||
},
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn render_to_map(
|
||||
t: &TypeIdentifier,
|
||||
ci: &ComponentInterface,
|
||||
obj_name: &str,
|
||||
field_name: &str,
|
||||
optional: bool,
|
||||
) -> Result<String, askama::Error> {
|
||||
let res: Result<String, askama::Error> = match t {
|
||||
Type::UInt8 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::Int8 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::UInt16 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::Int16 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::UInt32 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::Int32 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::UInt64 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::Int64 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::Float32 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::Float64 => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::Boolean => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::String => Ok(format!("{obj_name}.{field_name}")),
|
||||
Type::Timestamp => unimplemented!("render_to_map: Timestamp is not implemented"),
|
||||
Type::Duration => unimplemented!("render_to_map: Duration is not implemented"),
|
||||
Type::Object(_) => unimplemented!("render_to_map: Object is not implemented"),
|
||||
Type::Record(_) => match optional {
|
||||
true => Ok(format!(
|
||||
"{obj_name}.{field_name}?.let {{ readableMapOf(it) }}"
|
||||
)),
|
||||
false => Ok(format!("readableMapOf({obj_name}.{field_name})")),
|
||||
},
|
||||
Type::Enum(inner) => {
|
||||
let enum_def = ci.get_enum_definition(inner).unwrap();
|
||||
match enum_def.is_flat() {
|
||||
true => match optional {
|
||||
true => Ok(format!(
|
||||
"{obj_name}.{field_name}?.let {{ it.name.lowercase() }}"
|
||||
)),
|
||||
false => Ok(format!("{obj_name}.{field_name}.name.lowercase()")),
|
||||
},
|
||||
false => match optional {
|
||||
true => Ok(format!(
|
||||
"{obj_name}.{field_name}?.let {{ readableMapOf(it) }}"
|
||||
)),
|
||||
false => Ok(format!("readableMapOf({obj_name}.{field_name})")),
|
||||
},
|
||||
}
|
||||
}
|
||||
Type::Error(_) => unimplemented!("render_to_map: Error is not implemented"),
|
||||
Type::CallbackInterface(_) => {
|
||||
unimplemented!("render_to_map: CallbackInterface is not implemented")
|
||||
}
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
render_to_map(unboxed, ci, obj_name, field_name, true)
|
||||
}
|
||||
Type::Sequence(_) => match optional {
|
||||
true => Ok(format!(
|
||||
"{obj_name}.{field_name}?.let {{ readableArrayOf(it) }}"
|
||||
)),
|
||||
false => Ok(format!("readableArrayOf({obj_name}.{field_name})")),
|
||||
},
|
||||
Type::Map(_, _) => unimplemented!("render_to_map: Map is not implemented"),
|
||||
Type::External { .. } => {
|
||||
unimplemented!("render_to_map: External is not implemented")
|
||||
}
|
||||
Type::Custom { .. } => {
|
||||
unimplemented!("render_to_map: Custom is not implemented")
|
||||
}
|
||||
Type::Unresolved { .. } => {
|
||||
unimplemented!("render_to_map: Unresolved is not implemented")
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn render_from_map(
|
||||
t: &TypeIdentifier,
|
||||
ci: &ComponentInterface,
|
||||
name: &str,
|
||||
field_name: &str,
|
||||
optional: bool,
|
||||
) -> Result<String, askama::Error> {
|
||||
let mut mandatory_suffix = "";
|
||||
if !optional {
|
||||
mandatory_suffix = "!!"
|
||||
}
|
||||
let res: String = match t {
|
||||
Type::UInt8 => format!("{name}.getInt(\"{field_name}\").toUByte()"),
|
||||
Type::Int8 => format!("{name}.getInt(\"{field_name}\").toByte()"),
|
||||
Type::UInt16 => format!("{name}.getInt(\"{field_name}\").toUShort()"),
|
||||
Type::Int16 => format!("{name}.getInt(\"{field_name}\").toShort()"),
|
||||
Type::UInt32 => format!("{name}.getInt(\"{field_name}\").toUInt()"),
|
||||
Type::Int32 => format!("{name}.getInt(\"{field_name}\")"),
|
||||
Type::UInt64 => format!("{name}.getDouble(\"{field_name}\").toULong()"),
|
||||
Type::Int64 => format!("{name}.getDouble(\"{field_name}\").toLong()"),
|
||||
Type::Float32 => format!("{name}.getDouble(\"{field_name}\")"),
|
||||
Type::Float64 => format!("{name}.getDouble(\"{field_name}\")"),
|
||||
Type::Boolean => format!("{name}.getBoolean(\"{field_name}\")"),
|
||||
Type::String => format!("{name}.getString(\"{field_name}\"){mandatory_suffix}"),
|
||||
Type::Timestamp => "".into(),
|
||||
Type::Duration => "".into(),
|
||||
Type::Object(_) => "".into(),
|
||||
Type::Record(_) => {
|
||||
let record_type_name = type_name(t)?;
|
||||
format!(
|
||||
"{name}.getMap(\"{field_name}\")?.let {{ as{record_type_name}(it)}}{mandatory_suffix}"
|
||||
)
|
||||
}
|
||||
Type::Enum(inner) => {
|
||||
let enum_def = ci.get_enum_definition(inner).unwrap();
|
||||
match enum_def.is_flat() {
|
||||
false => {
|
||||
format!("{name}.getMap(\"{field_name}\")?.let {{ as{inner}(it)}}{mandatory_suffix}")
|
||||
}
|
||||
true => format!(
|
||||
"{name}.getString(\"{field_name}\")?.let {{ as{inner}(it)}}{mandatory_suffix}"
|
||||
),
|
||||
}
|
||||
}
|
||||
Type::Error(_) => "".into(),
|
||||
Type::CallbackInterface(_) => "".into(),
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
let inner_res = render_from_map(unboxed, ci, name, field_name, true)?;
|
||||
format!("if (hasNonNullKey({name}, \"{field_name}\")) {inner_res} else null")
|
||||
}
|
||||
Type::Sequence(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
let element_type_name = type_name(unboxed)?;
|
||||
format!("{name}.getArray(\"{field_name}\")?.let {{ as{element_type_name}List(it) }}{mandatory_suffix}")
|
||||
}
|
||||
Type::Map(_, _) => "".into(),
|
||||
Type::External { .. } => "".into(),
|
||||
Type::Custom { .. } => "".into(),
|
||||
Type::Unresolved { .. } => "".into(),
|
||||
};
|
||||
Ok(res.to_string())
|
||||
}
|
||||
|
||||
/// Get the idiomatic Kotlin rendering of a variable name.
|
||||
pub fn var_name(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(format!("`{}`", nm.to_string().to_lower_camel_case()))
|
||||
}
|
||||
|
||||
pub fn unquote(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(nm.trim_matches('`').to_string())
|
||||
}
|
||||
|
||||
pub fn ignored_function(nm: &str) -> Result<bool, askama::Error> {
|
||||
Ok(IGNORED_FUNCTIONS.contains(nm))
|
||||
}
|
||||
|
||||
pub fn rn_convert_type(
|
||||
t: &TypeIdentifier,
|
||||
_ci: &ComponentInterface,
|
||||
) -> Result<String, askama::Error> {
|
||||
match t {
|
||||
Type::UInt8 | Type::UInt16 | Type::UInt32 => Ok(".toUInt()".to_string()),
|
||||
Type::Int64 => Ok(".toLong()".to_string()),
|
||||
Type::UInt64 => Ok(".toULong()".to_string()),
|
||||
Type::Float32 | Type::Float64 => Ok(".toFloat()".to_string()),
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
let conversion = rn_convert_type(unboxed, _ci).unwrap();
|
||||
let optional = match *unboxed {
|
||||
Type::Int8
|
||||
| Type::UInt8
|
||||
| Type::Int16
|
||||
| Type::UInt16
|
||||
| Type::Int32
|
||||
| Type::UInt32 => ".takeUnless { it == 0 }".to_string(),
|
||||
Type::Int64 => ".takeUnless { it == 0L }".to_string(),
|
||||
Type::UInt64 => ".takeUnless { it == 0UL }".to_string(),
|
||||
Type::Float32 | Type::Float64 => ".takeUnless { it == 0.0 }".to_string(),
|
||||
Type::String => ".takeUnless { it.isEmpty() }".to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
Ok(format!("{}{}", conversion, optional))
|
||||
}
|
||||
_ => Ok("".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rn_type_name(
|
||||
t: &TypeIdentifier,
|
||||
ci: &ComponentInterface,
|
||||
) -> Result<String, askama::Error> {
|
||||
match t {
|
||||
Type::Boolean => Ok("Boolean".to_string()),
|
||||
Type::Int8 | Type::UInt8 | Type::Int16 | Type::UInt16 | Type::Int32 | Type::UInt32 => {
|
||||
Ok("Int".to_string())
|
||||
}
|
||||
Type::Int64 | Type::UInt64 | Type::Float32 | Type::Float64 => Ok("Double".to_string()),
|
||||
Type::String => Ok("String".to_string()),
|
||||
Type::Enum(inner) => {
|
||||
let enum_def = ci.get_enum_definition(inner).unwrap();
|
||||
match enum_def.is_flat() {
|
||||
false => Ok("ReadableMap".to_string()),
|
||||
true => Ok("String".to_string()),
|
||||
}
|
||||
}
|
||||
Type::Record(_) => Ok("ReadableMap".to_string()),
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
rn_type_name(unboxed, ci)
|
||||
}
|
||||
Type::Sequence(_) => Ok("ReadableArray".to_string()),
|
||||
_ => Ok("".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn temporary(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(format!("{nm}Tmp"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{%- let e = ci.get_enum_definition(name).unwrap() %}
|
||||
{%- if e.is_flat() %}
|
||||
|
||||
fun as{{ type_name }}(type: String): {{ type_name }} {
|
||||
return {{ type_name }}.valueOf(type.uppercase())
|
||||
}
|
||||
|
||||
{%- else %}
|
||||
|
||||
fun as{{ type_name }}({{ type_name|var_name|unquote }}: ReadableMap): {{ type_name }}? {
|
||||
val type = {{ type_name|var_name|unquote }}.getString("type")
|
||||
|
||||
{% for variant in e.variants() -%}
|
||||
if (type == "{{ variant.name()|var_name|unquote }}") {
|
||||
{% if variant.has_fields() -%}
|
||||
return {{ type_name }}.{{ variant.name() }}( {{ variant.fields()[0].type_()|render_from_map(ci, type_name|var_name|unquote, variant.fields()[0].name()|var_name|unquote, false) }})
|
||||
{%- else %}
|
||||
return {{ type_name }}.{{ variant.name() }}
|
||||
{%- endif %}
|
||||
}
|
||||
{% endfor -%}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun readableMapOf({{ type_name|var_name|unquote }}: {{ type_name }}): ReadableMap? {
|
||||
val map = Arguments.createMap()
|
||||
when ({{ type_name|var_name|unquote }}) {
|
||||
{% for variant in e.variants() -%}
|
||||
is {{ type_name }}.{{ variant.name() }} -> {
|
||||
pushToMap(map, "type", "{{ variant.name()|var_name|unquote }}")
|
||||
{% for f in variant.fields() -%}
|
||||
pushToMap(map, "{{ f.name()|var_name|unquote }}", {{ f.type_()|render_to_map(ci,type_name|var_name|unquote,f.name()|var_name|unquote, false) }})
|
||||
{% endfor -%}
|
||||
}
|
||||
{% endfor %}
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
{%- endif %}
|
||||
|
||||
fun as{{ type_name }}List(arr: ReadableArray): List<{{ type_name }}> {
|
||||
val list = ArrayList<{{ type_name }}>()
|
||||
for (value in arr.toArrayList()) {
|
||||
when (value) {
|
||||
{%- if e.is_flat() %}
|
||||
is String -> list.add(as{{ type_name }}(value)!!)
|
||||
{%- else %}
|
||||
is ReadableMap -> list.add(as{{ type_name }}(value)!!)
|
||||
{%- endif %}
|
||||
else -> throw LsSdkException.Generic(errUnexpectedType("${value::class.java.name}"))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
fun readableMapOf(vararg values: Pair<String, *>): ReadableMap {
|
||||
val map = Arguments.createMap()
|
||||
for ((key, value) in values) {
|
||||
pushToMap(map, key, value)
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
fun hasNonNullKey(map: ReadableMap, key: String): Boolean {
|
||||
return map.hasKey(key) && !map.isNull(key)
|
||||
}
|
||||
|
||||
fun validateMandatoryFields(map: ReadableMap, keys: Array<String>): Boolean {
|
||||
for (k in keys) {
|
||||
if (!hasNonNullKey(map, k)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun pushToArray(array: WritableArray, value: Any?) {
|
||||
when (value) {
|
||||
null -> array.pushNull()
|
||||
{%- for sequence_type in self.sequence_types() %}
|
||||
is {{ sequence_type }} -> {{sequence_type|render_to_array(ci)}}
|
||||
{%- endfor %}
|
||||
is Array<*> -> array.pushArray(readableArrayOf(value.asIterable()))
|
||||
is List<*> -> array.pushArray(readableArrayOf(value))
|
||||
else -> throw LsSdkException.Generic(errUnexpectedType("${value::class.java.name}"))
|
||||
}
|
||||
}
|
||||
|
||||
fun pushToMap(map: WritableMap, key: String, value: Any?) {
|
||||
when (value) {
|
||||
null -> map.putNull(key)
|
||||
is Boolean -> map.putBoolean(key, value)
|
||||
is Byte -> map.putInt(key, value.toInt())
|
||||
is Double -> map.putDouble(key, value)
|
||||
is Int -> map.putInt(key, value)
|
||||
is Long -> map.putDouble(key, value.toDouble())
|
||||
is ReadableArray -> map.putArray(key, value)
|
||||
is ReadableMap -> map.putMap(key, value)
|
||||
is String -> map.putString(key, value)
|
||||
is UByte -> map.putInt(key, value.toInt())
|
||||
is UInt -> map.putInt(key, value.toInt())
|
||||
is UShort -> map.putInt(key, value.toInt())
|
||||
is ULong -> map.putDouble(key, value.toDouble())
|
||||
is Array<*> -> map.putArray(key, readableArrayOf(value.asIterable()))
|
||||
is List<*> -> map.putArray(key, readableArrayOf(value))
|
||||
else -> throw LsSdkException.Generic("Unexpected type ${value::class.java.name} for key [$key]")
|
||||
}
|
||||
}
|
||||
|
||||
fun readableArrayOf(values: Iterable<*>?): ReadableArray {
|
||||
val array = Arguments.createArray()
|
||||
if (values != null) {
|
||||
for (value in values) {
|
||||
pushToArray(array, value)
|
||||
}
|
||||
}
|
||||
|
||||
return array
|
||||
}
|
||||
|
||||
fun asUByteList(arr: ReadableArray): List<UByte> {
|
||||
val list = ArrayList<UByte>()
|
||||
for (value in arr.toArrayList()) {
|
||||
when (value) {
|
||||
is Double -> list.add(value.toInt().toUByte())
|
||||
is Int -> list.add(value.toUByte())
|
||||
is UByte -> list.add(value)
|
||||
else -> throw LsSdkException.Generic(errUnexpectedType("${value::class.java.name}"))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun asStringList(arr: ReadableArray): List<String> {
|
||||
val list = ArrayList<String>()
|
||||
for (value in arr.toArrayList()) {
|
||||
list.add(value.toString())
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun errMissingMandatoryField(fieldName: String, typeName: String): String {
|
||||
return "Missing mandatory field ${fieldName} for type ${typeName}"
|
||||
}
|
||||
|
||||
fun errUnexpectedType(typeName: String): String {
|
||||
return "Unexpected type ${typeName}"
|
||||
}
|
||||
|
||||
fun errUnexpectedValue(fieldName: String): String {
|
||||
return "Unexpected value for optional field ${fieldName}"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{%- for type_ in ci.iter_types() %}
|
||||
{%- let type_name = type_|type_name %}
|
||||
{%- match type_ %}
|
||||
{%- when Type::Object ( name ) %}
|
||||
{% let obj = ci.get_object_definition(name).unwrap() %}
|
||||
{% let obj_interface = "getBindingWallet()." %}
|
||||
{%- for func in obj.methods() -%}
|
||||
{%- include "TopLevelFunctionTemplate.kt" %}
|
||||
{% endfor %}
|
||||
{%- else -%}
|
||||
{%- endmatch -%}
|
||||
{%- endfor %}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
{%- let rec = ci.get_record_definition(name).unwrap() %}
|
||||
fun as{{ type_name }}({{ type_name|var_name|unquote }}: ReadableMap): {{ type_name }}? {
|
||||
if (!validateMandatoryFields({{ type_name|var_name|unquote }}, arrayOf(
|
||||
{%- for field in rec.fields() %}
|
||||
{%- match field.type_() %}
|
||||
{%- when Type::Optional(_) %}
|
||||
{%- else %}
|
||||
"{{ field.name()|var_name |unquote }}",
|
||||
{%- endmatch %}
|
||||
{%- endfor %}
|
||||
))) {
|
||||
return null
|
||||
}
|
||||
|
||||
{%- for field in rec.fields() %}
|
||||
val {{field.name()|var_name|unquote}} = {{ field.type_()|render_from_map(ci, type_name|var_name|unquote, field.name()|var_name|unquote, false) }}
|
||||
{%- endfor %}
|
||||
return {{ type_name }}({%- call kt::field_list(rec) -%})
|
||||
}
|
||||
|
||||
fun readableMapOf({{ type_name|var_name|unquote }}: {{ type_name }}): ReadableMap {
|
||||
return readableMapOf(
|
||||
{%- for field in rec.fields() %}
|
||||
{%- match field.type_() %}
|
||||
{%- when Type::Optional(inner) %}
|
||||
{%- let unboxed = inner.as_ref() %}
|
||||
{%- match unboxed %}
|
||||
{%- when Type::Sequence(inner_type) %}
|
||||
{{- self.add_sequence_type(inner_type|type_name) }}
|
||||
{%- else %}
|
||||
{%- endmatch %}
|
||||
{%- when Type::Sequence(inner_type) %}
|
||||
{{- self.add_sequence_type(inner_type|type_name) }}
|
||||
{%- else %}
|
||||
{%- endmatch %}
|
||||
"{{ field.name()|var_name|unquote }}" to {{ field.type_()|render_to_map(ci,type_name|var_name|unquote, field.name()|var_name|unquote, false) }},
|
||||
{%- endfor %}
|
||||
)
|
||||
}
|
||||
|
||||
fun as{{ type_name }}List(arr: ReadableArray): List<{{ type_name }}> {
|
||||
val list = ArrayList<{{ type_name }}>()
|
||||
for (value in arr.toArrayList()) {
|
||||
when (value) {
|
||||
is ReadableMap -> list.add(as{{ type_name }}(value)!!)
|
||||
else -> throw LsSdkException.Generic(errUnexpectedType("${value::class.java.name}"))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
@ReactMethod
|
||||
fun {{ func.name()|fn_name|unquote }}({%- call kt::arg_list_decl(func) -%}promise: Promise) {
|
||||
executor.execute {
|
||||
try {
|
||||
{%- for arg in func.arguments() -%}
|
||||
{%- match arg.type_() %}
|
||||
{%- when Type::Enum(inner) %}
|
||||
{%- let e = ci.get_enum_definition(inner).unwrap() %}
|
||||
{%- if e.is_flat() %}
|
||||
val {{arg.name()|var_name|unquote|temporary}} = as{{arg.type_()|type_name}}({{ arg.name()|var_name|unquote }})
|
||||
{%- else %}
|
||||
val {{arg.name()|var_name|unquote|temporary}} = as{{arg.type_()|type_name}}({{ arg.name()|var_name|unquote }}) ?: run { throw LsSdkException.Generic(errMissingMandatoryField("{{arg.name()|var_name|unquote}}", "{{ arg.type_()|type_name }}")) }
|
||||
{%- endif %}
|
||||
{%- when Type::Optional(_) %}
|
||||
val {{arg.name()|var_name|unquote|temporary}} = {{arg.name()|var_name|unquote}}{{ arg.type_()|rn_convert_type(ci) -}}
|
||||
{%- when Type::Record(_) %}
|
||||
val {{arg.type_()|type_name|var_name|unquote}} = as{{arg.type_()|type_name}}({{ arg.name()|var_name|unquote }}) ?: run { throw LsSdkException.Generic(errMissingMandatoryField("{{arg.name()|var_name|unquote}}", "{{ arg.type_()|type_name }}")) }
|
||||
{%- else %}
|
||||
{%- endmatch %}
|
||||
{%- endfor %}
|
||||
{%- match func.return_type() -%}
|
||||
{%- when Some with (return_type) %}
|
||||
val res = {{ obj_interface }}{{ func.name()|fn_name|unquote }}({%- call kt::arg_list(func) -%})
|
||||
{%- match return_type %}
|
||||
{%- when Type::Optional(inner) %}
|
||||
{%- let unboxed = inner.as_ref() %}
|
||||
promise.resolve(res?.let { {% call kt::return_value(unboxed) %} })
|
||||
{%- else %}
|
||||
promise.resolve({% call kt::return_value(return_type) %})
|
||||
{%- endmatch %}
|
||||
{%- when None %}
|
||||
{{ obj_interface }}{{ func.name()|fn_name|unquote }}({%- call kt::arg_list(func) -%})
|
||||
promise.resolve(readableMapOf("status" to "ok"))
|
||||
{%- endmatch %}
|
||||
} catch (e: Exception) {
|
||||
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{%- for type_ in ci.iter_types() -%}
|
||||
{%- let type_name = type_|type_name -%}
|
||||
{%- match type_ -%}
|
||||
{%- when Type::Record ( name ) %}
|
||||
{%- include "RecordTemplate.kt" %}
|
||||
{%- when Type::Enum ( name ) %}
|
||||
{%- include "EnumTemplate.kt" %}
|
||||
{%- when Type::Object ( name ) %}
|
||||
{% let obj = ci.get_object_definition(name).unwrap() -%}
|
||||
{%- for func in obj.methods() -%}
|
||||
{%- match func.return_type() -%}
|
||||
{%- when Some with (return_type) -%}
|
||||
{%- match return_type -%}
|
||||
{%- when Type::Optional(inner) -%}
|
||||
{%- let unboxed = inner.as_ref() -%}
|
||||
{%- match unboxed -%}
|
||||
{%- when Type::Sequence(inner_type) -%}
|
||||
{{- self.add_sequence_type(inner_type|type_name) -}}
|
||||
{%- else -%}
|
||||
{%- endmatch -%}
|
||||
{%- when Type::Sequence(inner_type) -%}
|
||||
{{- self.add_sequence_type(inner_type|type_name) -}}
|
||||
{%- else -%}
|
||||
{%- endmatch -%}
|
||||
{%- else -%}
|
||||
{%- endmatch -%}
|
||||
{% endfor -%}
|
||||
{%- else -%}
|
||||
{%- endmatch -%}
|
||||
{%- endfor -%}
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
{% macro arg_list(func) %}
|
||||
{%- for arg in func.arguments() -%}
|
||||
{%- match arg.type_() -%}
|
||||
{%- when Type::Enum(_) -%}
|
||||
{{ arg.name()|var_name|unquote|temporary }}
|
||||
{%- when Type::Optional(_) -%}
|
||||
{{ arg.name()|var_name|unquote|temporary }}
|
||||
{%- when Type::Record(_) -%}
|
||||
{{ arg.type_()|type_name|var_name|unquote -}}
|
||||
{%- else -%}
|
||||
{{ arg.name()|var_name|unquote }}{{ arg.type_()|rn_convert_type(ci) -}}
|
||||
{%- endmatch -%}
|
||||
{%- if !loop.last %}, {% endif -%}
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro arg_list_decl(func) %}
|
||||
{%- for arg in func.arguments() -%}
|
||||
{{- arg.name()|var_name|unquote }}: {{ arg.type_()|rn_type_name(ci) -}}, {% endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro field_list(rec) %}
|
||||
{%- for f in rec.fields() %}
|
||||
{{ f.name()|var_name|unquote }},
|
||||
{%- endfor %}
|
||||
{%- endmacro -%}
|
||||
|
||||
{% macro return_value(ret_type) %}
|
||||
{%- match ret_type %}
|
||||
{%- when Type::Enum(_) %}readableMapOf(res)
|
||||
{%- when Type::Record(_) %}readableMapOf(res)
|
||||
{%- when Type::Sequence(_) %}readableArrayOf(res)
|
||||
{%- else %}res
|
||||
{%- endmatch %}
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,13 @@
|
||||
{%- import "macros.kt" as kt -%}
|
||||
package com.lssdk
|
||||
import ls_sdk.*
|
||||
import com.facebook.react.bridge.*
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
{%- include "Types.kt" %}
|
||||
|
||||
{%- include "Helpers.kt" %}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.lssdk
|
||||
|
||||
import ls_sdk.*
|
||||
import com.facebook.react.bridge.*
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
{% import "macros.kt" as kt %}
|
||||
|
||||
class LiquidSwapSDKModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
||||
private lateinit var executor: ExecutorService
|
||||
private var bindingWallet: BindingWallet? = null
|
||||
|
||||
companion object {
|
||||
const val TAG = "RNLiquidSwapSDK"
|
||||
}
|
||||
|
||||
override fun initialize() {
|
||||
super.initialize()
|
||||
|
||||
executor = Executors.newFixedThreadPool(3)
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return TAG
|
||||
}
|
||||
|
||||
@Throws(LsSdkException::class)
|
||||
fun getBindingWallet(): BindingWallet {
|
||||
if (bindingWallet != null) {
|
||||
return bindingWallet!!
|
||||
}
|
||||
|
||||
throw LsSdkException.Generic("Not initialized")
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun addListener(eventName: String) {}
|
||||
|
||||
@ReactMethod
|
||||
fun removeListeners(count: Int) {}
|
||||
|
||||
{% let obj_interface = "" -%}
|
||||
{% for func in ci.function_definitions() %}
|
||||
{%- if func.name()|ignored_function == false -%}
|
||||
{% include "TopLevelFunctionTemplate.kt" %}
|
||||
{% endif -%}
|
||||
{%- endfor %}
|
||||
|
||||
@ReactMethod
|
||||
fun initBindingWallet(mnemonic: String, dataDir: String, network: String, promise: Promise) {
|
||||
if (bindingWallet != null) {
|
||||
promise.reject("Generic", "Already initialized")
|
||||
return
|
||||
}
|
||||
|
||||
executor.execute {
|
||||
try {
|
||||
val dataDirTmp = dataDir.takeUnless { it.isEmpty() } ?: run { reactApplicationContext.filesDir.toString() + "/lsSdk" }
|
||||
val networkTmp = asNetwork(network)
|
||||
bindingWallet = init(mnemonic, dataDirTmp, networkTmp)
|
||||
promise.resolve(readableMapOf("status" to "ok"))
|
||||
} catch (e: Exception) {
|
||||
promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
{%- include "Objects.kt" %}
|
||||
}
|
||||
|
||||
388
lib/ls-sdk-bindings/bindings-react-native/src/gen_swift/mod.rs
Normal file
388
lib/ls-sdk-bindings/bindings-react-native/src/gen_swift/mod.rs
Normal file
@@ -0,0 +1,388 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use askama::Template;
|
||||
use once_cell::sync::Lazy;
|
||||
use uniffi_bindgen::interface::*;
|
||||
|
||||
use crate::generator::RNConfig;
|
||||
|
||||
pub use uniffi_bindgen::bindings::swift::gen_swift::*;
|
||||
|
||||
static IGNORED_FUNCTIONS: Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
let list: Vec<&str> = vec!["init"];
|
||||
HashSet::from_iter(list.into_iter().map(|s| s.to_string()))
|
||||
});
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(syntax = "rn", escape = "none", path = "mapper.swift")]
|
||||
#[allow(dead_code)]
|
||||
pub struct MapperGenerator<'a> {
|
||||
config: RNConfig,
|
||||
ci: &'a ComponentInterface,
|
||||
}
|
||||
|
||||
impl<'a> MapperGenerator<'a> {
|
||||
pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self {
|
||||
Self { config, ci }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(syntax = "rn", escape = "none", path = "extern.m")]
|
||||
#[allow(dead_code)]
|
||||
pub struct ExternGenerator<'a> {
|
||||
config: RNConfig,
|
||||
ci: &'a ComponentInterface,
|
||||
}
|
||||
|
||||
impl<'a> ExternGenerator<'a> {
|
||||
pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self {
|
||||
Self { config, ci }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(syntax = "rn", escape = "none", path = "module.swift")]
|
||||
#[allow(dead_code)]
|
||||
pub struct ModuleGenerator<'a> {
|
||||
config: RNConfig,
|
||||
ci: &'a ComponentInterface,
|
||||
}
|
||||
|
||||
impl<'a> ModuleGenerator<'a> {
|
||||
pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self {
|
||||
Self { config, ci }
|
||||
}
|
||||
}
|
||||
|
||||
pub mod filters {
|
||||
|
||||
use heck::*;
|
||||
use uniffi_bindgen::backend::CodeOracle;
|
||||
use uniffi_bindgen::backend::{CodeType, TypeIdentifier};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn oracle() -> &'static SwiftCodeOracle {
|
||||
&SwiftCodeOracle
|
||||
}
|
||||
|
||||
pub fn type_name(codetype: &impl CodeType) -> Result<String, askama::Error> {
|
||||
Ok(codetype.type_label(oracle()))
|
||||
}
|
||||
|
||||
pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(oracle().fn_name(nm))
|
||||
}
|
||||
|
||||
pub fn render_to_map(
|
||||
t: &TypeIdentifier,
|
||||
ci: &ComponentInterface,
|
||||
obj_name: &str,
|
||||
field_name: &str,
|
||||
optional: bool,
|
||||
) -> Result<String, askama::Error> {
|
||||
let type_name = filters::type_name(t)?;
|
||||
let type_name_str = type_name.as_str();
|
||||
let var_name = filters::unquote(filters::var_name(type_name_str)?.as_str())?;
|
||||
let mut obj_prefix = "".to_string();
|
||||
if !obj_name.is_empty() {
|
||||
obj_prefix = format!("{obj_name}.");
|
||||
}
|
||||
let mut optional_suffix = "";
|
||||
if optional {
|
||||
optional_suffix = "!";
|
||||
}
|
||||
let res: Result<String, askama::Error> = match t {
|
||||
Type::UInt8 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::Int8 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::UInt16 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::Int16 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::UInt32 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::Int32 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::UInt64 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::Int64 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::Float32 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::Float64 => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::Boolean => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::String => Ok(format!("{obj_prefix}{field_name}")),
|
||||
Type::Timestamp => unimplemented!("render_to_map: Timestamp is not implemented"),
|
||||
Type::Duration => unimplemented!("render_to_map: Duration is not implemented"),
|
||||
Type::Object(_) => unimplemented!("render_to_map: Object is not implemented"),
|
||||
Type::Record(_) => Ok(format!(
|
||||
"dictionaryOf({var_name}: {obj_prefix}{field_name}{optional_suffix})"
|
||||
)),
|
||||
Type::Enum(inner) => {
|
||||
let enum_def = ci.get_enum_definition(inner).unwrap();
|
||||
match enum_def.is_flat() {
|
||||
true => Ok(format!(
|
||||
"valueOf( {var_name}: {obj_prefix}{field_name}{optional_suffix})"
|
||||
)),
|
||||
false => Ok(format!(
|
||||
"dictionaryOf({var_name}: {obj_prefix}{field_name}{optional_suffix})"
|
||||
)),
|
||||
}
|
||||
}
|
||||
Type::Error(_) => unimplemented!("render_to_map: Error is not implemented"),
|
||||
Type::CallbackInterface(_) => {
|
||||
unimplemented!("render_to_map: CallbackInterface is not implemented")
|
||||
}
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
let inner_render = render_to_map(unboxed, ci, obj_name, field_name, true)?;
|
||||
Ok(format!(
|
||||
"{obj_prefix}{field_name} == nil ? nil : {inner_render}"
|
||||
))
|
||||
}
|
||||
Type::Sequence(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
let type_name = filters::type_name(unboxed)?;
|
||||
let var_name = filters::var_name(type_name.as_str())?;
|
||||
let var_name = filters::unquote(var_name.as_str())?;
|
||||
let as_array_statment = match unboxed {
|
||||
Type::Record(_) => format!(
|
||||
"arrayOf({var_name}List: {obj_prefix}{field_name}{optional_suffix})"
|
||||
),
|
||||
Type::Enum(_) => format!(
|
||||
"arrayOf({var_name}List: {obj_prefix}{field_name}{optional_suffix})"
|
||||
),
|
||||
_ => format!("{obj_prefix}{field_name}"),
|
||||
};
|
||||
Ok(as_array_statment)
|
||||
}
|
||||
Type::Map(_, _) => unimplemented!("render_to_map: Map is not implemented"),
|
||||
Type::External { .. } => {
|
||||
unimplemented!("render_to_map: External is not implemented")
|
||||
}
|
||||
Type::Custom { .. } => {
|
||||
unimplemented!("render_to_map: Custom is not implemented")
|
||||
}
|
||||
Type::Unresolved { .. } => {
|
||||
unimplemented!("render_to_map: Unresolved is not implemented")
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn rn_convert_type(
|
||||
t: &TypeIdentifier,
|
||||
converted_var_name: &str,
|
||||
) -> Result<String, askama::Error> {
|
||||
match t {
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
let optional = match *unboxed {
|
||||
Type::Int8
|
||||
| Type::UInt8
|
||||
| Type::Int16
|
||||
| Type::UInt16
|
||||
| Type::Int32
|
||||
| Type::UInt32
|
||||
| Type::Int64
|
||||
| Type::UInt64 => {
|
||||
format!("{} == 0 ? nil : {}", converted_var_name, converted_var_name)
|
||||
}
|
||||
Type::Float32 | Type::Float64 => format!(
|
||||
"{} == 0.0 ? nil : {}",
|
||||
converted_var_name, converted_var_name
|
||||
),
|
||||
Type::String => format!(
|
||||
"{}.isEmpty ? nil : {}",
|
||||
converted_var_name, converted_var_name
|
||||
),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
Ok(optional.to_string())
|
||||
}
|
||||
_ => Ok(converted_var_name.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rn_return_type(
|
||||
t: &TypeIdentifier,
|
||||
name: &str,
|
||||
optional: bool,
|
||||
) -> Result<String, askama::Error> {
|
||||
let mut optional_suffix = "";
|
||||
if optional {
|
||||
optional_suffix = "!";
|
||||
}
|
||||
match t {
|
||||
Type::Enum(_) | Type::Record(_) => Ok(format!(
|
||||
"LiquidSwapSDKMapper.dictionaryOf({}: res{})",
|
||||
name, optional_suffix
|
||||
)),
|
||||
Type::Sequence(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
match unboxed {
|
||||
Type::Enum(_) | Type::Record(_) => Ok(format!(
|
||||
"LiquidSwapSDKMapper.arrayOf({}List: res{})",
|
||||
name, optional_suffix
|
||||
)),
|
||||
_ => Ok(format!("res{}", optional_suffix)),
|
||||
}
|
||||
}
|
||||
_ => Ok(format!("res{}", optional_suffix)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rn_type_name(
|
||||
t: &TypeIdentifier,
|
||||
ci: &ComponentInterface,
|
||||
optional: bool,
|
||||
) -> Result<String, askama::Error> {
|
||||
let mut optional_suffix = "";
|
||||
if optional {
|
||||
optional_suffix = "?";
|
||||
}
|
||||
match t {
|
||||
Type::Record(_) => Ok(format!("[String: Any{}]", optional_suffix)),
|
||||
Type::Enum(inner) => {
|
||||
let enum_def = ci.get_enum_definition(inner).unwrap();
|
||||
match enum_def.is_flat() {
|
||||
false => Ok(format!("[String: Any{}]", optional_suffix)),
|
||||
true => Ok("String".into()),
|
||||
}
|
||||
}
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
rn_type_name(unboxed, ci, optional)
|
||||
}
|
||||
Type::Sequence(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
Ok(format!("[{}]", rn_type_name(unboxed, ci, optional)?))
|
||||
}
|
||||
t => {
|
||||
let name = filters::type_name(t)?;
|
||||
Ok(name.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extern_type_name(
|
||||
t: &TypeIdentifier,
|
||||
ci: &ComponentInterface,
|
||||
) -> Result<String, askama::Error> {
|
||||
match t {
|
||||
Type::Boolean => Ok("BOOL".to_string()),
|
||||
Type::Int8 | Type::Int16 | Type::Int32 | Type::Int64 => Ok("NSInteger*".to_string()),
|
||||
Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64 => {
|
||||
Ok("NSUInteger*".to_string())
|
||||
}
|
||||
Type::Float32 | Type::Float64 => Ok("NSNumber*".to_string()),
|
||||
Type::String => Ok("NSString*".to_string()),
|
||||
Type::Enum(inner) => {
|
||||
let enum_def = ci.get_enum_definition(inner).unwrap();
|
||||
match enum_def.is_flat() {
|
||||
false => Ok("NSDictionary*".to_string()),
|
||||
true => Ok("NSString*".to_string()),
|
||||
}
|
||||
}
|
||||
Type::Record(_) => Ok("NSDictionary*".to_string()),
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
extern_type_name(unboxed, ci)
|
||||
}
|
||||
Type::Sequence(_) => Ok("NSArray*".to_string()),
|
||||
_ => Ok("".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inline_optional_field(
|
||||
t: &TypeIdentifier,
|
||||
ci: &ComponentInterface,
|
||||
) -> Result<bool, askama::Error> {
|
||||
match t {
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
inline_optional_field(unboxed, ci)
|
||||
}
|
||||
_ => {
|
||||
let mapped_name = filters::rn_type_name(t, ci, true)?;
|
||||
let type_name = filters::type_name(t)?;
|
||||
Ok(mapped_name == type_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_from_map(
|
||||
t: &TypeIdentifier,
|
||||
ci: &ComponentInterface,
|
||||
map_var_name: &str,
|
||||
) -> Result<String, askama::Error> {
|
||||
let res: String = match t {
|
||||
Type::UInt8 => map_var_name.to_string(),
|
||||
Type::Int8 => map_var_name.to_string(),
|
||||
Type::UInt16 => map_var_name.to_string(),
|
||||
Type::Int16 => map_var_name.to_string(),
|
||||
Type::UInt32 => map_var_name.to_string(),
|
||||
Type::Int32 => map_var_name.to_string(),
|
||||
Type::UInt64 => map_var_name.to_string(),
|
||||
Type::Int64 => map_var_name.to_string(),
|
||||
Type::Float32 => map_var_name.to_string(),
|
||||
Type::Float64 => map_var_name.to_string(),
|
||||
Type::Boolean => map_var_name.to_string(),
|
||||
Type::String => map_var_name.to_string(),
|
||||
Type::Timestamp => "".into(),
|
||||
Type::Duration => "".into(),
|
||||
Type::Object(_) => "".into(),
|
||||
Type::Record(_) => {
|
||||
let record_type_name = type_name(t)?;
|
||||
let record_var_name = var_name(&record_type_name)?;
|
||||
let record_unquoted_name = unquote(&record_var_name)?;
|
||||
format!("try as{record_type_name}({record_unquoted_name}: {map_var_name})")
|
||||
}
|
||||
Type::Enum(inner) => {
|
||||
let enum_def = ci.get_enum_definition(inner).unwrap();
|
||||
let enum_var_name = var_name(inner)?;
|
||||
let enum_unquoted_name = unquote(&enum_var_name)?;
|
||||
match enum_def.is_flat() {
|
||||
false => format!("try as{inner}({enum_unquoted_name}: {map_var_name})"),
|
||||
true => format!("try as{inner}({enum_unquoted_name}: {map_var_name})"),
|
||||
}
|
||||
}
|
||||
Type::Error(_) => "".into(),
|
||||
Type::CallbackInterface(_) => "".into(),
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
|
||||
render_from_map(unboxed, ci, map_var_name)?
|
||||
}
|
||||
Type::Sequence(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
let element_type_name = type_name(unboxed)?;
|
||||
match unboxed {
|
||||
Type::Enum(_) | Type::Record(_) => {
|
||||
format!("try as{element_type_name}List(arr: {map_var_name})")
|
||||
}
|
||||
_ => map_var_name.to_string(),
|
||||
}
|
||||
}
|
||||
Type::Map(_, _) => "".into(),
|
||||
Type::External { .. } => "".into(),
|
||||
Type::Custom { .. } => "".into(),
|
||||
Type::Unresolved { .. } => "".into(),
|
||||
};
|
||||
Ok(res.to_string())
|
||||
}
|
||||
|
||||
pub fn var_name(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(format!("`{}`", nm.to_string().to_lower_camel_case()))
|
||||
}
|
||||
|
||||
pub fn unquote(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(nm.trim_matches('`').to_string())
|
||||
}
|
||||
|
||||
pub fn ignored_function(nm: &str) -> Result<bool, askama::Error> {
|
||||
Ok(IGNORED_FUNCTIONS.contains(nm))
|
||||
}
|
||||
|
||||
pub fn list_arg(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(format!("`{nm}List`"))
|
||||
}
|
||||
|
||||
pub fn temporary(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(format!("{nm}Tmp"))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
{%- let e = ci.get_enum_definition(name).unwrap() %}
|
||||
{%- if e.is_flat() %}
|
||||
|
||||
static func as{{ type_name }}({{ type_name|var_name|unquote }}: String) throws -> {{ type_name }} {
|
||||
switch({{ type_name|var_name|unquote }}) {
|
||||
{%- for variant in e.variants() %}
|
||||
|
||||
case "{{variant.name()|var_name|unquote}}":
|
||||
return {{ type_name }}.{{variant.name()|var_name|unquote}}
|
||||
|
||||
{%- endfor %}
|
||||
|
||||
default: throw LsSdkError.Generic(message: "Invalid variant \({{ type_name|var_name|unquote }}) for enum {{ type_name }}")
|
||||
}
|
||||
}
|
||||
|
||||
static func valueOf({{ type_name|var_name|unquote }}: {{ type_name }}) -> String {
|
||||
switch({{ type_name|var_name|unquote }}) {
|
||||
{%- for variant in e.variants() %}
|
||||
|
||||
case .{{variant.name()|var_name|unquote}}:
|
||||
return "{{variant.name()|var_name|unquote}}"
|
||||
|
||||
{%- endfor %}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static func arrayOf({{ type_name|var_name|unquote|list_arg }}: [{{ type_name }}]) -> [String] {
|
||||
return {{ type_name|var_name|unquote|list_arg }}.map { (v) -> String in return valueOf({{ type_name|var_name|unquote }}: v) }
|
||||
}
|
||||
|
||||
{%- else %}
|
||||
|
||||
static func as{{ type_name }}({{ type_name|var_name|unquote }}: [String: Any?]) throws -> {{ type_name }} {
|
||||
let type = {{ type_name|var_name|unquote }}["type"] as! String
|
||||
|
||||
{%- for variant in e.variants() %}
|
||||
if (type == "{{ variant.name()|var_name|unquote }}") {
|
||||
{%- if variant.has_fields() %}
|
||||
{% let field = variant.fields()[0] %}
|
||||
{%- match field.type_() %}
|
||||
{%- when Type::Optional(_) %}
|
||||
{% if field.type_()|inline_optional_field(ci) -%}
|
||||
let _{{field.name()|var_name|unquote}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}}
|
||||
{% else -%}
|
||||
var _{{field.name()|var_name|unquote}}: {{field.type_()|type_name}}
|
||||
if let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} {
|
||||
_{{field.name()|var_name|unquote}} = {{field.type_()|render_from_map(ci, field.name()|var_name|unquote|temporary)}}
|
||||
}
|
||||
{% endif -%}
|
||||
{%- else %}
|
||||
{% if field.type_()|inline_optional_field(ci) -%}
|
||||
guard let _{{field.name()|var_name|unquote}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else {
|
||||
throw LsSdkError.Generic(message: errMissingMandatoryField(fieldName: "{{field.name()|var_name|unquote}}", typeName: "{{ type_name }}"))
|
||||
}
|
||||
{%- else -%}
|
||||
guard let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else {
|
||||
throw LsSdkError.Generic(message: errMissingMandatoryField(fieldName: "{{field.name()|var_name|unquote}}", typeName: "{{ type_name }}"))
|
||||
}
|
||||
let _{{field.name()|var_name|unquote}} = {{field.type_()|render_from_map(ci, field.name()|var_name|unquote|temporary)}}
|
||||
{% endif -%}
|
||||
{% endmatch %}
|
||||
return {{ type_name }}.{{ variant.name()|var_name|unquote }}({{ variant.fields()[0].name()|var_name|unquote }}: _{{field.name()|var_name|unquote}})
|
||||
{%- else %}
|
||||
return {{ type_name }}.{{ variant.name()|var_name|unquote }}
|
||||
{%- endif %}
|
||||
}
|
||||
{%- endfor %}
|
||||
|
||||
throw LsSdkError.Generic(message: "Unexpected type \(type) for enum {{ type_name }}")
|
||||
}
|
||||
|
||||
static func dictionaryOf({{ type_name|var_name|unquote }}: {{ type_name }}) -> [String: Any?] {
|
||||
switch ({{ type_name|var_name|unquote }}) {
|
||||
{%- for variant in e.variants() %}
|
||||
{% if variant.has_fields() %}
|
||||
case let .{{ variant.name()|var_name|unquote }}(
|
||||
{% for f in variant.fields() %}{{f.name()|var_name|unquote}}{%- if !loop.last %}, {% endif -%}{%- endfor %}
|
||||
):
|
||||
{% else %}
|
||||
case .{{ variant.name()|var_name|unquote }}:
|
||||
{% endif -%}
|
||||
return [
|
||||
"type": "{{ variant.name()|var_name|unquote }}",
|
||||
{%- for f in variant.fields() %}
|
||||
"{{ f.name()|var_name|unquote }}": {{ f.type_()|render_to_map(ci,"",f.name()|var_name|unquote, false) }},
|
||||
{%- endfor %}
|
||||
]
|
||||
{%- endfor %}
|
||||
}
|
||||
}
|
||||
|
||||
static func arrayOf({{ type_name|var_name|unquote|list_arg }}: [{{ type_name }}]) -> [Any] {
|
||||
return {{ type_name|var_name|unquote|list_arg }}.map { (v) -> [String: Any?] in return dictionaryOf({{ type_name|var_name|unquote }}: v) }
|
||||
}
|
||||
|
||||
{%- endif %}
|
||||
|
||||
static func as{{ type_name }}List(arr: [Any]) throws -> [{{ type_name }}] {
|
||||
var list = [{{ type_name }}]()
|
||||
for value in arr {
|
||||
{%- if e.is_flat() %}
|
||||
if let val = value as? String {
|
||||
{%- else %}
|
||||
if let val = value as? [String: Any?] {
|
||||
{%- endif %}
|
||||
var {{ type_name|var_name|unquote }} = try as{{ type_name }}({{ type_name|var_name|unquote }}: val)
|
||||
list.append({{ type_name|var_name|unquote }})
|
||||
} else {
|
||||
throw LsSdkError.Generic(message: errUnexpectedType(typeName: "{{ type_name }}"))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
RCT_EXTERN_METHOD(
|
||||
{%- if func.arguments().len() == 0 %}
|
||||
{{ func.name()|fn_name|unquote }}: (RCTPromiseResolveBlock)resolve{# -#}
|
||||
{% else -%}
|
||||
{%- for arg in func.arguments() %}
|
||||
{%- if loop.first %}
|
||||
{{ func.name()|fn_name|unquote }}: ({{arg.type_()|extern_type_name(ci)}}){{ arg.name()|var_name|unquote }}
|
||||
{%- else %}
|
||||
{{ arg.name()|var_name|unquote }}: ({{arg.type_()|extern_type_name(ci)}}){{ arg.name()|var_name|unquote }}
|
||||
{%- endif -%}
|
||||
{% endfor %}
|
||||
resolve: (RCTPromiseResolveBlock)resolve
|
||||
{%- endif %}
|
||||
reject: (RCTPromiseRejectBlock)reject
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
static func hasNonNilKey(data: [String: Any?], key: String) -> Bool {
|
||||
if let val = data[key] {
|
||||
return !(val == nil || val is NSNull)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
static func errMissingMandatoryField(fieldName: String, typeName: String) -> String {
|
||||
return "Missing mandatory field \(fieldName) for type \(typeName)"
|
||||
}
|
||||
|
||||
static func errUnexpectedType(typeName: String) -> String {
|
||||
return "Unexpected type \(typeName)"
|
||||
}
|
||||
|
||||
static func errUnexpectedValue(fieldName: String) -> String {
|
||||
return "Unexpected value for optional field \(fieldName)"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{%- for type_ in ci.iter_types() %}
|
||||
{%- let type_name = type_|type_name %}
|
||||
{%- match type_ %}
|
||||
{%- when Type::Object ( name ) %}
|
||||
{% let obj = ci.get_object_definition(name).unwrap() %}
|
||||
{% let obj_interface = "getBindingWallet()." %}
|
||||
{%- for func in obj.methods() -%}
|
||||
{%- include "TopLevelFunctionTemplate.swift" %}
|
||||
{% endfor %}
|
||||
{%- else -%}
|
||||
{%- endmatch -%}
|
||||
{%- endfor %}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
{%- let rec = ci.get_record_definition(name).unwrap() %}
|
||||
static func as{{ type_name }}({{ type_name|var_name|unquote }}: [String: Any?]) throws -> {{ type_name }} {
|
||||
{%- for field in rec.fields() %}
|
||||
{%- match field.type_() %}
|
||||
{%- when Type::Optional(_) %}
|
||||
var {{field.name()|var_name|unquote}}: {{field.type_()|type_name}}
|
||||
{% if field.type_()|inline_optional_field(ci) -%}
|
||||
if hasNonNilKey(data: {{ type_name|var_name|unquote }}, key: "{{field.name()|var_name|unquote}}") {
|
||||
guard let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else {
|
||||
throw LsSdkError.Generic(message: errUnexpectedValue(fieldName: "{{field.name()|var_name|unquote}}"))
|
||||
}
|
||||
{{field.name()|var_name|unquote}} = {{field.name()|var_name|unquote|temporary}}
|
||||
}
|
||||
{%- else -%}
|
||||
if let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} {
|
||||
{{field.name()|var_name|unquote}} = {{field.type_()|render_from_map(ci, field.name()|var_name|unquote|temporary)}}
|
||||
}
|
||||
{% endif -%}
|
||||
{%- else %}
|
||||
{% if field.type_()|inline_optional_field(ci) -%}
|
||||
guard let {{field.name()|var_name|unquote}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else {
|
||||
throw LsSdkError.Generic(message: errMissingMandatoryField(fieldName: "{{field.name()|var_name|unquote}}", typeName: "{{ type_name }}"))
|
||||
}
|
||||
{%- else -%}
|
||||
guard let {{field.name()|var_name|unquote|temporary}} = {{ type_name|var_name|unquote }}["{{field.name()|var_name|unquote}}"] as? {{field.type_()|rn_type_name(ci, true)}} else {
|
||||
throw LsSdkError.Generic(message: errMissingMandatoryField(fieldName: "{{field.name()|var_name|unquote}}", typeName: "{{ type_name }}"))
|
||||
}
|
||||
let {{field.name()|var_name|unquote}} = {{field.type_()|render_from_map(ci, field.name()|var_name|unquote|temporary)}}
|
||||
{% endif -%}
|
||||
{% endmatch %}
|
||||
{%- endfor %}
|
||||
|
||||
return {{ type_name }}({%- call swift::field_list(rec) -%})
|
||||
}
|
||||
|
||||
static func dictionaryOf({{ type_name|var_name|unquote }}: {{ type_name }}) -> [String: Any?] {
|
||||
return [
|
||||
{%- for field in rec.fields() %}
|
||||
"{{ field.name()|var_name|unquote }}": {{ field.type_()|render_to_map(ci,type_name|var_name|unquote,field.name()|var_name|unquote,false)}},
|
||||
{%- endfor %}
|
||||
]
|
||||
}
|
||||
|
||||
static func as{{ type_name }}List(arr: [Any]) throws -> [{{ type_name }}] {
|
||||
var list = [{{ type_name }}]()
|
||||
for value in arr {
|
||||
if let val = value as? [String: Any?] {
|
||||
var {{ type_name|var_name|unquote }} = try as{{ type_name }}({{ type_name|var_name|unquote }}: val)
|
||||
list.append({{ type_name|var_name|unquote }})
|
||||
} else {
|
||||
throw LsSdkError.Generic(message: errUnexpectedType(typeName: "{{ type_name }}"))
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
static func arrayOf({{ type_name|var_name|unquote|list_arg }}: [{{ type_name }}]) -> [Any] {
|
||||
return {{ type_name|var_name|unquote|list_arg }}.map { (v) -> [String: Any?] in return dictionaryOf({{ type_name|var_name|unquote }}: v) }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
@objc({%- call swift::extern_arg_list(func) -%})
|
||||
func {{ func.name()|fn_name|unquote }}(_ {% call swift::arg_list_decl(func) -%}resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
||||
do {
|
||||
{%- for arg in func.arguments() -%}
|
||||
{%- match arg.type_() %}
|
||||
{%- when Type::Enum(inner) %}
|
||||
{%- let e = ci.get_enum_definition(inner).unwrap() %}
|
||||
{%- if e.is_flat() %}
|
||||
let {{arg.name()|var_name|unquote|temporary}} = try LiquidSwapSDKMapper.as{{arg.type_()|type_name}}({{ arg.type_()|type_name|var_name|unquote }}: {{ arg.name()|var_name|unquote }})
|
||||
{%- else %}
|
||||
let {{arg.name()|var_name|unquote|temporary}} = try LiquidSwapSDKMapper.as{{arg.type_()|type_name}}({{ arg.type_()|type_name|var_name|unquote }}: {{ arg.name()|var_name|unquote }})
|
||||
{%- endif %}
|
||||
{%- when Type::Optional(_) %}
|
||||
let {{arg.name()|var_name|unquote|temporary}} = {{ arg.type_()|rn_convert_type(arg.name()|var_name|unquote) -}}
|
||||
{%- when Type::Record(_) %}
|
||||
let {{arg.type_()|type_name|var_name|unquote}} = try LiquidSwapSDKMapper.as{{arg.type_()|type_name}}({{ arg.type_()|type_name|var_name|unquote }}: {{ arg.name()|var_name|unquote }})
|
||||
{%- else %}
|
||||
{%- endmatch %}
|
||||
{%- endfor %}
|
||||
{%- match func.return_type() -%}
|
||||
{%- when Some with (return_type) %}
|
||||
var res = {%- call swift::throws_decl(func) -%}{{ obj_interface }}{{ func.name()|fn_name|unquote }}({%- call swift::arg_list(func) -%})
|
||||
{%- match return_type %}
|
||||
{%- when Type::Optional(inner) %}
|
||||
{%- let unboxed = inner.as_ref() %}
|
||||
if res != nil {
|
||||
resolve({{ unboxed|rn_return_type(unboxed|type_name|var_name|unquote, true) }})
|
||||
} else {
|
||||
resolve(nil)
|
||||
}
|
||||
{%- else %}
|
||||
resolve({{ return_type|rn_return_type(return_type|type_name|var_name|unquote, false) }})
|
||||
{%- endmatch %}
|
||||
{%- when None %}
|
||||
try {{ obj_interface }}{{ func.name()|fn_name|unquote }}({%- call swift::arg_list(func) -%})
|
||||
resolve(["status": "ok"])
|
||||
{%- endmatch %}
|
||||
} catch let err {
|
||||
rejectErr(err: err, reject: reject)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{%- for type_ in ci.iter_types() %}
|
||||
{%- let type_name = type_|type_name %}
|
||||
{%- match type_ %}
|
||||
{%- when Type::Record ( name ) %}
|
||||
{%- include "RecordTemplate.swift" %}
|
||||
{%- when Type::Enum ( name ) %}
|
||||
{%- include "EnumTemplate.swift" %}
|
||||
{%- else %}
|
||||
{%- endmatch -%}
|
||||
|
||||
{%- endfor %}
|
||||
@@ -0,0 +1,28 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface RCT_EXTERN_MODULE(RNLiquidSwapSDK, RCTEventEmitter)
|
||||
{% for func in ci.function_definitions() %}
|
||||
{%- if func.name()|ignored_function == false -%}
|
||||
{% include "ExternFunctionTemplate.m" %}
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
RCT_EXTERN_METHOD(
|
||||
initBindingWallet: (NSString*)mnemonic
|
||||
dataDir: (NSString*)dataDir
|
||||
network: (NSString*)network
|
||||
resolve: (RCTPromiseResolveBlock)resolve
|
||||
reject: (RCTPromiseRejectBlock)reject
|
||||
)
|
||||
{%- for type_ in ci.iter_types() %}
|
||||
{%- let type_name = type_|type_name %}
|
||||
{%- match type_ %}
|
||||
{%- when Type::Object ( name ) %}
|
||||
{% let obj = ci.get_object_definition(name).unwrap() %}
|
||||
{%- for func in obj.methods() -%}
|
||||
{%- include "ExternFunctionTemplate.m" %}
|
||||
{% endfor %}
|
||||
{%- else -%}
|
||||
{%- endmatch -%}
|
||||
{%- endfor %}
|
||||
@end
|
||||
@@ -0,0 +1,42 @@
|
||||
{% macro arg_list(func) %}
|
||||
{%- for arg in func.arguments() -%}
|
||||
{%- match arg.type_() -%}
|
||||
{%- when Type::Enum(_) -%}
|
||||
{{ arg.name()|var_name|unquote }}: {{ arg.name()|var_name|unquote|temporary -}}
|
||||
{%- when Type::Optional(_) -%}
|
||||
{{ arg.name()|var_name|unquote }}: {{ arg.name()|var_name|unquote|temporary -}}
|
||||
{%- when Type::Record(_) -%}
|
||||
{{ arg.name()|var_name|unquote }}: {{ arg.type_()|type_name|var_name|unquote -}}
|
||||
{%- else -%}
|
||||
{{ arg.name()|var_name|unquote }}: {{ arg.name()|var_name|unquote -}}
|
||||
{%- endmatch -%}
|
||||
{%- if !loop.last %}, {% endif -%}
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro arg_list_decl(func) %}
|
||||
{%- for arg in func.arguments() -%}
|
||||
{{- arg.name()|var_name|unquote }}: {{ arg.type_()|rn_type_name(ci, false) -}}, {% endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro extern_arg_list(func) %}
|
||||
{{- func.name()|var_name|unquote -}}:
|
||||
{%- for arg in func.arguments() -%}
|
||||
{%- if !loop.first -%}
|
||||
{{- arg.name()|var_name|unquote }}:
|
||||
{%- endif -%}
|
||||
{%- endfor %}
|
||||
{%- if func.arguments().len() >= 1 -%}resolve:{%- endif -%}reject:
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro field_list(rec) %}
|
||||
{%- for f in rec.fields() %}
|
||||
{{ f.name()|var_name|unquote }}: {{ f.name()|var_name|unquote }}{%- if !loop.last %}, {% endif -%}
|
||||
{%- endfor %}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro throws_decl(func) %}
|
||||
{%- match func.throws_type() -%}
|
||||
{%- when Some with (throws_type) -%}try {% else -%}
|
||||
{%- endmatch -%}
|
||||
{%- endmacro -%}
|
||||
@@ -0,0 +1,11 @@
|
||||
{%- import "macros.swift" as swift -%}
|
||||
import Foundation
|
||||
import LiquidSwapSDK
|
||||
|
||||
enum LiquidSwapSDKMapper {
|
||||
|
||||
{%- include "Types.swift" %}
|
||||
|
||||
{%- include "Helpers.swift" %}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import Foundation
|
||||
import LiquidSwapSDK
|
||||
|
||||
@objc(RNLiquidSwapSDK)
|
||||
class RNLiquidSwapSDK: RCTEventEmitter {
|
||||
static let TAG: String = "LiquidSwapSDK"
|
||||
|
||||
public static var emitter: RCTEventEmitter!
|
||||
public static var hasListeners: Bool = false
|
||||
|
||||
private var bindingWallet: BindingWallet!
|
||||
|
||||
static var defaultDataDir: URL {
|
||||
let applicationDirectory = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
|
||||
|
||||
return applicationDirectory.appendingPathComponent("lsSdk", isDirectory: true)
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
RNLiquidSwapSDK.emitter = self
|
||||
}
|
||||
|
||||
@objc
|
||||
override static func moduleName() -> String! {
|
||||
TAG
|
||||
}
|
||||
|
||||
override func supportedEvents() -> [String]! {
|
||||
return []
|
||||
}
|
||||
|
||||
override func startObserving() {
|
||||
RNLiquidSwapSDK.hasListeners = true
|
||||
}
|
||||
|
||||
override func stopObserving() {
|
||||
RNLiquidSwapSDK.hasListeners = false
|
||||
}
|
||||
|
||||
@objc
|
||||
override static func requiresMainQueueSetup() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func getBindingWallet() throws -> BindingWallet {
|
||||
if bindingWallet != nil {
|
||||
return bindingWallet
|
||||
}
|
||||
|
||||
throw LsSdkError.Generic(message: "Not initialized")
|
||||
}
|
||||
|
||||
{% let obj_interface = "LiquidSwapSDK." -%}
|
||||
{% for func in ci.function_definitions() %}
|
||||
{%- if func.name()|ignored_function == false -%}
|
||||
{% include "TopLevelFunctionTemplate.swift" %}
|
||||
{% endif -%}
|
||||
{%- endfor %}
|
||||
@objc(initBindingWallet:dataDir:network:resolve:reject:)
|
||||
func initBindingWallet(_ mnemonic: String, dataDir: String, network: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
||||
if bindingWallet != nil {
|
||||
reject("Generic", "Already initialized", nil)
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let dataDirTmp = dataDir.isEmpty ? RNLiquidSwapSDK.defaultDataDir.path : dataDir
|
||||
let networkTmp = try LiquidSwapSDKMapper.asNetwork(network: network)
|
||||
bindingWallet = try LiquidSwapSDK.`init`(mnemonic: mnemonic, dataDir: dataDirTmp, network: networkTmp)
|
||||
resolve(["status": "ok"])
|
||||
} catch let err {
|
||||
rejectErr(err: err, reject: reject)
|
||||
}
|
||||
}
|
||||
{%- include "Objects.swift" %}
|
||||
|
||||
func rejectErr(err: Error, reject: @escaping RCTPromiseRejectBlock) {
|
||||
var errorName = "Generic"
|
||||
var message = "\(err)"
|
||||
if let errAssociated = Mirror(reflecting: err).children.first {
|
||||
errorName = errAssociated.label ?? errorName
|
||||
if let associatedMessage = Mirror(reflecting: errAssociated.value).children.first {
|
||||
message = associatedMessage.value as! String
|
||||
}
|
||||
}
|
||||
reject(errorName, message, err)
|
||||
}
|
||||
}
|
||||
|
||||
{% import "macros.swift" as swift %}
|
||||
@@ -0,0 +1,33 @@
|
||||
/* 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 uniffi_bindgen::backend::{CodeOracle, CodeType, Literal};
|
||||
|
||||
pub struct CallbackInterfaceCodeType {
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl CallbackInterfaceCodeType {
|
||||
pub fn new(id: String) -> Self {
|
||||
Self { id }
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeType for CallbackInterfaceCodeType {
|
||||
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
|
||||
oracle.class_name(&self.id)
|
||||
}
|
||||
|
||||
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
format!("Type{}", self.id)
|
||||
}
|
||||
|
||||
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
|
||||
nm.to_string()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* 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 paste::paste;
|
||||
use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal, TypeIdentifier};
|
||||
|
||||
fn render_literal(oracle: &dyn CodeOracle, literal: &Literal, inner: &TypeIdentifier) -> String {
|
||||
match literal {
|
||||
Literal::Null => "null".into(),
|
||||
Literal::EmptySequence => "[]".into(),
|
||||
Literal::EmptyMap => "{}".into(),
|
||||
|
||||
// For optionals
|
||||
_ => oracle.find(inner).literal(oracle, literal),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_code_type_for_compound {
|
||||
($T:ty, $type_label_pattern:literal, $canonical_name_pattern: literal) => {
|
||||
paste! {
|
||||
pub struct $T {
|
||||
inner: TypeIdentifier,
|
||||
}
|
||||
|
||||
impl $T {
|
||||
pub fn new(inner: TypeIdentifier) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
fn inner(&self) -> &TypeIdentifier {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeType for $T {
|
||||
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
|
||||
format!($type_label_pattern, oracle.find(self.inner()).type_label(oracle))
|
||||
}
|
||||
|
||||
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
|
||||
format!($canonical_name_pattern, oracle.find(self.inner()).canonical_name(oracle))
|
||||
}
|
||||
|
||||
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
|
||||
render_literal(oracle, literal, self.inner())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_code_type_for_compound!(OptionalCodeType, "{}?", "Optional{}");
|
||||
impl_code_type_for_compound!(SequenceCodeType, "{}[]", "Sequence{}");
|
||||
|
||||
pub struct MapCodeType {
|
||||
key: TypeIdentifier,
|
||||
value: TypeIdentifier,
|
||||
}
|
||||
|
||||
impl MapCodeType {
|
||||
pub fn new(key: TypeIdentifier, value: TypeIdentifier) -> Self {
|
||||
Self { key, value }
|
||||
}
|
||||
|
||||
fn key(&self) -> &TypeIdentifier {
|
||||
&self.key
|
||||
}
|
||||
|
||||
fn value(&self) -> &TypeIdentifier {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeType for MapCodeType {
|
||||
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
|
||||
format!(
|
||||
"Record<{}, {}>",
|
||||
self.key().type_label(oracle),
|
||||
self.value().type_label(oracle),
|
||||
)
|
||||
}
|
||||
|
||||
fn canonical_name(&self, oracle: &dyn CodeOracle) -> String {
|
||||
format!(
|
||||
"Record{}{}",
|
||||
self.key().type_label(oracle),
|
||||
self.value().type_label(oracle),
|
||||
)
|
||||
}
|
||||
|
||||
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
|
||||
render_literal(oracle, literal, &self.value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* 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 uniffi_bindgen::backend::{CodeOracle, CodeType};
|
||||
|
||||
pub struct CustomCodeType {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl CustomCodeType {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeType for CustomCodeType {
|
||||
fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
format!("Type{}", self.name)
|
||||
}
|
||||
|
||||
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
|
||||
nm.to_string()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* 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 uniffi_bindgen::backend::{CodeOracle, CodeType, Literal};
|
||||
|
||||
pub struct EnumCodeType {
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl EnumCodeType {
|
||||
pub fn new(id: String) -> Self {
|
||||
Self { id }
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeType for EnumCodeType {
|
||||
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
|
||||
oracle.class_name(&self.id)
|
||||
}
|
||||
|
||||
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
format!("Type{}", self.id)
|
||||
}
|
||||
|
||||
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
|
||||
if let Literal::Enum(v, _) = literal {
|
||||
format!(
|
||||
"{}.{}",
|
||||
self.type_label(oracle),
|
||||
oracle.enum_variant_name(v)
|
||||
)
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* 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 uniffi_bindgen::backend::{CodeOracle, CodeType, Literal};
|
||||
|
||||
pub struct ErrorCodeType {
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl ErrorCodeType {
|
||||
pub fn new(id: String) -> Self {
|
||||
Self { id }
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeType for ErrorCodeType {
|
||||
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
|
||||
oracle.error_name(&self.id)
|
||||
}
|
||||
|
||||
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
format!("Type{}", self.id)
|
||||
}
|
||||
|
||||
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* 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 uniffi_bindgen::backend::{CodeOracle, CodeType};
|
||||
|
||||
pub struct ExternalCodeType {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl ExternalCodeType {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeType for ExternalCodeType {
|
||||
fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
format!("Type{}", self.name)
|
||||
}
|
||||
|
||||
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
|
||||
nm.into()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
use paste::paste;
|
||||
use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal};
|
||||
|
||||
macro_rules! impl_code_type_for_miscellany {
|
||||
($T:ty, $canonical_name:literal) => {
|
||||
paste! {
|
||||
pub struct $T;
|
||||
|
||||
impl CodeType for $T {
|
||||
fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
format!("{}", $canonical_name)
|
||||
}
|
||||
|
||||
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
format!("{}", $canonical_name)
|
||||
}
|
||||
|
||||
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn coerce(&self, _oracle: &dyn CodeOracle, nm: &str) -> String {
|
||||
nm.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_code_type_for_miscellany!(TimestampCodeType, "Date");
|
||||
@@ -0,0 +1,236 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use askama::Template;
|
||||
use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase};
|
||||
use once_cell::sync::Lazy;
|
||||
use uniffi_bindgen::backend::{CodeOracle, CodeType, TypeIdentifier};
|
||||
use uniffi_bindgen::interface::*;
|
||||
|
||||
use crate::generator::RNConfig;
|
||||
|
||||
mod callback_interface;
|
||||
mod compounds;
|
||||
mod custom;
|
||||
mod enum_;
|
||||
mod error;
|
||||
mod external;
|
||||
mod miscellany;
|
||||
mod object;
|
||||
mod primitives;
|
||||
mod record;
|
||||
|
||||
// Keywords to fix
|
||||
static KEYWORDS: Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
let list = vec!["Function", "Number", "Object", "Record", "String", "Symbol"];
|
||||
HashSet::from_iter(list.into_iter().map(|s| s.to_string()))
|
||||
});
|
||||
|
||||
static IGNORED_FUNCTIONS: Lazy<HashSet<String>> = Lazy::new(|| {
|
||||
let list: Vec<&str> = vec!["init"];
|
||||
HashSet::from_iter(list.into_iter().map(|s| s.to_string()))
|
||||
});
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(syntax = "rn", escape = "none", path = "module.ts")]
|
||||
#[allow(dead_code)]
|
||||
pub struct ModuleGenerator<'a> {
|
||||
config: RNConfig,
|
||||
ci: &'a ComponentInterface,
|
||||
}
|
||||
|
||||
impl<'a> ModuleGenerator<'a> {
|
||||
pub fn new(config: RNConfig, ci: &'a ComponentInterface) -> Self {
|
||||
Self { config, ci }
|
||||
}
|
||||
}
|
||||
|
||||
fn fixup_keyword(name: String, append: String) -> String {
|
||||
if KEYWORDS.contains(&name) {
|
||||
format!("{name}{append}")
|
||||
} else {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypescriptCodeOracle;
|
||||
|
||||
impl TypescriptCodeOracle {
|
||||
// Map `Type` instances to a `Box<dyn CodeType>` for that type.
|
||||
//
|
||||
// There is a companion match in `templates/Types.ts` which performs a similar function for the
|
||||
// template code.
|
||||
//
|
||||
// - When adding additional types here, make sure to also add a match arm to the `Types.ts` template.
|
||||
// - To keep things managable, let's try to limit ourselves to these 2 mega-matches
|
||||
fn create_code_type(&self, type_: TypeIdentifier) -> Box<dyn CodeType> {
|
||||
match type_ {
|
||||
Type::UInt8 => Box::new(primitives::UInt8CodeType),
|
||||
Type::Int8 => Box::new(primitives::Int8CodeType),
|
||||
Type::UInt16 => Box::new(primitives::UInt16CodeType),
|
||||
Type::Int16 => Box::new(primitives::Int16CodeType),
|
||||
Type::UInt32 => Box::new(primitives::UInt32CodeType),
|
||||
Type::Int32 => Box::new(primitives::Int32CodeType),
|
||||
Type::UInt64 => Box::new(primitives::UInt64CodeType),
|
||||
Type::Int64 => Box::new(primitives::Int64CodeType),
|
||||
Type::Float32 => Box::new(primitives::Float32CodeType),
|
||||
Type::Float64 => Box::new(primitives::Float64CodeType),
|
||||
Type::Boolean => Box::new(primitives::BooleanCodeType),
|
||||
Type::String => Box::new(primitives::StringCodeType),
|
||||
|
||||
Type::Timestamp => Box::new(miscellany::TimestampCodeType),
|
||||
Type::Duration => {
|
||||
unimplemented!("Duration is not implemented")
|
||||
}
|
||||
|
||||
Type::Enum(id) => Box::new(enum_::EnumCodeType::new(id)),
|
||||
Type::Object(id) => Box::new(object::ObjectCodeType::new(id)),
|
||||
Type::Record(id) => Box::new(record::RecordCodeType::new(id)),
|
||||
Type::Error(id) => Box::new(error::ErrorCodeType::new(id)),
|
||||
Type::CallbackInterface(id) => {
|
||||
Box::new(callback_interface::CallbackInterfaceCodeType::new(id))
|
||||
}
|
||||
Type::Optional(inner) => Box::new(compounds::OptionalCodeType::new(*inner)),
|
||||
Type::Sequence(inner) => Box::new(compounds::SequenceCodeType::new(*inner)),
|
||||
Type::Map(key, value) => Box::new(compounds::MapCodeType::new(*key, *value)),
|
||||
Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)),
|
||||
Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
|
||||
|
||||
Type::Unresolved { name } => {
|
||||
unreachable!("Type `{name}` must be resolved before calling create_code_type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeOracle for TypescriptCodeOracle {
|
||||
fn find(&self, type_: &TypeIdentifier) -> Box<dyn CodeType> {
|
||||
self.create_code_type(type_.clone())
|
||||
}
|
||||
|
||||
/// Get the idiomatic Typescript rendering of a class name (for enums, records, errors, etc).
|
||||
fn class_name(&self, nm: &str) -> String {
|
||||
fixup_keyword(nm.to_string().to_upper_camel_case(), "Type".to_string())
|
||||
}
|
||||
|
||||
/// Get the idiomatic Typescript rendering of a function name.
|
||||
fn fn_name(&self, nm: &str) -> String {
|
||||
fixup_keyword(nm.to_string().to_lower_camel_case(), "Fn".to_string())
|
||||
}
|
||||
|
||||
/// Get the idiomatic Typescript rendering of a variable name.
|
||||
fn var_name(&self, nm: &str) -> String {
|
||||
fixup_keyword(nm.to_string().to_lower_camel_case(), "Var".to_string())
|
||||
}
|
||||
|
||||
/// Get the idiomatic Typescript rendering of an individual enum variant.
|
||||
fn enum_variant_name(&self, nm: &str) -> String {
|
||||
fixup_keyword(nm.to_string().to_shouty_snake_case(), "Enum".to_string())
|
||||
}
|
||||
|
||||
/// Get the idiomatic Typescript rendering of an exception name
|
||||
fn error_name(&self, nm: &str) -> String {
|
||||
self.class_name(nm)
|
||||
}
|
||||
|
||||
fn ffi_type_label(&self, ffi_type: &FfiType) -> String {
|
||||
match ffi_type {
|
||||
FfiType::Int8
|
||||
| FfiType::UInt8
|
||||
| FfiType::Int16
|
||||
| FfiType::UInt16
|
||||
| FfiType::Int32
|
||||
| FfiType::UInt32
|
||||
| FfiType::Int64
|
||||
| FfiType::UInt64
|
||||
| FfiType::Float32
|
||||
| FfiType::Float64 => "number".to_string(),
|
||||
FfiType::RustArcPtr(name) => format!("{}SafeHandle", name),
|
||||
FfiType::RustBuffer(_) => "RustBuffer".to_string(),
|
||||
FfiType::ForeignBytes => "ForeignBytes".to_string(),
|
||||
FfiType::ForeignCallback => "ForeignCallback".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod filters {
|
||||
use uniffi_bindgen::backend::CodeType;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn oracle() -> &'static TypescriptCodeOracle {
|
||||
&TypescriptCodeOracle
|
||||
}
|
||||
|
||||
pub fn type_name(codetype: &impl CodeType) -> Result<String, askama::Error> {
|
||||
Ok(codetype.type_label(oracle()))
|
||||
}
|
||||
|
||||
/// Get the idiomatic Typescript rendering of a function name.
|
||||
pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(oracle().fn_name(nm))
|
||||
}
|
||||
|
||||
/// Get the idiomatic Typescript rendering of a variable name.
|
||||
pub fn var_name(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(oracle().var_name(nm))
|
||||
}
|
||||
|
||||
/// Get the idiomatic Typescript rendering of an individual enum variant.
|
||||
pub fn enum_variant(nm: &str) -> Result<String, askama::Error> {
|
||||
Ok(oracle().enum_variant_name(nm))
|
||||
}
|
||||
|
||||
pub fn absolute_type_name(t: &TypeIdentifier) -> Result<String, askama::Error> {
|
||||
let res: Result<String, askama::Error> = match t {
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
type_name(unboxed)
|
||||
}
|
||||
_ => type_name(t),
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn return_type_name(t: &TypeIdentifier) -> Result<String, askama::Error> {
|
||||
let res: Result<String, askama::Error> = match t {
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
let name = type_name(unboxed)?;
|
||||
Ok(format!("{name} | null"))
|
||||
}
|
||||
_ => type_name(t),
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn default_value(t: &TypeIdentifier) -> Result<String, askama::Error> {
|
||||
let res: Result<String, askama::Error> = match t {
|
||||
Type::Optional(inner) => {
|
||||
let unboxed = inner.as_ref();
|
||||
match unboxed {
|
||||
Type::UInt8
|
||||
| Type::Int8
|
||||
| Type::UInt16
|
||||
| Type::Int16
|
||||
| Type::UInt32
|
||||
| Type::Int32
|
||||
| Type::UInt64
|
||||
| Type::Int64
|
||||
| Type::Float32
|
||||
| Type::Float64 => Ok(" = 0".into()),
|
||||
Type::String => Ok(" = \"\"".into()),
|
||||
Type::Record(_) => Ok(" = {}".into()),
|
||||
Type::Sequence(_) => Ok(" = []".into()),
|
||||
_ => Ok("".into()),
|
||||
}
|
||||
}
|
||||
_ => Ok("".into()),
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn ignored_function(nm: &str) -> Result<bool, askama::Error> {
|
||||
Ok(IGNORED_FUNCTIONS.contains(nm))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* 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 uniffi_bindgen::backend::{CodeOracle, CodeType, Literal};
|
||||
|
||||
pub struct ObjectCodeType {
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl ObjectCodeType {
|
||||
pub fn new(id: String) -> Self {
|
||||
Self { id }
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeType for ObjectCodeType {
|
||||
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
|
||||
oracle.class_name(&self.id)
|
||||
}
|
||||
|
||||
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
format!("Type{}", self.id)
|
||||
}
|
||||
|
||||
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
use paste::paste;
|
||||
use uniffi_bindgen::backend::{CodeOracle, CodeType, Literal};
|
||||
use uniffi_bindgen::interface::{types::Type, Radix};
|
||||
|
||||
fn render_literal(_oracle: &dyn CodeOracle, literal: &Literal) -> String {
|
||||
fn typed_number(type_: &Type, num_str: String) -> String {
|
||||
match type_ {
|
||||
// Bytes, Shorts and Ints can all be inferred from the type.
|
||||
Type::Int8 | Type::Int16 | Type::Int32 => num_str,
|
||||
Type::Int64 => format!("{num_str}L"),
|
||||
|
||||
Type::UInt8 | Type::UInt16 | Type::UInt32 => format!("{num_str}u"),
|
||||
Type::UInt64 => format!("{num_str}uL"),
|
||||
|
||||
Type::Float32 => format!("{num_str}f"),
|
||||
Type::Float64 => num_str,
|
||||
_ => panic!("Unexpected literal: {num_str} is not a number"),
|
||||
}
|
||||
}
|
||||
|
||||
match literal {
|
||||
Literal::Boolean(v) => format!("{v}"),
|
||||
Literal::String(s) => format!("\"{s}\""),
|
||||
Literal::Int(i, radix, type_) => typed_number(
|
||||
type_,
|
||||
match radix {
|
||||
Radix::Octal => format!("{i:#x}"),
|
||||
Radix::Decimal => format!("{i}"),
|
||||
Radix::Hexadecimal => format!("{i:#x}"),
|
||||
},
|
||||
),
|
||||
Literal::UInt(i, radix, type_) => typed_number(
|
||||
type_,
|
||||
match radix {
|
||||
Radix::Octal => format!("{i:#x}"),
|
||||
Radix::Decimal => format!("{i}"),
|
||||
Radix::Hexadecimal => format!("{i:#x}"),
|
||||
},
|
||||
),
|
||||
Literal::Float(string, type_) => typed_number(type_, string.clone()),
|
||||
|
||||
_ => unreachable!("Literal"),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_code_type_for_primitive {
|
||||
($T:ty, $class_name:literal) => {
|
||||
paste! {
|
||||
pub struct $T;
|
||||
|
||||
impl CodeType for $T {
|
||||
fn type_label(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
$class_name.into()
|
||||
}
|
||||
|
||||
fn literal(&self, oracle: &dyn CodeOracle, literal: &Literal) -> String {
|
||||
render_literal(oracle, &literal)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_code_type_for_primitive!(BooleanCodeType, "boolean");
|
||||
impl_code_type_for_primitive!(StringCodeType, "string");
|
||||
impl_code_type_for_primitive!(Int8CodeType, "number");
|
||||
impl_code_type_for_primitive!(Int16CodeType, "number");
|
||||
impl_code_type_for_primitive!(Int32CodeType, "number");
|
||||
impl_code_type_for_primitive!(Int64CodeType, "number");
|
||||
impl_code_type_for_primitive!(UInt8CodeType, "number");
|
||||
impl_code_type_for_primitive!(UInt16CodeType, "number");
|
||||
impl_code_type_for_primitive!(UInt32CodeType, "number");
|
||||
impl_code_type_for_primitive!(UInt64CodeType, "number");
|
||||
impl_code_type_for_primitive!(Float32CodeType, "number");
|
||||
impl_code_type_for_primitive!(Float64CodeType, "number");
|
||||
@@ -0,0 +1,29 @@
|
||||
/* 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 uniffi_bindgen::backend::{CodeOracle, CodeType, Literal};
|
||||
|
||||
pub struct RecordCodeType {
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl RecordCodeType {
|
||||
pub fn new(id: String) -> Self {
|
||||
Self { id }
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeType for RecordCodeType {
|
||||
fn type_label(&self, oracle: &dyn CodeOracle) -> String {
|
||||
oracle.class_name(&self.id)
|
||||
}
|
||||
|
||||
fn canonical_name(&self, _oracle: &dyn CodeOracle) -> String {
|
||||
format!("Type{}", self.id)
|
||||
}
|
||||
|
||||
fn literal(&self, _oracle: &dyn CodeOracle, _literal: &Literal) -> String {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{%- let e = ci.get_enum_definition(name).unwrap() %}
|
||||
{%- if e.is_flat() %}
|
||||
|
||||
export enum {{ type_name }} {
|
||||
{% for variant in e.variants() -%}
|
||||
{{ variant.name()|enum_variant }} = "{{ variant.name()|var_name }}"{% if !loop.last %},
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
}
|
||||
|
||||
{%- else %}
|
||||
|
||||
export enum {{ type_name }}Variant {
|
||||
{% for variant in e.variants() -%}
|
||||
{{ variant.name()|enum_variant }} = "{{ variant.name()|var_name }}"{% if !loop.last %},
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
}
|
||||
|
||||
export type {{ type_name }} = {% for variant in e.variants() -%}{
|
||||
type: {{ type_name }}Variant.{{ variant.name()|enum_variant }}{% if variant.has_fields() %},
|
||||
{%- call ts::field_list_decl(variant) -%}{% endif %}
|
||||
}{% if !loop.last %} | {% endif %}
|
||||
{%- endfor %}
|
||||
|
||||
{%- endif %}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
export const init = async (mnemonic: string, dataDir: string = "", network: Network): Promise<void> => {
|
||||
const response = await LiquidSwapSDK.initBindingWallet(mnemonic, dataDir, network)
|
||||
return response
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{%- for type_ in ci.iter_types() %}
|
||||
{%- let type_name = type_|type_name %}
|
||||
{%- match type_ %}
|
||||
{%- when Type::Object ( name ) %}
|
||||
{% let obj = ci.get_object_definition(name).unwrap() %}
|
||||
{%- for func in obj.methods() -%}
|
||||
{%- include "TopLevelFunctionTemplate.ts" %}
|
||||
{% endfor %}
|
||||
{%- else -%}
|
||||
{%- endmatch -%}
|
||||
{%- endfor %}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{%- let rec = ci.get_record_definition(name).unwrap() %}
|
||||
|
||||
export type {{ type_name }} = {
|
||||
{%- call ts::field_list_decl(rec) %}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{%- match func.return_type() -%}
|
||||
{%- when Some with (return_type) %}
|
||||
export const {{ func.name()|fn_name }} = async ({%- call ts::arg_list_decl(func) -%}): Promise<{{ return_type|return_type_name }}> => {
|
||||
const response = await LiquidSwapSDK.{{func.name()|fn_name}}({%- call ts::arg_list(func) -%})
|
||||
return response
|
||||
}
|
||||
{%- when None %}
|
||||
export const {{ func.name()|fn_name }} = async ({%- call ts::arg_list_decl(func) -%}): Promise<void> => {
|
||||
await LiquidSwapSDK.{{ func.name()|fn_name }}({%- call ts::arg_list(func) -%})
|
||||
}
|
||||
{%- endmatch %}
|
||||
@@ -0,0 +1,10 @@
|
||||
{%- for type_ in ci.iter_types() %}
|
||||
{%- let type_name = type_|type_name %}
|
||||
{%- match type_ %}
|
||||
{%- when Type::Record ( name ) %}
|
||||
{%- include "RecordTemplate.ts" %}
|
||||
{%- when Type::Enum ( name ) %}
|
||||
{%- include "EnumTemplate.ts" %}
|
||||
{%- else %}
|
||||
{%- endmatch -%}
|
||||
{%- endfor %}
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
{% macro arg_list(func) %}
|
||||
{%- for arg in func.arguments() -%}
|
||||
{{ arg.name()|var_name -}}
|
||||
{%- if !loop.last %}, {% endif -%}
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro field_list(rec) %}
|
||||
{%- for f in rec.fields() %}
|
||||
{{ f.name()|var_name|unquote }},
|
||||
{%- endfor %}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro field_list_decl(rec) %}
|
||||
{%- for f in rec.fields() %}
|
||||
{%- match f.type_() %}
|
||||
{%- when Type::Optional(inner) %}
|
||||
{%- let unboxed = inner.as_ref() %}
|
||||
{{ f.name()|var_name }}?: {{ unboxed|type_name }}
|
||||
{%- else %}
|
||||
{{ f.name()|var_name }}: {{ f.type_()|type_name }}
|
||||
{%- endmatch %}
|
||||
{%- endfor %}
|
||||
{%- endmacro -%}
|
||||
|
||||
{% macro arg_list_decl(func) %}
|
||||
{%- for arg in func.arguments() -%}
|
||||
{{ arg.name()|var_name }}: {{ arg.type_()|absolute_type_name }}{{- arg.type_()|default_value -}}
|
||||
{%- if !loop.last %}, {% endif -%}
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { NativeModules, Platform } from "react-native"
|
||||
|
||||
const LINKING_ERROR =
|
||||
`The package 'react-native-liquid-swap-sdk' doesn't seem to be linked. Make sure: \n\n` +
|
||||
Platform.select({ ios: "- You have run 'pod install'\n", default: "" }) +
|
||||
"- You rebuilt the app after installing the package\n" +
|
||||
"- You are not using Expo managed workflow\n"
|
||||
|
||||
const LiquidSwapSDK = NativeModules.RNLiquidSwapSDK
|
||||
? NativeModules.RNLiquidSwapSDK
|
||||
: new Proxy(
|
||||
{},
|
||||
{
|
||||
get() {
|
||||
throw new Error(LINKING_ERROR)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
{%- import "macros.ts" as ts %}
|
||||
{%- include "Types.ts" %}
|
||||
{% include "Helpers.ts" -%}
|
||||
{% for func in ci.function_definitions() %}
|
||||
{%- if func.name()|ignored_function == false -%}
|
||||
{%- include "TopLevelFunctionTemplate.ts" %}
|
||||
{% endif -%}
|
||||
{% endfor -%}
|
||||
{%- include "Objects.ts" %}
|
||||
264
lib/ls-sdk-bindings/bindings-react-native/src/generator.rs
Normal file
264
lib/ls-sdk-bindings/bindings-react-native/src/generator.rs
Normal file
@@ -0,0 +1,264 @@
|
||||
use anyhow::Result;
|
||||
use askama::Template;
|
||||
use camino::Utf8Path;
|
||||
use camino::Utf8PathBuf;
|
||||
use serde::*;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
use uniffi_bindgen::{BindingGenerator, BindingGeneratorConfig, ComponentInterface};
|
||||
|
||||
use crate::gen_kotlin;
|
||||
use crate::gen_swift;
|
||||
use crate::gen_typescript;
|
||||
|
||||
pub struct RNBindingGenerator {}
|
||||
|
||||
impl RNBindingGenerator {
|
||||
fn write_bindings(
|
||||
&self,
|
||||
bindings_output: &String,
|
||||
output_path: &Utf8Path,
|
||||
file_name: &Utf8Path,
|
||||
) -> Result<Utf8PathBuf> {
|
||||
fs::create_dir_all(output_path)?;
|
||||
let bindings_path: camino::Utf8PathBuf = output_path.join(file_name);
|
||||
let mut f: File = File::create(&bindings_path)?;
|
||||
write!(f, "{}", bindings_output)?;
|
||||
Ok(bindings_path)
|
||||
}
|
||||
|
||||
fn write_kotlin_mapper_bindings(
|
||||
&self,
|
||||
ci: &ComponentInterface,
|
||||
config: RNConfig,
|
||||
base_output_path: &Utf8Path,
|
||||
) -> Result<()> {
|
||||
// Create the path
|
||||
let output_path = base_output_path.join(Utf8Path::new("android/src/main/java/com/lssdk"));
|
||||
// Generate and write the binding to file
|
||||
let bindings_output = self::gen_kotlin::MapperGenerator::new(config.clone(), ci)
|
||||
.render()
|
||||
.map_err(anyhow::Error::new)?;
|
||||
let bindings_file = self
|
||||
.write_bindings(
|
||||
&bindings_output,
|
||||
&output_path,
|
||||
Utf8Path::new("LiquidSwapSDKMapper.kt"),
|
||||
)
|
||||
.unwrap();
|
||||
// Lint binding
|
||||
self.lint_kotlin_bindings(&bindings_file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_kotlin_module_bindings(
|
||||
&self,
|
||||
ci: &ComponentInterface,
|
||||
config: RNConfig,
|
||||
base_output_path: &Utf8Path,
|
||||
) -> Result<()> {
|
||||
// Create the path
|
||||
let output_path = base_output_path.join(Utf8Path::new("android/src/main/java/com/lssdk"));
|
||||
// Generate and write the binding to file
|
||||
let bindings_output = self::gen_kotlin::ModuleGenerator::new(config.clone(), ci)
|
||||
.render()
|
||||
.map_err(anyhow::Error::new)?;
|
||||
let bindings_file = self
|
||||
.write_bindings(
|
||||
&bindings_output,
|
||||
&output_path,
|
||||
Utf8Path::new("LiquidSwapSDKModule.kt"),
|
||||
)
|
||||
.unwrap();
|
||||
// Lint binding
|
||||
self.lint_kotlin_bindings(&bindings_file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lint_kotlin_bindings(&self, bindings_file: &Utf8PathBuf) {
|
||||
if let Err(e) = Command::new("ktlint").arg("-F").arg(bindings_file).output() {
|
||||
println!(
|
||||
"Warning: Unable to auto-format {} using ktlint: {:?}",
|
||||
bindings_file.file_name().unwrap(),
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_swift_mapper_bindings(
|
||||
&self,
|
||||
ci: &ComponentInterface,
|
||||
config: RNConfig,
|
||||
base_output_path: &Utf8Path,
|
||||
) -> Result<()> {
|
||||
// Create the path
|
||||
let output_path = base_output_path.join(Utf8Path::new("ios"));
|
||||
// Generate and write the binding to file
|
||||
let bindings_output = self::gen_swift::MapperGenerator::new(config.clone(), ci)
|
||||
.render()
|
||||
.map_err(anyhow::Error::new)?;
|
||||
let bindings_file = self
|
||||
.write_bindings(
|
||||
&bindings_output,
|
||||
&output_path,
|
||||
Utf8Path::new("LiquidSwapSDKMapper.swift"),
|
||||
)
|
||||
.unwrap();
|
||||
// Lint binding
|
||||
self.lint_swift_bindings(&bindings_file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_swift_extern_bindings(
|
||||
&self,
|
||||
ci: &ComponentInterface,
|
||||
config: RNConfig,
|
||||
base_output_path: &Utf8Path,
|
||||
) -> Result<()> {
|
||||
// Create the path
|
||||
let output_path = base_output_path.join(Utf8Path::new("ios"));
|
||||
// Generate and write the binding to file
|
||||
let bindings_output = self::gen_swift::ExternGenerator::new(config.clone(), ci)
|
||||
.render()
|
||||
.map_err(anyhow::Error::new)?;
|
||||
let bindings_file = self
|
||||
.write_bindings(
|
||||
&bindings_output,
|
||||
&output_path,
|
||||
Utf8Path::new("RNLiquidSwapSDK.m"),
|
||||
)
|
||||
.unwrap();
|
||||
// Lint binding
|
||||
self.lint_swift_bindings(&bindings_file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_swift_module_bindings(
|
||||
&self,
|
||||
ci: &ComponentInterface,
|
||||
config: RNConfig,
|
||||
base_output_path: &Utf8Path,
|
||||
) -> Result<()> {
|
||||
// Create the path
|
||||
let output_path = base_output_path.join(Utf8Path::new("ios"));
|
||||
// Generate and write the binding to file
|
||||
let bindings_output = self::gen_swift::ModuleGenerator::new(config.clone(), ci)
|
||||
.render()
|
||||
.map_err(anyhow::Error::new)?;
|
||||
let bindings_file = self
|
||||
.write_bindings(
|
||||
&bindings_output,
|
||||
&output_path,
|
||||
Utf8Path::new("RNLiquidSwapSDK.swift"),
|
||||
)
|
||||
.unwrap();
|
||||
// Lint binding
|
||||
self.lint_swift_bindings(&bindings_file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lint_swift_bindings(&self, bindings_file: &Utf8PathBuf) {
|
||||
if let Err(e) = Command::new("swiftformat")
|
||||
.arg(bindings_file.as_str())
|
||||
.output()
|
||||
{
|
||||
println!(
|
||||
"Warning: Unable to auto-format {} using swiftformat: {:?}",
|
||||
bindings_file.file_name().unwrap(),
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_typescript_bindings(
|
||||
&self,
|
||||
ci: &ComponentInterface,
|
||||
config: RNConfig,
|
||||
base_output_path: &Utf8Path,
|
||||
) -> Result<()> {
|
||||
// Create the path
|
||||
let output_path = base_output_path.join(Utf8Path::new("ts/src"));
|
||||
// Generate and write the binding to file
|
||||
let bindings_output = self::gen_typescript::ModuleGenerator::new(config.clone(), ci)
|
||||
.render()
|
||||
.map_err(anyhow::Error::new)?;
|
||||
let bindings_file = self
|
||||
.write_bindings(&bindings_output, &output_path, Utf8Path::new("index.ts"))
|
||||
.unwrap();
|
||||
// Lint binding
|
||||
self.lint_typescript_bindings(&bindings_file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lint_typescript_bindings(&self, bindings_file: &Utf8PathBuf) {
|
||||
if let Err(e) = Command::new("tslint")
|
||||
.arg("--fix")
|
||||
.arg(bindings_file)
|
||||
.output()
|
||||
{
|
||||
println!(
|
||||
"Warning: Unable to auto-format {} using tslint: {:?}",
|
||||
bindings_file.file_name().unwrap(),
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct RNConfig {
|
||||
package_name: Option<String>,
|
||||
}
|
||||
|
||||
impl RNConfig {}
|
||||
|
||||
impl BindingGeneratorConfig for RNConfig {
|
||||
fn get_entry_from_bindings_table(_bindings: &toml::value::Value) -> Option<toml::value::Value> {
|
||||
if let Some(table) = _bindings.as_table() {
|
||||
table.get("rn").cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_config_defaults(ci: &ComponentInterface) -> Vec<(String, toml::value::Value)> {
|
||||
vec![
|
||||
(
|
||||
"package_name".to_string(),
|
||||
toml::value::Value::String(ci.namespace().to_string()),
|
||||
),
|
||||
(
|
||||
"cdylib_name".to_string(),
|
||||
toml::value::Value::String(ci.namespace().to_string()),
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl BindingGenerator for RNBindingGenerator {
|
||||
type Config = RNConfig;
|
||||
|
||||
fn write_bindings(
|
||||
&self,
|
||||
ci: ComponentInterface,
|
||||
config: Self::Config,
|
||||
out_dir: &Utf8Path,
|
||||
) -> Result<()> {
|
||||
fs::create_dir_all(out_dir)?;
|
||||
|
||||
// generate kotlin
|
||||
self.write_kotlin_mapper_bindings(&ci, config.clone(), out_dir)?;
|
||||
self.write_kotlin_module_bindings(&ci, config.clone(), out_dir)?;
|
||||
|
||||
// generate ios
|
||||
self.write_swift_mapper_bindings(&ci, config.clone(), out_dir)?;
|
||||
self.write_swift_extern_bindings(&ci, config.clone(), out_dir)?;
|
||||
self.write_swift_module_bindings(&ci, config.clone(), out_dir)?;
|
||||
|
||||
// generate typescript
|
||||
self.write_typescript_bindings(&ci, config.clone(), out_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
34
lib/ls-sdk-bindings/bindings-react-native/src/main.rs
Normal file
34
lib/ls-sdk-bindings/bindings-react-native/src/main.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
mod gen_kotlin;
|
||||
mod gen_swift;
|
||||
mod gen_typescript;
|
||||
mod generator;
|
||||
use camino::Utf8Path;
|
||||
use clap::Parser;
|
||||
use generator::RNBindingGenerator;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub(crate) struct Cli {
|
||||
#[clap(name = "binding_dir", short = 'b', long = "binding_dir")]
|
||||
pub(crate) binding_dir: Option<String>,
|
||||
#[clap(name = "out_dir", short = 'o', long = "out_dir")]
|
||||
pub(crate) out_dir: Option<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
let cli_binding_dir = cli.binding_dir.unwrap_or("../".into());
|
||||
let cli_out_dir = cli.out_dir.unwrap_or("./".into());
|
||||
let binding_dir = Utf8Path::new(cli_binding_dir.as_str());
|
||||
let udl_file = binding_dir.join(Utf8Path::new("src/ls_sdk.udl"));
|
||||
let config = binding_dir.join(Utf8Path::new("uniffi.toml"));
|
||||
let out_dir = Utf8Path::new(cli_out_dir.as_str());
|
||||
|
||||
// React Native generator
|
||||
uniffi_bindgen::generate_external_bindings(
|
||||
RNBindingGenerator {},
|
||||
udl_file,
|
||||
Some(config),
|
||||
Some(out_dir),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
97
lib/ls-sdk-bindings/bindings-react-native/tslint.json
Normal file
97
lib/ls-sdk-bindings/bindings-react-native/tslint.json
Normal file
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"callable-types": true,
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space"
|
||||
],
|
||||
"curly": true,
|
||||
"eofline": true,
|
||||
"forin": true,
|
||||
"import-blacklist": [
|
||||
true
|
||||
],
|
||||
"import-spacing": true,
|
||||
"indent": [
|
||||
true,
|
||||
"spaces"
|
||||
],
|
||||
"interface-over-type-literal": true,
|
||||
"label-position": true,
|
||||
"member-access": false,
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
"order": "fields-first"
|
||||
}
|
||||
],
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-construct": true,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-empty": false,
|
||||
"no-empty-interface": true,
|
||||
"no-eval": true,
|
||||
"no-inferrable-types": [
|
||||
true,
|
||||
"ignore-params"
|
||||
],
|
||||
"no-shadowed-variable": false,
|
||||
"no-string-literal": false,
|
||||
"no-string-throw": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unused-expression": true,
|
||||
"no-var-keyword": true,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-catch",
|
||||
"check-else",
|
||||
"check-whitespace"
|
||||
],
|
||||
"prefer-const": true,
|
||||
"quotemark": [
|
||||
true,
|
||||
"double"
|
||||
],
|
||||
"radix": true,
|
||||
"semicolon": false,
|
||||
"triple-equals": [
|
||||
true,
|
||||
"allow-null-check"
|
||||
],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
}
|
||||
],
|
||||
"unified-signatures": true,
|
||||
"variable-name": false,
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -111,3 +111,7 @@ bindings-swift: ios-universal darwin-universal
|
||||
python: $(SOURCES)
|
||||
cargo build --release --target $(TARGET)
|
||||
cargo run --features=uniffi/cli --bin uniffi-bindgen generate src/ls_sdk.udl --no-format --language python -o ffi/python
|
||||
|
||||
## React Native
|
||||
react-native:
|
||||
make -C bindings-react-native codegen
|
||||
Reference in New Issue
Block a user