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