Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Vec, HashMap and more types #61

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions wolfram-library-link/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@

use std::{
cell::RefCell,
collections::HashMap,
ffi::{CStr, CString},
os::raw::c_char,
};

use ref_cast::RefCast;

use crate::{
data_store::{DataStoreAdd, DataStoreNodeValue},
expr::{Expr, Symbol},
rtl,
sys::{self, mint, mreal, MArgument},
Expand Down Expand Up @@ -254,6 +256,16 @@ impl<'a> FromArg<'a> for &'a str {
}
}

impl FromArg<'_> for char {
unsafe fn from_arg(arg: &MArgument) -> Self {
String::from_arg(arg).chars().next().unwrap()
}
Copy link
Collaborator

@ConnorGray ConnorGray Nov 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @sw1sh, as discussed in our meeting, the basic implementation here is exactly right, but we may want to improve the error reporting to help users out in cases where they accidentally pass in strings that are not a single char in length.

My proposal would be something like:

unsafe fn from_arg(arg: &MArgument) -> Self {
    let string = String::from_arg(arg);

    let mut chars = string.chars();

    let Some(char) = chars.next() else {
        panic!("char argument string was empty")
    };

    if chars.next().is_some() {
        panic!("char argument string contains multiple chars: {string}")
    }

    char
}


fn parameter_type() -> Expr {
String::parameter_type()
}
}

//--------------------------------------
// NumericArray
//--------------------------------------
Expand Down Expand Up @@ -456,6 +468,61 @@ impl<'a> FromArg<'a> for &'a DataStore {
}
}

impl<T: for<'a> From<DataStoreNodeValue<'a>>> FromArg<'_> for Vec<T> {
unsafe fn from_arg(arg: &MArgument) -> Vec<T> {
let mut vec = Vec::new();
DataStore::from_arg(arg)
.nodes()
.for_each(|node| vec.push(T::from(node.value())));
vec
}

fn parameter_type() -> Expr {
Expr::string("DataStore")
}
}

impl<
K: for<'a> From<DataStoreNodeValue<'a>> + Eq + std::hash::Hash,
V: for<'a> From<DataStoreNodeValue<'a>>,
S: Default + std::hash::BuildHasher,
> FromArg<'_> for HashMap<K, V, S>
{
unsafe fn from_arg(arg: &MArgument) -> HashMap<K, V, S> {
let mut dict = HashMap::default();
DataStore::from_arg(arg)
.nodes()
.for_each(|node| match node.value() {
DataStoreNodeValue::DataStore(rule) => {
let mut nodes = rule.nodes();
let k = K::from(nodes.next().unwrap().value());
let v = V::from(nodes.next().unwrap().value());
dict.insert(k, v);
()
},
_ => panic!("Expected DataStore of DataStores"),
});
dict
}

fn parameter_type() -> Expr {
Expr::string("DataStore")
}
}

impl<T: for<'a> From<DataStoreNodeValue<'a>>> FromArg<'_> for Option<T> {
unsafe fn from_arg(arg: &MArgument) -> Option<T> {
match DataStore::from_arg(arg).nodes().next() {
None => None,
Some(v) => Some(T::from(v.value())),
}
}

fn parameter_type() -> Expr {
Expr::string("DataStore")
}
}

//======================================
// impl IntoArg
//======================================
Expand Down Expand Up @@ -644,6 +711,16 @@ impl IntoArg for String {
}
}

impl IntoArg for char {
unsafe fn into_arg(self, arg: MArgument) {
String::from(self).into_arg(arg)
}

fn return_type() -> Expr {
String::return_type()
}
}

//---------------------------------------
// NumericArray, Image, DataStore
//---------------------------------------
Expand Down Expand Up @@ -703,6 +780,19 @@ impl IntoArg for DataStore {
}
}


impl<T: DataStoreAdd> IntoArg for Vec<T> {
unsafe fn into_arg(self, arg: MArgument) {
let mut store = DataStore::new();
self.iter().for_each(|x| x.add_to_datastore(&mut store));
store.into_arg(arg)
}

fn return_type() -> Expr {
Expr::string("DataStore")
}
}

//======================================
// impl NativeFunction
//======================================
Expand Down
158 changes: 158 additions & 0 deletions wolfram-library-link/src/data_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,164 @@ impl<'store> DataStoreNode<'store> {
}
}

//---------------
// DataStoreAdd trait
//---------------

pub trait DataStoreAdd {
fn add_to_datastore(&self, ds: &mut DataStore);
}

impl DataStoreAdd for bool {
fn add_to_datastore(&self, ds: &mut DataStore) {
ds.add_bool(*self)
}
}

