seam-skeleton 0.5.38

HTML skeleton extraction pipeline for SeamJS CLI
Documentation
/* src/cli/skeleton/src/extract/variant.rs */

use std::collections::HashSet;

use super::super::Axis;
use super::combo::generate_combos;

/// Find a pair of variant indices that differ only in the given axis.
/// Returns (index_for_first_value, index_for_second_value).
pub(super) fn find_pair_for_axis(
	axes: &[Axis],
	variant_count: usize,
	target_axis: usize,
) -> Option<(usize, usize)> {
	let axis = &axes[target_axis];
	if axis.values.len() < 2 {
		return None;
	}

	let combos = generate_combos(axes);
	let first_val = &axis.values[0];
	let second_val = &axis.values[1];

	for (i, combo_a) in combos.iter().enumerate() {
		for (j, combo_b) in combos.iter().enumerate() {
			if i == j || i >= variant_count || j >= variant_count {
				continue;
			}
			let mut differs_only_in_target = true;
			for (k, (va, vb)) in combo_a.iter().zip(combo_b.iter()).enumerate() {
				if k == target_axis {
					if va != first_val || vb != second_val {
						differs_only_in_target = false;
						break;
					}
				} else if va != vb {
					differs_only_in_target = false;
					break;
				}
			}
			if differs_only_in_target {
				return Some((i, j));
			}
		}
	}

	None
}

/// Find one representative variant index per enum value (other axes at reference).
pub(super) fn find_enum_group_for_axis(
	axes: &[Axis],
	variant_count: usize,
	target_axis: usize,
) -> Vec<(String, usize)> {
	let axis = &axes[target_axis];
	let combos = generate_combos(axes);
	let mut result = Vec::new();

	let reference_combo = if combos.is_empty() { return result } else { &combos[0] };

	for value in &axis.values {
		let val_str = match value {
			serde_json::Value::String(s) => s.clone(),
			other => other.to_string(),
		};

		for (i, combo) in combos.iter().enumerate() {
			if i >= variant_count {
				break;
			}
			let mut matches = true;
			for (k, v) in combo.iter().enumerate() {
				if k == target_axis {
					if v != value {
						matches = false;
						break;
					}
				} else if v != &reference_combo[k] {
					matches = false;
					break;
				}
			}
			if matches {
				result.push((val_str, i));
				break;
			}
		}
	}

	result
}

/// Find ALL variant indices for each enum value (other axes vary freely).
pub(super) fn find_enum_all_variants_for_axis(
	axes: &[Axis],
	variant_count: usize,
	target_axis: usize,
) -> Vec<(String, Vec<usize>)> {
	let axis = &axes[target_axis];
	let combos = generate_combos(axes);
	let mut result = Vec::new();

	for value in &axis.values {
		let val_str = match value {
			serde_json::Value::String(s) => s.clone(),
			other => other.to_string(),
		};
		let indices: Vec<usize> = combos
			.iter()
			.enumerate()
			.filter(|&(i, _)| i < variant_count)
			.filter(|(_, combo)| &combo[target_axis] == value)
			.map(|(i, _)| i)
			.collect();
		result.push((val_str, indices));
	}

	result
}

/// Find variant indices where the parent array is populated,
/// non-child top-level axes match the reference combo, and child axes vary freely.
pub(super) fn find_scoped_variant_indices(
	axes: &[Axis],
	variant_count: usize,
	parent_axis_idx: usize,
	children: &[usize],
) -> Vec<usize> {
	let combos = generate_combos(axes);
	let reference = if combos.is_empty() { return Vec::new() } else { &combos[0] };
	let child_set: HashSet<usize> = children.iter().copied().collect();

	combos
		.iter()
		.enumerate()
		.filter(|&(i, _)| i < variant_count)
		.filter(|(_, combo)| {
			combo.iter().enumerate().all(|(k, v)| {
				if k == parent_axis_idx {
					// Must be populated (first value)
					v == &axes[parent_axis_idx].values[0]
				} else if child_set.contains(&k) {
					true
				} else {
					v == &reference[k]
				}
			})
		})
		.map(|(i, _)| i)
		.collect()
}