1use std::{
2 collections::HashMap,
3 ffi::{c_long, c_ulong},
4};
5
6use serde::Serialize;
7
8use crate::model::{
9 DefineValue, SourceLocation,
10 doc::PropertyDoc,
11 expression::{CallExpression, Expression},
12 operators::UnaryOp,
13 types::GType,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
17#[serde(rename_all = "snake_case")]
18pub enum ParamFlag {
19 Readable,
21 Writable,
23 ReadWrite,
25 Construct,
27 ConstructOnly,
29 LaxValidation,
31 StaticName,
33 Private,
35 StaticNick,
37 StaticBlurb,
39 StaticStrings,
41 ExplicitNotify,
43 Deprecated,
45 Unknown(String),
47}
48
49impl ParamFlag {
50 pub fn from_identifier(name: &str) -> Self {
51 match name {
52 "G_PARAM_READABLE" => Self::Readable,
53 "G_PARAM_WRITABLE" => Self::Writable,
54 "G_PARAM_READWRITE" => Self::ReadWrite,
55 "G_PARAM_CONSTRUCT" => Self::Construct,
56 "G_PARAM_CONSTRUCT_ONLY" => Self::ConstructOnly,
57 "G_PARAM_LAX_VALIDATION" => Self::LaxValidation,
58 "G_PARAM_STATIC_NAME" => Self::StaticName,
59 "G_PARAM_PRIVATE" => Self::Private,
60 "G_PARAM_STATIC_NICK" => Self::StaticNick,
61 "G_PARAM_STATIC_BLURB" => Self::StaticBlurb,
62 "G_PARAM_STATIC_STRINGS" => Self::StaticStrings,
63 "G_PARAM_EXPLICIT_NOTIFY" => Self::ExplicitNotify,
64 "G_PARAM_DEPRECATED" => Self::Deprecated,
65 _ => Self::Unknown(name.to_string()),
66 }
67 }
68
69 pub fn as_str(&self) -> &str {
70 match self {
71 Self::Readable => "G_PARAM_READABLE",
72 Self::Writable => "G_PARAM_WRITABLE",
73 Self::ReadWrite => "G_PARAM_READWRITE",
74 Self::Construct => "G_PARAM_CONSTRUCT",
75 Self::ConstructOnly => "G_PARAM_CONSTRUCT_ONLY",
76 Self::LaxValidation => "G_PARAM_LAX_VALIDATION",
77 Self::StaticName => "G_PARAM_STATIC_NAME",
78 Self::Private => "G_PARAM_PRIVATE",
79 Self::StaticNick => "G_PARAM_STATIC_NICK",
80 Self::StaticBlurb => "G_PARAM_STATIC_BLURB",
81 Self::StaticStrings => "G_PARAM_STATIC_STRINGS",
82 Self::ExplicitNotify => "G_PARAM_EXPLICIT_NOTIFY",
83 Self::Deprecated => "G_PARAM_DEPRECATED",
84 Self::Unknown(name) => name.as_str(),
85 }
86 }
87}
88
89#[derive(Debug, Clone, Serialize)]
90pub struct Property {
91 pub name: String,
92 pub property_type: PropertyType,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub nick: Option<String>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub blurb: Option<String>,
97 #[serde(skip_serializing_if = "Vec::is_empty")]
98 pub flags: Vec<ParamFlag>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub doc: Option<PropertyDoc>,
101}
102
103#[derive(Debug, Clone, Serialize)]
104#[serde(rename_all = "snake_case")]
105pub enum PropertyType {
106 String,
107 Int {
108 min: i32,
109 max: i32,
110 default: i32,
111 },
112 UInt {
113 min: u32,
114 max: u32,
115 default: u32,
116 },
117 Int64 {
118 min: i64,
119 max: i64,
120 default: i64,
121 },
122 UInt64 {
123 min: u64,
124 max: u64,
125 default: u64,
126 },
127 Long {
128 min: c_long,
129 max: c_long,
130 default: c_long,
131 },
132 ULong {
133 min: c_ulong,
134 max: c_ulong,
135 default: c_ulong,
136 },
137 Char {
138 min: i8,
139 max: i8,
140 default: i8,
141 },
142 UChar {
143 min: u8,
144 max: u8,
145 default: u8,
146 },
147 Unichar {
148 default: u32,
149 },
150 Param {
151 param_type: GType,
152 },
153 Boolean {
154 default: bool,
155 },
156 Float {
157 min: f32,
158 max: f32,
159 default: f32,
160 },
161 Double {
162 min: f64,
163 max: f64,
164 default: f64,
165 },
166 Enum {
167 enum_type: GType,
168 default: i32,
169 },
170 Flags {
171 flags_type: GType,
172 default: u32,
173 },
174 Object {
175 object_type: GType,
176 },
177 Boxed {
178 boxed_type: GType,
179 },
180 Pointer,
181 GType {
182 is_a_type: GType,
183 },
184 Variant {
185 variant_type: Option<String>,
186 default_value: Option<Expression>,
187 },
188 Override,
189 Unknown {
190 spec_function: String,
191 },
192}
193
194impl Property {
195 pub(crate) fn from_param_spec_call(
201 call: &CallExpression,
202 defines: &HashMap<String, DefineValue>,
203 ) -> Option<Self> {
204 let func_name = call.function_name_str()?;
205
206 let args = &call.arguments;
207
208 if func_name == "g_param_spec_override" {
210 let name = args.first()?.resolve_string_value(defines)?;
211 return Some(Self {
212 name,
213 nick: None,
214 blurb: None,
215 property_type: PropertyType::Override,
216 flags: Vec::new(),
217 doc: None,
218 });
219 }
220
221 if args.len() < 3 {
223 return None;
224 }
225
226 let name = args[0].resolve_string_value(defines)?;
227 let nick = args[1].extract_string_value();
228 let blurb = args[2].extract_string_value();
229
230 let property_type = match func_name {
231 "g_param_spec_string" => {
232 PropertyType::String
234 }
235 "g_param_spec_boolean" => {
236 let default = if args.len() > 3 {
238 extract_boolean_arg(&args[3]).unwrap_or(false)
239 } else {
240 false
241 };
242 PropertyType::Boolean { default }
243 }
244 "g_param_spec_int" => {
245 let min = if args.len() > 3 {
247 extract_int_arg::<i32>(&args[3]).unwrap_or(i32::MIN)
248 } else {
249 i32::MIN
250 };
251 let max = if args.len() > 4 {
252 extract_int_arg::<i32>(&args[4]).unwrap_or(i32::MAX)
253 } else {
254 i32::MAX
255 };
256 let default = if args.len() > 5 {
257 extract_int_arg::<i32>(&args[5]).unwrap_or(0)
258 } else {
259 0
260 };
261 PropertyType::Int { min, max, default }
262 }
263 "g_param_spec_uint" => {
264 let min = if args.len() > 3 {
266 extract_uint_arg::<u32>(&args[3]).unwrap_or(0)
267 } else {
268 0
269 };
270 let max = if args.len() > 4 {
271 extract_uint_arg::<u32>(&args[4]).unwrap_or(u32::MAX)
272 } else {
273 u32::MAX
274 };
275 let default = if args.len() > 5 {
276 extract_uint_arg::<u32>(&args[5]).unwrap_or(0)
277 } else {
278 0
279 };
280 PropertyType::UInt { min, max, default }
281 }
282 "g_param_spec_float" => {
283 let min = if args.len() > 3 {
285 extract_float_arg::<f32>(&args[3]).unwrap_or(f32::MIN)
286 } else {
287 f32::MIN
288 };
289 let max = if args.len() > 4 {
290 extract_float_arg::<f32>(&args[4]).unwrap_or(f32::MAX)
291 } else {
292 f32::MAX
293 };
294 let default = if args.len() > 5 {
295 extract_float_arg::<f32>(&args[5]).unwrap_or(0.0)
296 } else {
297 0.0
298 };
299 PropertyType::Float { min, max, default }
300 }
301 "g_param_spec_double" => {
302 let min = if args.len() > 3 {
304 extract_float_arg::<f64>(&args[3]).unwrap_or(f64::MIN)
305 } else {
306 f64::MIN
307 };
308 let max = if args.len() > 4 {
309 extract_float_arg::<f64>(&args[4]).unwrap_or(f64::MAX)
310 } else {
311 f64::MAX
312 };
313 let default = if args.len() > 5 {
314 extract_float_arg::<f64>(&args[5]).unwrap_or(0.0)
315 } else {
316 0.0
317 };
318 PropertyType::Double { min, max, default }
319 }
320 "g_param_spec_enum" => {
321 let enum_type = args.get(3).and_then(|e| extract_gtype_arg(e))?;
323 let default = if args.len() > 4 {
324 extract_int_arg::<i32>(&args[4]).unwrap_or(0)
325 } else {
326 0
327 };
328 PropertyType::Enum { enum_type, default }
329 }
330 "g_param_spec_flags" => {
331 let flags_type = args.get(3).and_then(|e| extract_gtype_arg(e))?;
333 let default = if args.len() > 4 {
334 extract_uint_arg::<u32>(&args[4]).unwrap_or(0)
335 } else {
336 0
337 };
338 PropertyType::Flags {
339 flags_type,
340 default,
341 }
342 }
343 "g_param_spec_object" => {
344 let object_type = args.get(3).and_then(|e| extract_gtype_arg(e))?;
346 PropertyType::Object { object_type }
347 }
348 "g_param_spec_boxed" => {
349 let boxed_type = args.get(3).and_then(|e| extract_gtype_arg(e))?;
351 PropertyType::Boxed { boxed_type }
352 }
353 "g_param_spec_pointer" => PropertyType::Pointer,
354 "g_param_spec_gtype" => {
355 let is_a_type = args.get(3).and_then(|e| extract_gtype_arg(e))?;
357 PropertyType::GType { is_a_type }
358 }
359 "g_param_spec_int64" => {
360 let min = if args.len() > 3 {
362 extract_int_arg::<i64>(&args[3]).unwrap_or(i64::MIN)
363 } else {
364 i64::MIN
365 };
366 let max = if args.len() > 4 {
367 extract_int_arg::<i64>(&args[4]).unwrap_or(i64::MAX)
368 } else {
369 i64::MAX
370 };
371 let default = if args.len() > 5 {
372 extract_int_arg::<i64>(&args[5]).unwrap_or(0)
373 } else {
374 0
375 };
376 PropertyType::Int64 { min, max, default }
377 }
378 "g_param_spec_uint64" => {
379 let min = if args.len() > 3 {
381 extract_uint_arg::<u64>(&args[3]).unwrap_or(0)
382 } else {
383 0
384 };
385 let max = if args.len() > 4 {
386 extract_uint_arg::<u64>(&args[4]).unwrap_or(u64::MAX)
387 } else {
388 u64::MAX
389 };
390 let default = if args.len() > 5 {
391 extract_uint_arg::<u64>(&args[5]).unwrap_or(0)
392 } else {
393 0
394 };
395 PropertyType::UInt64 { min, max, default }
396 }
397 "g_param_spec_long" => {
398 let min = if args.len() > 3 {
400 extract_int_arg::<c_long>(&args[3]).unwrap_or(c_long::MIN)
401 } else {
402 c_long::MIN
403 };
404 let max = if args.len() > 4 {
405 extract_int_arg::<c_long>(&args[4]).unwrap_or(c_long::MAX)
406 } else {
407 c_long::MAX
408 };
409 let default = if args.len() > 5 {
410 extract_int_arg::<c_long>(&args[5]).unwrap_or(0)
411 } else {
412 0
413 };
414 PropertyType::Long { min, max, default }
415 }
416 "g_param_spec_ulong" => {
417 let min = if args.len() > 3 {
419 extract_uint_arg::<c_ulong>(&args[3]).unwrap_or(0)
420 } else {
421 0
422 };
423 let max = if args.len() > 4 {
424 extract_uint_arg::<c_ulong>(&args[4]).unwrap_or(c_ulong::MAX)
425 } else {
426 c_ulong::MAX
427 };
428 let default = if args.len() > 5 {
429 extract_uint_arg::<c_ulong>(&args[5]).unwrap_or(0)
430 } else {
431 0
432 };
433 PropertyType::ULong { min, max, default }
434 }
435 "g_param_spec_char" => {
436 let min = if args.len() > 3 {
438 extract_int_arg::<i8>(&args[3]).unwrap_or(i8::MIN)
439 } else {
440 i8::MIN
441 };
442 let max = if args.len() > 4 {
443 extract_int_arg::<i8>(&args[4]).unwrap_or(i8::MAX)
444 } else {
445 i8::MAX
446 };
447 let default = if args.len() > 5 {
448 extract_int_arg::<i8>(&args[5]).unwrap_or(0)
449 } else {
450 0
451 };
452 PropertyType::Char { min, max, default }
453 }
454 "g_param_spec_uchar" => {
455 let min = if args.len() > 3 {
457 extract_uint_arg::<u8>(&args[3]).unwrap_or(0)
458 } else {
459 0
460 };
461 let max = if args.len() > 4 {
462 extract_uint_arg::<u8>(&args[4]).unwrap_or(u8::MAX)
463 } else {
464 u8::MAX
465 };
466 let default = if args.len() > 5 {
467 extract_uint_arg::<u8>(&args[5]).unwrap_or(0)
468 } else {
469 0
470 };
471 PropertyType::UChar { min, max, default }
472 }
473 "g_param_spec_unichar" => {
474 let default = if args.len() > 3 {
476 extract_uint_arg::<u32>(&args[3]).unwrap_or(0)
477 } else {
478 0
479 };
480 PropertyType::Unichar { default }
481 }
482 "g_param_spec_param" => {
483 let param_type = args.get(3).and_then(|e| extract_gtype_arg(e))?;
485 PropertyType::Param { param_type }
486 }
487 "g_param_spec_variant" => {
488 let variant_type = args.get(3).and_then(|arg| match arg.as_ref() {
490 Expression::Identifier(id) => Some(id.name.as_str()),
491 Expression::Call(call) => Some(call.function_name()),
492 _ => None,
493 });
494 let default_value = args.get(4).and_then(|arg| {
495 if matches!(arg.as_ref(), Expression::Null(_)) {
496 None
497 } else {
498 Some(*arg.clone())
499 }
500 });
501 PropertyType::Variant {
502 variant_type: variant_type.map(ToOwned::to_owned),
503 default_value,
504 }
505 }
506 _ => PropertyType::Unknown {
507 spec_function: func_name.to_string(),
508 },
509 };
510
511 let flags = if let Some(last_arg) = args.last() {
513 extract_flags_arg(last_arg)
514 } else {
515 Vec::new()
516 };
517
518 Some(Self {
519 name,
520 property_type,
521 nick,
522 blurb,
523 flags,
524 doc: None,
525 })
526 }
527
528 pub fn from_override_property_call(
532 call: &CallExpression,
533 defines: &HashMap<String, DefineValue>,
534 ) -> Option<Self> {
535 let func_name = call.function_name_str()?;
536 if func_name != "g_object_class_override_property" {
537 return None;
538 }
539
540 let args = &call.arguments;
541 if args.len() < 3 {
542 return None;
543 }
544
545 let name = args[2].resolve_string_value(defines)?;
547
548 Some(Self {
549 name,
550 property_type: PropertyType::Override,
551 nick: None,
552 blurb: None,
553 flags: Vec::new(),
554 doc: None,
555 })
556 }
557}
558
559fn extract_int_arg<T>(arg: &Expression) -> Option<T>
560where
561 T: std::str::FromStr + std::ops::Neg<Output = T>,
562{
563 match arg {
564 Expression::NumberLiteral(num) => num.value.parse().ok(),
565 Expression::Unary(unary) => {
566 if matches!(unary.operator, UnaryOp::Negate)
567 && let Expression::NumberLiteral(num) = &*unary.operand
568 {
569 return num.value.parse::<T>().ok().map(|v| -v);
570 }
571 None
572 }
573 _ => None,
574 }
575}
576
577fn extract_uint_arg<T: std::str::FromStr>(arg: &Expression) -> Option<T> {
578 match arg {
579 Expression::NumberLiteral(num) => num.value.parse().ok(),
580 _ => None,
581 }
582}
583
584fn extract_float_arg<T>(arg: &Expression) -> Option<T>
585where
586 T: std::str::FromStr + std::ops::Neg<Output = T>,
587{
588 match arg {
589 Expression::NumberLiteral(num) => num.value.parse().ok(),
590 Expression::Unary(unary) => {
591 if matches!(unary.operator, UnaryOp::Negate)
592 && let Expression::NumberLiteral(num) = &*unary.operand
593 {
594 return num.value.parse::<T>().ok().map(|v| -v);
595 }
596 None
597 }
598 _ => None,
599 }
600}
601
602fn extract_boolean_arg(arg: &Expression) -> Option<bool> {
603 match arg {
604 Expression::Boolean(b) => Some(b.value),
605 Expression::Identifier(id) => match id.name.as_str() {
606 "TRUE" | "true" => Some(true),
607 "FALSE" | "false" => Some(false),
608 _ => None,
609 },
610 _ => None,
611 }
612}
613
614fn extract_gtype_arg(arg: &Expression) -> Option<GType> {
615 GType::from_expression(arg)
616}
617
618fn extract_flags_arg(arg: &Expression) -> Vec<ParamFlag> {
619 let mut flags = Vec::new();
620 arg.walk(&mut |e| {
621 if let Expression::Identifier(id) = e {
622 flags.push(ParamFlag::from_identifier(&id.name));
623 }
624 });
625 flags
626}
627
628#[derive(Debug, Clone, Serialize)]
630#[serde(rename_all = "snake_case")]
631pub enum ParamSpecAssignment {
632 ArraySubscript {
634 array_name: String,
635 enum_value: String,
636 statement_location: SourceLocation,
637 call: CallExpression,
638 property: Property,
639 install_call: Option<CallExpression>,
640 },
641 Variable {
643 variable_name: String,
644 statement_location: SourceLocation,
645 call: CallExpression,
646 property: Property,
647 install_call: Option<CallExpression>,
648 },
649 OverrideProperty {
652 enum_value: String,
653 statement_location: SourceLocation,
654 call: CallExpression,
655 property: Property,
656 },
657 DirectInstall {
660 enum_value: String,
661 statement_location: SourceLocation,
662 call: CallExpression,
664 property: Property,
665 install_call: CallExpression,
667 },
668}
669
670impl ParamSpecAssignment {
671 pub fn is_installed(&self) -> bool {
674 match self {
675 Self::ArraySubscript { install_call, .. } => install_call.is_some(),
676 Self::OverrideProperty { .. } => true,
677 Self::Variable { install_call, .. } => install_call.is_some(),
678 Self::DirectInstall { .. } => true,
679 }
680 }
681
682 pub fn get_installed_enum_value(&self) -> Option<&str> {
685 match self {
686 Self::ArraySubscript {
687 enum_value,
688 install_call,
689 ..
690 } => install_call.as_ref().map(|_| enum_value.as_str()),
691 Self::OverrideProperty { enum_value, .. } => Some(enum_value),
692 Self::Variable { install_call, .. } => install_call
693 .as_ref()
694 .and_then(|call| call.get_arg(1).and_then(|arg| arg.location().as_str())),
695 Self::DirectInstall { enum_value, .. } => Some(enum_value),
696 }
697 }
698
699 pub fn property(&self) -> &Property {
701 match self {
702 Self::ArraySubscript { property, .. } => property,
703 Self::Variable { property, .. } => property,
704 Self::OverrideProperty { property, .. } => property,
705 Self::DirectInstall { property, .. } => property,
706 }
707 }
708
709 pub fn param_spec_call(&self) -> Option<&CallExpression> {
711 match self {
712 Self::ArraySubscript { call, .. }
713 | Self::Variable { call, .. }
714 | Self::DirectInstall { call, .. } => Some(call),
715 Self::OverrideProperty { .. } => None,
716 }
717 }
718
719 pub fn statement_location(&self) -> &SourceLocation {
720 match self {
721 Self::ArraySubscript {
722 statement_location, ..
723 }
724 | Self::Variable {
725 statement_location, ..
726 }
727 | Self::OverrideProperty {
728 statement_location, ..
729 }
730 | Self::DirectInstall {
731 statement_location, ..
732 } => statement_location,
733 }
734 }
735
736 pub fn enum_value(&self) -> Option<&str> {
737 match self {
738 Self::ArraySubscript { enum_value, .. } => Some(enum_value.as_str()),
739 Self::OverrideProperty { enum_value, .. } => Some(enum_value.as_str()),
740 Self::DirectInstall { enum_value, .. } => Some(enum_value.as_str()),
741 Self::Variable { .. } => None,
742 }
743 }
744}