libgir/analysis/
out_parameters.rs1use std::slice::Iter;
2
3use log::error;
4
5use crate::{
6 analysis::{
7 self, conversion_type::ConversionType, function_parameters::CParameter,
8 functions::is_carray_with_direct_elements, return_value, rust_type::RustType,
9 },
10 config::{self, parameter_matchable::ParameterMatchable},
11 env::Env,
12 library::{self, Basic, Function, INTERNAL_NAMESPACE, ParameterDirection, Type, TypeId},
13 nameutil,
14};
15
16#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
17pub enum ThrowFunctionReturnStrategy {
18 #[default]
19 ReturnResult,
20 CheckError,
21 Void,
22}
23
24#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
25pub enum Mode {
26 #[default]
27 None,
28 Normal,
29 Optional,
30 Combined,
31 Throws(ThrowFunctionReturnStrategy),
32}
33
34#[derive(Debug, Default)]
35pub struct Info {
36 pub mode: Mode,
37 pub params: Vec<analysis::Parameter>,
38}
39
40impl Info {
41 pub fn is_empty(&self) -> bool {
42 self.mode == Mode::None
43 }
44
45 pub fn iter(&self) -> Iter<'_, analysis::Parameter> {
46 self.params.iter()
47 }
48}
49
50pub fn analyze(
51 env: &Env,
52 func: &Function,
53 func_c_params: &[CParameter],
54 func_ret: &return_value::Info,
55 configured_functions: &[&config::functions::Function],
56) -> (Info, bool) {
57 let mut info: Info = Default::default();
58 let mut unsupported_outs = false;
59
60 let nullable_override = configured_functions.iter().find_map(|f| f.ret.nullable);
61 if func.throws {
62 let return_strategy =
63 decide_throw_function_return_strategy(env, func_ret, &func.name, configured_functions);
64 info.mode = Mode::Throws(return_strategy);
65 } else if func.ret.typ() == TypeId::tid_none() {
66 info.mode = Mode::Normal;
67 } else if func.ret.typ() == TypeId::tid_bool() || func.ret.typ() == TypeId::tid_c_bool() {
68 if nullable_override == Some(false) {
69 info.mode = Mode::Combined;
70 } else {
71 info.mode = Mode::Optional;
72 }
73 } else {
74 info.mode = Mode::Combined;
75 }
76
77 for lib_par in &func.parameters {
78 if lib_par.direction() != ParameterDirection::Out {
79 continue;
80 }
81 if can_as_return(env, lib_par) {
82 let new_name = nameutil::mangle_keywords(lib_par.name());
83 let mut lib_par = lib_par.clone();
84 lib_par.set_name(&new_name);
85 let configured_parameters = configured_functions.matched_parameters(lib_par.name());
86 let mut out = analysis::Parameter::from_parameter(env, lib_par, &configured_parameters);
87
88 if let Some(c_par) = func_c_params
92 .iter()
93 .find(|c_par| c_par.name == out.lib_par.name())
94 {
95 out.lib_par.set_tid(c_par.typ);
96 out.lib_par.set_nullable(c_par.nullable);
97 }
98
99 info.params.push(out);
100 } else {
101 unsupported_outs = true;
102 }
103 }
104
105 if info.params.is_empty() {
106 info.mode = Mode::None;
107 }
108 if info.mode == Mode::Combined
109 || info.mode == Mode::Throws(ThrowFunctionReturnStrategy::ReturnResult)
110 {
111 let mut ret =
112 analysis::Parameter::from_return_value(env, func.ret.clone(), configured_functions);
113
114 if let Some(ref par) = func_ret.parameter {
116 ret.lib_par.set_tid(par.lib_par.typ());
117 }
118 if let Some(val) = nullable_override {
119 ret.lib_par.set_nullable(val);
120 }
121 info.params.insert(0, ret);
122 }
123
124 (info, unsupported_outs)
125}
126
127pub fn can_as_return(env: &Env, par: &library::Parameter) -> bool {
128 use super::conversion_type::ConversionType::*;
129 match ConversionType::of(env, par.typ()) {
130 Direct | Scalar | Option | Result { .. } => true,
131 Pointer => {
132 if is_carray_with_direct_elements(env, par.typ()) && par.array_length().is_none() {
134 return false;
135 }
136
137 RustType::builder(env, par.typ())
138 .direction(ParameterDirection::Out)
139 .scope(par.scope())
140 .try_build_param()
141 .is_ok()
142 }
143 Borrow => false,
144 Unknown => false,
145 }
146}
147
148fn decide_throw_function_return_strategy(
149 env: &Env,
150 ret: &return_value::Info,
151 func_name: &str,
152 configured_functions: &[&config::functions::Function],
153) -> ThrowFunctionReturnStrategy {
154 let typ = ret
155 .parameter
156 .as_ref()
157 .map(|par| par.lib_par.typ())
158 .unwrap_or_default();
159 if env.type_(typ).eq(&Type::Basic(Basic::None)) {
160 ThrowFunctionReturnStrategy::Void
161 } else if use_function_return_for_result(env, typ, func_name, configured_functions) {
162 ThrowFunctionReturnStrategy::ReturnResult
163 } else {
164 ThrowFunctionReturnStrategy::CheckError
165 }
166}
167
168pub fn use_function_return_for_result(
169 env: &Env,
170 typ: TypeId,
171 func_name: &str,
172 configured_functions: &[&config::functions::Function],
173) -> bool {
174 let use_return_for_result = configured_functions
176 .iter()
177 .find_map(|f| f.ret.use_return_for_result.as_ref());
178 if let Some(use_return_for_result) = use_return_for_result {
179 if typ == Default::default() {
180 error!(
181 "Function \"{func_name}\": use_return_for_result set to true, but function has no return value"
182 );
183 return false;
184 }
185 return *use_return_for_result;
186 }
187
188 if typ == Default::default() {
189 return false;
190 }
191 if typ.ns_id != INTERNAL_NAMESPACE {
192 return true;
193 }
194 let type_ = env.type_(typ);
195 !matches!(&*type_.get_name(), "UInt" | "Boolean" | "Bool")
196}