use runmat_builtins::{
BuiltinCompletionPolicy, BuiltinDescriptor, BuiltinErrorDescriptor, BuiltinOutputMode,
BuiltinParamArity, BuiltinParamDescriptor, BuiltinParamType, BuiltinSignatureDescriptor, Value,
};
use runmat_macros::runtime_builtin;
const CALL_METHOD_OUTPUT: [BuiltinParamDescriptor; 1] = [BuiltinParamDescriptor {
name: "out",
ty: BuiltinParamType::Any,
arity: BuiltinParamArity::Variadic,
default: None,
description: "Method return value(s).",
}];
const CALL_METHOD_INPUTS: [BuiltinParamDescriptor; 3] = [
BuiltinParamDescriptor {
name: "base",
ty: BuiltinParamType::Any,
arity: BuiltinParamArity::Required,
default: None,
description: "Object receiver.",
},
BuiltinParamDescriptor {
name: "method",
ty: BuiltinParamType::StringScalar,
arity: BuiltinParamArity::Required,
default: None,
description: "Method name.",
},
BuiltinParamDescriptor {
name: "varargin",
ty: BuiltinParamType::Any,
arity: BuiltinParamArity::Variadic,
default: None,
description: "Method arguments.",
},
];
const CALL_METHOD_SIGNATURES: [BuiltinSignatureDescriptor; 1] = [BuiltinSignatureDescriptor {
label: "[out] = call_method(base, method, varargin)",
inputs: &CALL_METHOD_INPUTS,
outputs: &CALL_METHOD_OUTPUT,
}];
const CALL_METHOD_ERRORS: [BuiltinErrorDescriptor; 2] = [
BuiltinErrorDescriptor {
code: "RM.CALL_METHOD.NAME_INVALID",
identifier: Some("RunMat:CallMethodNameInvalid"),
when: "The method name is empty or missing.",
message: "call_method: method name must not be empty",
},
BuiltinErrorDescriptor {
code: "RM.CALL_METHOD.RECEIVER_INVALID",
identifier: Some("RunMat:InvalidObjectDispatch"),
when: "Receiver is not an object or handle object.",
message: "call_method: requires object receiver",
},
];
pub const CALL_METHOD_DESCRIPTOR: BuiltinDescriptor = BuiltinDescriptor {
signatures: &CALL_METHOD_SIGNATURES,
output_mode: BuiltinOutputMode::ByRequestedOutputCount,
completion_policy: BuiltinCompletionPolicy::HiddenInternal,
errors: &CALL_METHOD_ERRORS,
};
pub(crate) const CALL_METHOD_ERROR_NAME_INVALID: BuiltinErrorDescriptor = BuiltinErrorDescriptor {
code: "RM.CALL_METHOD.NAME_INVALID",
identifier: Some("RunMat:CallMethodNameInvalid"),
when: "The method name is empty or missing.",
message: "call_method: method name must not be empty",
};
pub(crate) const CALL_METHOD_ERROR_RECEIVER_INVALID: BuiltinErrorDescriptor =
BuiltinErrorDescriptor {
code: "RM.CALL_METHOD.RECEIVER_INVALID",
identifier: Some("RunMat:InvalidObjectDispatch"),
when: "Receiver is not an object or handle object.",
message: "call_method: requires object receiver",
};
pub(crate) async fn dispatch_call_method(
base: Value,
method: String,
rest: Vec<Value>,
) -> crate::BuiltinResult<Value> {
let method = method.trim().to_string();
if method.is_empty() {
return Err(crate::runtime_descriptor_error(
"call_method",
&CALL_METHOD_ERROR_NAME_INVALID,
));
}
match base {
receiver @ Value::Object(_) | receiver @ Value::HandleObject(_) => {
let class_name = crate::object_receiver_class_name(&receiver).ok_or_else(|| {
crate::runtime_descriptor_error("call_method", &CALL_METHOD_ERROR_RECEIVER_INVALID)
})?;
let mut args = Vec::with_capacity(1 + rest.len());
args.push(receiver.clone());
args.extend(rest);
let requested_outputs = crate::current_requested_outputs();
match crate::dispatch_object_external_member(
class_name,
&method,
args.clone(),
requested_outputs,
)
.await
{
Ok(v) => return Ok(v),
Err(err) if crate::is_undefined_function_error(&err) => {}
Err(err) => return Err(err),
}
let (identity, fallback_policy) = crate::callable_identity_for_handle_name(&method);
crate::dispatch_callable_with_policy(identity, fallback_policy, args, requested_outputs)
.await
}
other => Err(crate::runtime_descriptor_error_with_detail(
"call_method",
&CALL_METHOD_ERROR_RECEIVER_INVALID,
format!("unsupported receiver {other:?} for method '{method}'"),
)),
}
}
#[runtime_builtin(
name = "call_method",
category = "introspection",
summary = "Dispatch object member calls with MATLAB method semantics.",
keywords = "classdef,method,dispatch,object",
descriptor(crate::builtins::introspection::call_method::CALL_METHOD_DESCRIPTOR),
builtin_path = "crate::builtins::introspection::call_method"
)]
pub async fn call_method_builtin(
base: Value,
method: String,
rest: Vec<Value>,
) -> crate::BuiltinResult<Value> {
dispatch_call_method(base, method, rest).await
}