impl DataStoreAdd for &str {
fn add_to_datastore(&self, ds: &mut DataStore) {
ds.add_str(*self)
}
}

impl DataStoreAdd for char {
fn add_to_datastore(&self, ds: &mut DataStore) {
String::from(*self).as_str().add_to_datastore(ds)
}
}

impl DataStoreAdd for u16 {
fn add_to_datastore(&self, ds: &mut DataStore) {
ds.add_i64(*self as i64)
}
}

impl DataStoreAdd for DataStore {
fn add_to_datastore(&self, ds: &mut DataStore) {
ds.add_data_store(self.clone())
}
}

impl<T: DataStoreAdd> DataStoreAdd for Vec<T> {
fn add_to_datastore(&self, ds: &mut DataStore) {
let mut inner = DataStore::new();
self.into_iter().for_each(|item| item.add_to_datastore(&mut inner));
ds.add_data_store(inner)
}
}

//---------------
// From DataStoreNodeValue
//---------------

impl From<DataStoreNodeValue<'_>> for bool {
fn from(value: DataStoreNodeValue) -> bool {
match value {
DataStoreNodeValue::Boolean(val) => val,
_ => panic!("expected DataStoreNodeValue::Boolean"),
}
}
}

impl From<DataStoreNodeValue<'_>> for mint {
fn from(value: DataStoreNodeValue) -> mint {
match value {
DataStoreNodeValue::Integer(val) => val,
_ => panic!("expected DataStoreNodeValue::Integer"),
}
}
}

impl From<DataStoreNodeValue<'_>> for mreal {
fn from(value: DataStoreNodeValue) -> mreal {
match value {
DataStoreNodeValue::Real(val) => val,
_ => panic!("expected DataStoreNodeValue::Real"),
}
}
}

impl From<DataStoreNodeValue<'_>> for f32 {
fn from(value: DataStoreNodeValue) -> f32 {
match value {
DataStoreNodeValue::Real(val) => val as f32,
_ => panic!("expected DataStoreNodeValue::Real"),
}
}
}

impl From<DataStoreNodeValue<'_>> for mcomplex {
fn from(value: DataStoreNodeValue) -> mcomplex {
match value {
DataStoreNodeValue::Complex(val) => val,
_ => panic!("expected DataStoreNodeValue::Complex"),
}
}
}

impl<'node> From<DataStoreNodeValue<'node>> for &'node str {
fn from(value: DataStoreNodeValue<'node>) -> &'node str {
match value {
DataStoreNodeValue::Str(val) => val,
_ => panic!("expected DataStoreNodeValue::Str"),
}
}
}

impl<'node> From<DataStoreNodeValue<'node>> for String {
fn from(value: DataStoreNodeValue<'node>) -> String {
match value {
DataStoreNodeValue::Str(val) => String::from(val),
_ => panic!("expected DataStoreNodeValue::Str"),
}
}
}

impl From<DataStoreNodeValue<'_>> for char {
fn from(value: DataStoreNodeValue) -> char {
match value {
DataStoreNodeValue::Str(val) => String::from(val).chars().next().unwrap(),
_ => panic!("expected DataStoreNodeValue::Str"),
}
}
}

impl<'node> From<DataStoreNodeValue<'node>> for &'node NumericArray {
fn from(value: DataStoreNodeValue<'node>) -> &'node NumericArray {
match value {
DataStoreNodeValue::NumericArray(val) => val,
_ => panic!("expected DataStoreNodeValue::NumericArray"),
}
}
}

impl<'node> From<DataStoreNodeValue<'node>> for &'node Image {
fn from(value: DataStoreNodeValue<'node>) -> &'node Image {
match value {
DataStoreNodeValue::Image(val) => val,
_ => panic!("expected DataStoreNodeValue::Image"),
}
}
}

impl<'node> From<DataStoreNodeValue<'node>> for &'node DataStore {
fn from(value: DataStoreNodeValue<'node>) -> &'node DataStore {
match value {
DataStoreNodeValue::DataStore(val) => val,
_ => panic!("expected DataStoreNodeValue::DataStore"),
}
}
}

impl<'node, T: for<'a> From<DataStoreNodeValue<'a>>> From<DataStoreNodeValue<'node>> for Vec<T> {
fn from(value: DataStoreNodeValue<'node>) -> Vec<T> {
match value {
DataStoreNodeValue::DataStore(val) => val.nodes().map(|n| n.value().into()).collect(),
_ => panic!("expected DataStoreNodeValue::DataStore"),
}
}
}

//---------------
// Nodes iterator
//---------------
Expand Down