treasury_import/
dependencies.rs

1use treasury_id::AssetId;
2
3use crate::{Dependency, NOT_FOUND, NOT_UTF8, SUCCESS};
4
5pub trait Dependencies {
6    /// Returns dependency id.
7    fn get(&self, source: &str, target: &str) -> Result<Option<AssetId>, String>;
8
9    fn get_or_append(
10        &self,
11        source: &str,
12        target: &str,
13        missing: &mut Vec<Dependency>,
14    ) -> Result<Option<AssetId>, String> {
15        match self.get(source, target) {
16            Err(err) => Err(err),
17            Ok(Some(id)) => Ok(Some(id)),
18            Ok(None) => {
19                missing.push(Dependency {
20                    source: source.to_owned(),
21                    target: target.to_owned(),
22                });
23                Ok(None)
24            }
25        }
26    }
27}
28
29#[repr(transparent)]
30pub struct DependenciesOpaque(u8);
31
32pub type DependenciesGetFn = unsafe extern "C" fn(
33    dependencies: *mut DependenciesOpaque,
34    source_ptr: *const u8,
35    source_len: u32,
36    target_ptr: *const u8,
37    target_len: u32,
38    id_ptr: *mut u64,
39) -> i32;
40
41unsafe extern "C" fn dependencies_get_ffi<F>(
42    dependencies: *mut DependenciesOpaque,
43    source_ptr: *const u8,
44    source_len: u32,
45    target_ptr: *const u8,
46    target_len: u32,
47    id_ptr: *mut u64,
48) -> i32
49where
50    F: FnMut(&str, &str) -> Option<AssetId>,
51{
52    let source =
53        match std::str::from_utf8(std::slice::from_raw_parts(source_ptr, source_len as usize)) {
54            Ok(source) => source,
55            Err(_) => return NOT_UTF8,
56        };
57    let target =
58        match std::str::from_utf8(std::slice::from_raw_parts(target_ptr, target_len as usize)) {
59            Ok(target) => target,
60            Err(_) => return NOT_UTF8,
61        };
62
63    let f = dependencies as *mut F;
64    let f = &mut *f;
65
66    match f(source, target) {
67        None => return NOT_FOUND,
68        Some(id) => {
69            std::ptr::write(id_ptr, id.value().get());
70            return SUCCESS;
71        }
72    }
73}
74
75pub struct DependenciesFFI {
76    pub opaque: *mut DependenciesOpaque,
77    pub get: DependenciesGetFn,
78}
79
80impl DependenciesFFI {
81    pub fn new<F>(f: &mut F) -> Self
82    where
83        F: FnMut(&str, &str) -> Option<AssetId>,
84    {
85        DependenciesFFI {
86            opaque: f as *mut F as _,
87            get: dependencies_get_ffi::<F>,
88        }
89    }
90}
91
92impl Dependencies for DependenciesFFI {
93    fn get(&self, source: &str, target: &str) -> Result<Option<AssetId>, String> {
94        let mut id = 0u64;
95        let result = unsafe {
96            (self.get)(
97                self.opaque,
98                source.as_ptr(),
99                source.len() as u32,
100                target.as_ptr(),
101                target.len() as u32,
102                &mut id,
103            )
104        };
105
106        match result {
107            SUCCESS => match AssetId::new(id) {
108                None => Err(format!("Null AssetId returned from `Dependencies::get`")),
109                Some(id) => Ok(Some(id)),
110            },
111            NOT_FOUND => Ok(None),
112            NOT_UTF8 => Err(format!("Source is not UTF8 while stored in `str`")),
113
114            _ => Err(format!(
115                "Unexpected return code from `Sources::get` FFI: {}",
116                result
117            )),
118        }
119    }
120}