Inspect a field's right-hand-side call for ORM hints, flags, relations, and type.
(expression: &Expression)
| 497 | |
| 498 | /// Inspect a field's right-hand-side call for ORM hints, flags, relations, and type. |
| 499 | fn analyze_call(expression: &Expression) -> Option<CallInfo> { |
| 500 | let Expression::Call(CallExpression { |
| 501 | func, arguments, .. |
| 502 | }) = expression |
| 503 | else { |
| 504 | return None; |
| 505 | }; |
| 506 | |
| 507 | let leaf = call_leaf(func)?; |
| 508 | let module = call_module(func); |
| 509 | let mut info = CallInfo::default(); |
| 510 | |
| 511 | if module.as_deref() == Some("models") { |
| 512 | info.orm = Some("django"); |
| 513 | } else if matches!(leaf.as_str(), "Column" | "mapped_column" | "relationship") { |
| 514 | info.orm = Some("sqlalchemy"); |
| 515 | } |
| 516 | |
| 517 | // Relationship / foreign-key constructors yield a relation, not a scalar column. |
| 518 | const RELATIONS: &[&str] = &[ |
| 519 | "relationship", |
| 520 | "ForeignKey", |
| 521 | "ManyToManyField", |
| 522 | "OneToOneField", |
| 523 | ]; |
| 524 | |
| 525 | if RELATIONS.contains(&leaf.as_str()) { |
| 526 | info.relation = arguments.args.first().and_then(relation_target); |
| 527 | |
| 528 | if leaf != "relationship" { |
| 529 | info.orm = Some("django"); |
| 530 | } |
| 531 | |
| 532 | return Some(info); |
| 533 | } |
| 534 | |
| 535 | // Flags from truthy boolean keywords (primary_key/unique/nullable/null). |
| 536 | for keyword in &arguments.keywords { |
| 537 | let Some(arg) = keyword.arg.as_ref().map(Identifier::as_str) else { |
| 538 | continue; |
| 539 | }; |
| 540 | |
| 541 | if !matches!(&keyword.value, Expression::BooleanLiteral(literal) if literal.value) { |
| 542 | continue; |
| 543 | } |
| 544 | |
| 545 | match arg { |
| 546 | "primary_key" => info.flags.push("pk".to_string()), |
| 547 | "unique" => info.flags.push("unique".to_string()), |
| 548 | "nullable" | "null" => info.flags.push("nullable".to_string()), |
| 549 | _ => {} |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | // Column type: Django field leaf, or SQLAlchemy `Column(Type, ...)` first positional. |
| 554 | if module.as_deref() == Some("models") { |
| 555 | info.ctor_type = Some(django_type(&leaf)); |
| 556 | } else if leaf == "Column" || leaf == "mapped_column" { |
no test coverage detected