use log::error;
use super::{conversion_type::ConversionType, ref_mode::RefMode, try_from_glib::TryFromGlib};
pub use crate::config::signals::TransformationType;
use crate::{
analysis::{is_gpointer, rust_type::RustType},
config::{self, parameter_matchable::ParameterMatchable},
env::Env,
library, nameutil,
};
#[derive(Clone, Debug)]
pub struct RustParameter {
pub name: String,
pub typ: library::TypeId,
pub direction: library::ParameterDirection,
pub nullable: library::Nullable,
pub ref_mode: RefMode,
pub try_from_glib: TryFromGlib,
}
#[derive(Clone, Debug)]
pub struct CParameter {
pub name: String,
pub typ: library::TypeId,
pub c_type: String,
}
impl CParameter {
pub fn is_real_gpointer(&self, env: &Env) -> bool {
is_gpointer(&self.c_type) && RustType::try_new(env, self.typ).is_err()
}
}
#[derive(Clone, Debug)]
pub struct Transformation {
pub ind_c: usize, pub ind_rust: usize, pub transformation: TransformationType,
pub name: String,
pub typ: library::TypeId,
pub transfer: library::Transfer,
pub ref_mode: RefMode,
pub conversion_type: ConversionType,
}
#[derive(Clone, Default, Debug)]
pub struct Parameters {
pub rust_parameters: Vec<RustParameter>,
pub c_parameters: Vec<CParameter>,
pub transformations: Vec<Transformation>,
}
impl Parameters {
pub fn new(capacity: usize) -> Self {
Self {
rust_parameters: Vec::with_capacity(capacity),
c_parameters: Vec::with_capacity(capacity),
transformations: Vec::with_capacity(capacity),
}
}
pub fn prepare_transformation(
&mut self,
env: &Env,
type_tid: library::TypeId,
name: String,
c_type: String,
direction: library::ParameterDirection,
transfer: library::Transfer,
nullable: library::Nullable,
ref_mode: RefMode,
conversion_type: ConversionType,
) -> Transformation {
let c_par = CParameter {
name: name.clone(),
typ: type_tid,
c_type,
};
let ind_c = self.c_parameters.len();
self.c_parameters.push(c_par);
let rust_par = RustParameter {
name: name.clone(),
typ: type_tid,
direction,
nullable,
ref_mode,
try_from_glib: TryFromGlib::from_type_defaults(env, type_tid),
};
let ind_rust = self.rust_parameters.len();
self.rust_parameters.push(rust_par);
Transformation {
ind_c,
ind_rust,
transformation: TransformationType::None,
name,
typ: type_tid,
transfer,
ref_mode,
conversion_type,
}
}
pub fn get(&self, ind_rust: usize) -> Option<&Transformation> {
self.transformations
.iter()
.find(|tr| tr.ind_rust == ind_rust)
}
}
pub fn analyze(
env: &Env,
signal_parameters: &[library::Parameter],
type_tid: library::TypeId,
configured_signals: &[&config::signals::Signal],
callback_parameters_config: Option<&config::functions::CallbackParameters>,
) -> Parameters {
let mut parameters = Parameters::new(signal_parameters.len() + 1);
let owner = env.type_(type_tid);
let c_type = format!("{}*", owner.get_glib_name().unwrap());
let transform = parameters.prepare_transformation(
env,
type_tid,
"this".to_owned(),
c_type,
library::ParameterDirection::In,
library::Transfer::None,
library::Nullable(false),
RefMode::ByRef,
ConversionType::Borrow,
);
parameters.transformations.push(transform);
for par in signal_parameters {
let name = nameutil::mangle_keywords(&*par.name).into_owned();
let ref_mode = RefMode::without_unneeded_mut(env, par, false, false);
let nullable_override = configured_signals
.matched_parameters(&name)
.iter()
.find_map(|p| p.nullable)
.or_else(|| {
callback_parameters_config.and_then(|cp| {
cp.iter()
.find(|cp| cp.ident.is_match(&par.name))
.and_then(|c| c.nullable)
})
});
let nullable = nullable_override.unwrap_or(par.nullable);
let conversion_type = {
match env.library.type_(par.typ) {
library::Type::Basic(library::Basic::Utf8)
| library::Type::Record(..)
| library::Type::Interface(..)
| library::Type::Class(..) => ConversionType::Borrow,
_ => ConversionType::of(env, par.typ),
}
};
let new_name = configured_signals
.matched_parameters(&name)
.iter()
.find_map(|p| p.new_name.clone());
let transformation_override = configured_signals
.matched_parameters(&name)
.iter()
.find_map(|p| p.transformation);
let mut transform = parameters.prepare_transformation(
env,
par.typ,
name,
par.c_type.clone(),
par.direction,
par.transfer,
nullable,
ref_mode,
conversion_type,
);
if let Some(new_name) = new_name {
transform.name = new_name;
}
if let Some(transformation_type) = transformation_override {
apply_transformation_type(env, &mut parameters, &mut transform, transformation_type);
}
parameters.transformations.push(transform);
}
parameters
}
fn apply_transformation_type(
env: &Env,
parameters: &mut Parameters,
transform: &mut Transformation,
transformation_type: TransformationType,
) {
transform.transformation = transformation_type;
match transformation_type {
TransformationType::None => (),
TransformationType::Borrow => {
if transform.conversion_type == ConversionType::Pointer {
transform.conversion_type = ConversionType::Borrow;
} else if transform.conversion_type != ConversionType::Borrow {
error!(
"Wrong conversion_type for borrow transformation {:?}",
transform.conversion_type
);
}
}
TransformationType::TreePath => {
let type_ = env.type_(transform.typ);
if let library::Type::Basic(library::Basic::Utf8) = type_ {
if let Some(type_tid) = env.library.find_type(0, "Gtk.TreePath") {
transform.typ = type_tid;
transform.conversion_type = ConversionType::Direct;
if let Some(rust_par) = parameters.rust_parameters.get_mut(transform.ind_rust) {
rust_par.typ = type_tid;
rust_par.ref_mode = RefMode::None;
}
} else {
error!("Type Gtk.TreePath not found for treepath transformation");
}
} else {
error!(
"Wrong parameter type for treepath transformation {:?}",
transform.typ
);
}
}
}
}