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