use std::collections::HashMap;
use tracing::trace;
use crate::error::ScanError;
use crate::model::{DepLevel, DepType, ParseResult};
use crate::port::ScanStore;
use super::types::{SameFileResult, UnresolvedRef};
enum RefResolution {
SameFile {
source_id: i64,
target_id: i64,
dep_type: DepType,
},
CrossFile(UnresolvedRef),
}
pub(super) fn resolve_same_file(
db: &dyn ScanStore,
file_id: i64,
parse_result: &ParseResult,
name_to_ids: &HashMap<String, Vec<i64>>,
) -> Result<SameFileResult, ScanError> {
let resolve_name =
|name: &str| -> Option<i64> { name_to_ids.get(name).and_then(|ids| ids.first().copied()) };
let call_resolutions: Vec<RefResolution> = parse_result
.calls
.iter()
.filter_map(|call| {
let caller_id = resolve_name(&call.caller_name)?;
trace!(
"{} -> {} at line {}",
call.caller_name,
call.callee_name,
call.line
);
Some(match resolve_name(&call.callee_name) {
Some(callee_id) => RefResolution::SameFile {
source_id: caller_id,
target_id: callee_id,
dep_type: DepType::Calls,
},
None => RefResolution::CrossFile(UnresolvedRef {
source_file_id: file_id,
source_symbol_id: caller_id,
target_name: call.callee_name.clone(),
dep_type: DepType::Calls,
}),
})
})
.collect();
let type_resolutions: Vec<RefResolution> = parse_result
.type_refs
.iter()
.filter_map(|type_ref| {
let from_id = resolve_name(&type_ref.from_symbol)?;
trace!(
"{} -> {} at line {}",
type_ref.from_symbol,
type_ref.to_type,
type_ref.line
);
Some(match resolve_name(&type_ref.to_type) {
Some(to_id) if from_id != to_id => RefResolution::SameFile {
source_id: from_id,
target_id: to_id,
dep_type: DepType::TypeRef,
},
Some(_) => return None, None => RefResolution::CrossFile(UnresolvedRef {
source_file_id: file_id,
source_symbol_id: from_id,
target_name: type_ref.to_type.clone(),
dep_type: DepType::TypeRef,
}),
})
})
.collect();
let all_resolutions: Vec<RefResolution> = call_resolutions
.into_iter()
.chain(type_resolutions)
.collect();
let (dep_count, unresolved_refs) = all_resolutions.into_iter().try_fold(
(0i64, Vec::<UnresolvedRef>::new()),
|(count, mut unresolved), res| -> Result<_, ScanError> {
match res {
RefResolution::SameFile {
source_id,
target_id,
dep_type,
} => {
db.insert_dependency(
file_id,
Some(source_id),
Some(file_id),
Some(target_id),
dep_type,
DepLevel::Symbol,
None,
)?;
Ok((count + 1, unresolved))
}
RefResolution::CrossFile(uref) => {
unresolved.push(uref);
Ok((count, unresolved))
}
}
},
)?;
Ok(SameFileResult {
dep_count,
unresolved_refs,
})
}