use caption_strategy::ArgumentCaptionStrategy;
use utils::JoinWithString;
use cpp_type::CppTypeBase;
use cpp_type::CppType;
use cpp_method::{CppMethod, ReturnValueAllocationPlace};
use caption_strategy::{MethodCaptionStrategy, TypeCaptionStrategy};
use cpp_operator::CppOperator;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum CppFfiArgumentMeaning {
This,
Argument(i8),
ReturnValue,
}
impl CppFfiArgumentMeaning {
pub fn is_argument(&self) -> bool {
match self {
&CppFfiArgumentMeaning::Argument(..) => true,
_ => false,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CppFfiFunctionArgument {
pub name: String,
pub argument_type: CppFfiType,
pub meaning: CppFfiArgumentMeaning,
}
impl CppFfiFunctionArgument {
pub fn caption(&self, strategy: ArgumentCaptionStrategy) -> String {
match strategy {
ArgumentCaptionStrategy::NameOnly => self.name.clone(),
ArgumentCaptionStrategy::TypeOnly(type_strategy) => {
self.argument_type.original_type.caption(type_strategy)
}
ArgumentCaptionStrategy::TypeAndName(type_strategy) => {
format!("{}_{}",
self.argument_type.original_type.caption(type_strategy),
self.name)
}
}
}
pub fn to_cpp_code(&self) -> Result<String, String> {
match self.argument_type.ffi_type.base {
CppTypeBase::FunctionPointer { .. } => {
Ok(try!(self.argument_type.ffi_type.to_cpp_code(Some(&self.name))))
}
_ => {
Ok(format!("{} {}",
try!(self.argument_type.ffi_type.to_cpp_code(None)),
self.name))
}
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CppFfiFunctionSignature {
pub arguments: Vec<CppFfiFunctionArgument>,
pub return_type: CppFfiType,
}
impl CppFfiFunctionSignature {
pub fn has_const_this(&self) -> bool {
self.arguments
.iter()
.find(|arg| arg.meaning == CppFfiArgumentMeaning::This && arg.argument_type.ffi_type.is_const)
.is_some()
}
pub fn arguments_caption(&self, strategy: ArgumentCaptionStrategy) -> String {
let r = self.arguments
.iter()
.filter(|x| x.meaning.is_argument())
.map(|x| x.caption(strategy.clone()))
.join("_");
if r.len() == 0 {
"no_args".to_string()
} else {
r
}
}
pub fn caption(&self, strategy: MethodCaptionStrategy) -> String {
match strategy {
MethodCaptionStrategy::ArgumentsOnly(s) => self.arguments_caption(s),
MethodCaptionStrategy::ConstOnly => {
if self.has_const_this() {
"const".to_string()
} else {
"".to_string()
}
}
MethodCaptionStrategy::ConstAndArguments(s) => {
let r = if self.has_const_this() {
"const_".to_string()
} else {
"".to_string()
};
r + &self.arguments_caption(s)
}
}
}
pub fn arguments_to_cpp_code(&self) -> Result<String, String> {
let mut code = Vec::new();
for arg in &self.arguments {
match arg.to_cpp_code() {
Ok(c) => code.push(c),
Err(msg) => return Err(msg),
}
}
Ok(code.join(", "))
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum IndirectionChange {
NoChange,
ValueToPointer,
ReferenceToPointer,
QFlagsToUInt,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CppFfiType {
pub original_type: CppType,
pub ffi_type: CppType,
pub conversion: IndirectionChange,
}
impl CppFfiType {
pub fn void() -> Self {
CppFfiType {
original_type: CppType::void(),
ffi_type: CppType::void(),
conversion: IndirectionChange::NoChange,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CppMethodWithFfiSignature {
pub cpp_method: CppMethod,
pub allocation_place: ReturnValueAllocationPlace,
pub c_signature: CppFfiFunctionSignature,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CppAndFfiMethod {
pub cpp_method: CppMethod,
pub allocation_place: ReturnValueAllocationPlace,
pub c_signature: CppFfiFunctionSignature,
pub c_name: String,
}
pub fn c_base_name(cpp_method: &CppMethod,
allocation_place: &ReturnValueAllocationPlace,
include_file: &String)
-> Result<String, String> {
let scope_prefix = match cpp_method.class_membership {
Some(ref info) => format!("{}_", info.class_type.caption()),
None => format!("{}_G_", include_file),
};
let add_place_note = |name| {
match *allocation_place {
ReturnValueAllocationPlace::Stack => format!("{}_to_output", name),
ReturnValueAllocationPlace::Heap => format!("{}_as_ptr", name),
ReturnValueAllocationPlace::NotApplicable => name,
}
};
let method_name = if cpp_method.is_constructor() {
match *allocation_place {
ReturnValueAllocationPlace::Stack => "constructor".to_string(),
ReturnValueAllocationPlace::Heap => "new".to_string(),
ReturnValueAllocationPlace::NotApplicable => {
return Err(format!("NotApplicable is not allowed for constructor"))
}
}
} else if cpp_method.is_destructor() {
match *allocation_place {
ReturnValueAllocationPlace::Stack => "destructor".to_string(),
ReturnValueAllocationPlace::Heap => "delete".to_string(),
ReturnValueAllocationPlace::NotApplicable => {
return Err(format!("NotApplicable is not allowed for constructor"))
}
}
} else if let Some(ref operator) = cpp_method.operator {
add_place_note(match *operator {
CppOperator::Conversion(ref cpp_type) => {
format!("convert_to_{}", cpp_type.caption(TypeCaptionStrategy::Full))
}
_ => format!("operator_{}", operator.c_name()),
})
} else {
add_place_note(cpp_method.name.replace("::", "_"))
};
Ok(scope_prefix + &method_name)
}
impl CppAndFfiMethod {
pub fn new(data: CppMethodWithFfiSignature, c_name: String) -> CppAndFfiMethod {
CppAndFfiMethod {
cpp_method: data.cpp_method,
allocation_place: data.allocation_place,
c_signature: data.c_signature,
c_name: c_name,
}
}
pub fn short_text(&self) -> String {
self.cpp_method.short_text()
}
}