export const STABLE_CRATES = new Set([
'std', 'core', 'alloc', 'serde', 'tokio', 'thiserror', 'anyhow',
'log', 'tracing', 'clap', 'syn', 'quote', 'proc_macro2', 'rayon',
'regex', 'chrono', 'uuid', 'rand', 'futures', 'async_trait',
'bytes', 'http', 'hyper', 'axum', 'tower', 'tonic', 'prost',
'sqlx', 'diesel'
]);
export function isExternalCrate(target) {
const parts = target.split('::');
if (parts.length < 2) return false;
const crateName = parts[0].toLowerCase();
return STABLE_CRATES.has(crateName) ||
(!target.startsWith('crate::') &&
!target.startsWith('self::') &&
!target.startsWith('super::'));
}
export function estimateVolatility(target, explicitVolatility) {
if (explicitVolatility && explicitVolatility !== 'Unknown') {
return explicitVolatility;
}
const parts = target.split('::');
const crateName = parts[0].replace('cargo-coupling::', '').toLowerCase();
if (STABLE_CRATES.has(crateName)) return 'Low';
if (parts.length > 1 && !target.startsWith('crate::')) return 'Medium';
return explicitVolatility || 'Medium';
}
export function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
export function downloadDataUrl(dataUrl, filename) {
const link = document.createElement('a');
link.download = filename;
link.href = dataUrl;
link.click();
}
export function downloadText(text, filename, mimeType = 'application/json') {
const blob = new Blob([text], { type: mimeType });
const url = URL.createObjectURL(blob);
downloadDataUrl(url, filename);
URL.revokeObjectURL(url);
}
export function getHealthColor(health) {
switch (health) {
case 'good': return '#22c55e';
case 'acceptable': return '#eab308';
case 'needs_review': return '#f97316';
case 'critical': return '#ef4444';
default: return '#6b7280';
}
}
export function getStrengthColor(strength) {
switch (strength) {
case 'Intrusive': return '#ef4444';
case 'Functional': return '#f97316';
case 'Model': return '#eab308';
case 'Contract': return '#22c55e';
default: return '#6b7280';
}
}
export function formatModulePath(path) {
if (!path) return '';
const parts = path.split('::');
return parts[parts.length - 1] || path;
}
export function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}