Files
shorebird-updater/library/src/c_api.rs
T
2023-03-31 12:44:12 -05:00

147 lines
4.7 KiB
Rust

// This file handles translating the updater library's types into C types.
// Currently manually prefixing all functions with "shorebird_" to avoid
// name collisions with other libraries.
// cbindgen:prefix-with-name could do this for us.
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use crate::updater;
/// Struct containing configuration parameters for the updater.
/// Passed to all updater functions.
/// NOTE: If this struct is changed all language bindings must be updated.
#[repr(C)]
pub struct AppParameters {
/// release_version, required. Named version of the app, off of which updates
/// are based. Can be either a version number or a hash.
pub release_version: *const libc::c_char,
/// Array of paths to the original aot library, required. For Flutter apps
/// these are the paths to the bundled libapp.so. May be used for compression downloaded artifacts.
pub original_libapp_paths: *const *const libc::c_char,
/// Length of the original_libapp_paths array.
pub original_libapp_paths_size: libc::c_int,
/// Path to the app's libflutter.so, required. May be used for ensuring
/// downloaded artifacts are compatible with the Flutter/Dart versions
/// used by the app. For Flutter apps this should be the path to the
/// bundled libflutter.so. For Dart apps this should be the path to the
/// dart executable.
pub vm_path: *const libc::c_char,
/// Path to cache_dir where the updater will store downloaded artifacts.
pub cache_dir: *const libc::c_char,
}
fn to_rust(c_string: *const libc::c_char) -> String {
unsafe { CStr::from_ptr(c_string).to_str().unwrap() }.to_string()
}
fn to_rust_vector(c_array: *const *const libc::c_char, size: libc::c_int) -> Vec<String> {
let mut result = Vec::new();
for i in 0..size {
let c_string = unsafe { *c_array.offset(i as isize) };
result.push(to_rust(c_string));
}
result
}
fn app_config_from_c(c_params: *const AppParameters) -> updater::AppConfig {
let c_params_ref = unsafe { &*c_params };
updater::AppConfig {
cache_dir: to_rust(c_params_ref.cache_dir),
release_version: to_rust(c_params_ref.release_version),
original_libapp_paths: to_rust_vector(
c_params_ref.original_libapp_paths,
c_params_ref.original_libapp_paths_size,
),
vm_path: to_rust(c_params_ref.vm_path),
}
}
/// Configures updater. First parameter is a struct containing configuration
/// from the running app. Second parameter is a YAML string containing
/// configuration compiled into the app.
#[no_mangle]
pub extern "C" fn shorebird_init(c_params: *const AppParameters, c_yaml: *const libc::c_char) {
let config = app_config_from_c(c_params);
let yaml_string = to_rust(c_yaml);
let result = updater::init(config, &yaml_string);
match result {
Ok(_) => {}
Err(e) => {
error!("Error initializing updater: {:?}", e);
}
}
}
/// Return the active patch number, or NULL if there is no active patch.
#[no_mangle]
pub extern "C" fn shorebird_active_patch_number() -> *mut c_char {
let patch = updater::active_patch();
match patch {
Some(v) => {
let c_patch = CString::new(v.number.to_string()).unwrap();
c_patch.into_raw()
}
None => std::ptr::null_mut(),
}
}
/// Return the path to the active patch for the app, or NULL if there is no
/// active patch.
#[no_mangle]
// rename to shorebird_patch_path
pub extern "C" fn shorebird_active_path() -> *mut c_char {
let version = updater::active_patch();
match version {
Some(v) => {
let c_version = CString::new(v.path).unwrap();
c_version.into_raw()
}
None => std::ptr::null_mut(),
}
}
/// Free a string returned by the updater library.
#[no_mangle]
pub extern "C" fn shorebird_free_string(c_string: *mut c_char) {
unsafe {
if c_string.is_null() {
return;
}
drop(CString::from_raw(c_string));
}
}
/// Check for an update. Returns true if an update is available.
#[no_mangle]
pub extern "C" fn shorebird_check_for_update() -> bool {
return updater::check_for_update();
}
/// Synchronously download an update if one is available.
#[no_mangle]
pub extern "C" fn shorebird_update() {
updater::update();
}
/// Report that the app failed to launch. This will cause the updater to
/// attempt to roll back to the previous version if this version has not
/// been launched successfully before.
#[no_mangle]
pub extern "C" fn shorebird_report_failed_launch() {
let result = updater::report_failed_launch();
match result {
Ok(_) => {}
Err(e) => {
error!("Error recording launch failure: {:?}", e);
}
}
}