use crate::types::describe::{FieldType, SObjectDescribe};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SchemaInsights {
pub total_fields: usize,
pub custom_field_count: usize,
pub standard_field_count: usize,
pub required_field_count: usize,
pub formula_field_count: usize,
pub relationship_field_count: usize,
pub complexity_score: usize,
}
#[must_use]
pub fn analyze_schema(describe: &SObjectDescribe) -> SchemaInsights {
let total_fields = describe.fields.len();
let mut custom_field_count = 0;
let mut required_field_count = 0;
let mut formula_field_count = 0;
let mut relationship_field_count = 0;
for field in &describe.fields {
if field.custom {
custom_field_count += 1;
}
if !field.nillable && !field.defaulted_on_create && field.name != "Id" {
required_field_count += 1;
}
if field.calculated {
formula_field_count += 1;
}
if matches!(field.type_, FieldType::Reference) {
relationship_field_count += 1;
}
}
let standard_field_count = total_fields - custom_field_count;
let complexity_score = (total_fields / 10)
+ (custom_field_count * 2)
+ (formula_field_count * 5)
+ (relationship_field_count * 3);
SchemaInsights {
total_fields,
custom_field_count,
standard_field_count,
required_field_count,
formula_field_count,
relationship_field_count,
complexity_score,
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_schema_analyzer_exhaustive_mutants() {
let describe = create_mock_describe(&json!([
mock_field("Id", "id", false, false, true, false), mock_field("Name", "string", false, false, false, false), mock_field("Nillable", "string", false, true, false, false), mock_field("Defaulted", "string", false, false, true, false), mock_field("Standard", "string", false, true, true, false), mock_field("Custom1__c", "string", true, true, true, false), mock_field("Custom2__c", "string", true, true, true, false), mock_field("Custom3__c", "string", true, true, true, false), mock_field("Custom4__c", "string", true, true, true, false), mock_field("Custom5__c", "string", true, true, true, false), mock_field("Custom6__c", "string", true, true, true, false), mock_field("Custom7__c", "string", true, true, true, false), mock_field("Formula1__c", "double", true, true, true, true), mock_field("Formula2__c", "double", true, true, true, true), mock_field("Rel1", "reference", false, true, true, false), mock_field("Rel2", "reference", false, true, true, false), mock_field("Rel3", "reference", false, true, true, false), mock_field("Rel4", "reference", false, true, true, false), mock_field("Req2", "string", false, false, false, false), mock_field("Req3", "string", false, false, false, false), mock_field("Pad1", "string", false, true, true, false) ]));
let insights = analyze_schema(&describe);
assert_eq!(insights.total_fields, 21, "total_fields mismatch");
assert_eq!(
insights.custom_field_count, 9,
"custom_field_count mismatch"
);
assert_eq!(
insights.formula_field_count, 2,
"formula_field_count mismatch"
);
assert_eq!(
insights.relationship_field_count, 4,
"relationship_field_count mismatch"
);
assert_eq!(
insights.required_field_count, 3,
"required_field_count mismatch"
); assert_eq!(
insights.standard_field_count, 12,
"standard_field_count mismatch"
);
assert_eq!(insights.complexity_score, 42, "complexity_score mismatch");
let expected = SchemaInsights {
total_fields: 21,
custom_field_count: 9,
standard_field_count: 12,
required_field_count: 3,
formula_field_count: 2,
relationship_field_count: 4,
complexity_score: 42,
};
assert_eq!(insights, expected, "Struct equality failed");
let empty_describe = create_mock_describe(&json!([]));
let empty_insights = analyze_schema(&empty_describe);
let empty_expected = SchemaInsights {
total_fields: 0,
custom_field_count: 0,
standard_field_count: 0,
required_field_count: 0,
formula_field_count: 0,
relationship_field_count: 0,
complexity_score: 0,
};
assert_eq!(
empty_insights, empty_expected,
"Empty struct equality failed"
);
}
use super::*;
use crate::test_support::Must;
use serde_json::json;
fn create_mock_describe(fields_json: &serde_json::Value) -> SObjectDescribe {
let describe_json = json!({
"name": "Account",
"label": "Account",
"custom": false,
"queryable": true,
"activateable": false, "createable": true, "customSetting": false, "deletable": true,
"deprecatedAndHidden": false, "feedEnabled": true, "hasSubtypes": false,
"isSubtype": false, "keyPrefix": "001", "labelPlural": "Accounts", "layoutable": true,
"mergeable": true, "mruEnabled": true, "replicateable": true, "retrieveable": true,
"searchable": true, "triggerable": true, "undeletable": true, "updateable": true,
"urls": {}, "childRelationships": [], "recordTypeInfos": [],
"fields": fields_json.clone()
});
serde_json::from_value(describe_json).must()
}
#[allow(clippy::fn_params_excessive_bools)]
fn mock_field(
name: &str,
field_type: &str,
custom: bool,
nillable: bool,
defaulted: bool,
calculated: bool,
) -> serde_json::Value {
json!({
"name": name,
"type": field_type,
"label": format!("{} Label", name),
"referenceTo": if field_type == "reference" { vec!["Account"] } else { vec![] },
"custom": custom,
"nillable": nillable,
"defaultedOnCreate": defaulted,
"calculated": calculated,
"createable": true, "autoNumber": false, "aggregatable": true, "byteLength": 18,
"cascadeDelete": false, "caseSensitive": false,
"dependentPicklist": false, "deprecatedAndHidden": false,
"digits": 0, "displayLocationInDecimal": false, "encrypted": false, "externalId": false,
"filterable": true, "groupable": true, "highScaleNumber": false, "htmlFormatted": false,
"idLookup": true, "length": 18, "nameField": false, "namePointing": false,
"permissionable": false, "polymorphicForeignKey": false, "precision": 0, "queryByDistance": false,
"restrictedDelete": false, "restrictedPicklist": false, "scale": 0, "soapType": "tns:ID",
"sortable": true, "unique": false, "updateable": false, "writeRequiresMasterRead": false
})
}
#[test]
fn test_schema_analyzer() {
let describe = create_mock_describe(&json!([
mock_field("Id", "id", false, false, true, false),
mock_field("Name", "string", false, false, false, false),
mock_field("CustomField__c", "string", true, true, false, false),
mock_field("Formula__c", "double", true, true, false, true),
mock_field("ParentId", "reference", false, true, false, false),
mock_field("CustomLookup__c", "reference", true, false, false, false)
]));
let insights = analyze_schema(&describe);
assert_eq!(insights.total_fields, 6);
assert_eq!(insights.custom_field_count, 3);
assert_eq!(insights.standard_field_count, 3);
assert_eq!(insights.required_field_count, 2);
assert_eq!(insights.formula_field_count, 1);
assert_eq!(insights.relationship_field_count, 2);
assert_eq!(insights.complexity_score, 17);
}
#[test]
fn test_schema_analyzer_complexity_math() {
let describe = create_mock_describe(&json!([
mock_field("Id", "id", false, false, true, false),
mock_field("Name", "string", false, false, false, false),
mock_field("Custom1__c", "string", true, true, false, false),
mock_field("Custom2__c", "string", true, true, false, false),
mock_field("Custom3__c", "string", true, true, false, false),
mock_field("Formula1__c", "double", true, true, false, true),
mock_field("Formula2__c", "double", true, true, false, true),
mock_field("Rel1__c", "reference", true, true, false, false),
mock_field("Rel2__c", "reference", true, true, false, false),
mock_field("Standard1", "string", false, true, false, false),
mock_field("Standard2", "string", false, true, false, false),
mock_field("Standard3", "string", false, true, false, false)
]));
let insights = analyze_schema(&describe);
assert_eq!(insights.total_fields, 12);
assert_eq!(insights.custom_field_count, 7);
assert_eq!(insights.formula_field_count, 2);
assert_eq!(insights.relationship_field_count, 2);
assert_eq!(insights.complexity_score, 31);
}
}