artistpath_core/pathfinding/
utils.rs

1use crate::pathfinding_config::PathfindingConfig;
2use byteorder::{LittleEndian, ReadBytesExt};
3use memmap2::Mmap;
4use rustc_hash::FxHashMap;
5use std::{
6    fs::File,
7    io::{Cursor, Read},
8    path::Path,
9};
10use uuid::Uuid;
11
12pub type PathStep = (Uuid, f32);
13pub type PathResult = (Option<Vec<PathStep>>, usize, f64);
14
15#[derive(Debug, Clone)]
16pub enum EnhancedPathResult {
17    Success {
18        primary_path: Vec<PathStep>,
19        related_artists: FxHashMap<Uuid, (f32, usize)>,
20        connections: FxHashMap<Uuid, Vec<(Uuid, f32)>>,
21        artists_visited: usize,
22        duration_ms: u64,
23    },
24    PathTooLong {
25        primary_path: Vec<PathStep>,
26        path_length: usize,
27        minimum_budget_needed: usize,
28        artists_visited: usize,
29        duration_ms: u64,
30    },
31    NoPath {
32        artists_visited: usize,
33        duration_ms: u64,
34    },
35}
36
37pub fn open_memory_mapped_file(file_path: &Path) -> Result<Mmap, std::io::Error> {
38    let file = File::open(file_path)?;
39    unsafe { Mmap::map(&file) }
40}
41
42pub fn get_artist_connections(
43    artist_id: Uuid,
44    graph_data: &Mmap,
45    graph_index: &FxHashMap<Uuid, u64>,
46    config: &PathfindingConfig,
47) -> Vec<(Uuid, f32)> {
48    let file_position = match find_artist_position(artist_id, graph_index) {
49        Some(pos) => pos,
50        None => return vec![],
51    };
52    
53    if file_position >= graph_data.len() {
54        return vec![];
55    }
56    
57    let artist_data_cursor = create_cursor_at_position(graph_data, file_position);
58    
59    match parse_artist_connections(artist_id, artist_data_cursor) {
60        Ok(raw_connections) => filter_and_sort_connections(raw_connections, config),
61        Err(_) => vec![],
62    }
63}
64
65fn find_artist_position(artist_id: Uuid, graph_index: &FxHashMap<Uuid, u64>) -> Option<usize> {
66    graph_index.get(&artist_id).map(|&pos| pos as usize)
67}
68
69fn create_cursor_at_position(graph_data: &Mmap, position: usize) -> Cursor<&[u8]> {
70    Cursor::new(&graph_data[position..])
71}
72
73fn parse_artist_connections(expected_artist_id: Uuid, mut cursor: Cursor<&[u8]>) -> Result<Vec<(Uuid, f32)>, std::io::Error> {
74    let stored_artist_id = read_uuid_from_cursor(&mut cursor)?;
75    
76    if stored_artist_id != expected_artist_id {
77        return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "Artist ID mismatch"));
78    }
79    
80    let connection_count = cursor.read_u32::<LittleEndian>()? as usize;
81    let mut connections = Vec::with_capacity(connection_count);
82    
83    for _ in 0..connection_count {
84        let connected_artist_id = read_uuid_from_cursor(&mut cursor)?;
85        let similarity_score = cursor.read_f32::<LittleEndian>()?;
86        connections.push((connected_artist_id, similarity_score));
87    }
88    
89    Ok(connections)
90}
91
92fn read_uuid_from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Uuid, std::io::Error> {
93    let mut uuid_bytes = [0u8; 16];
94    cursor.read_exact(&mut uuid_bytes)?;
95    Ok(Uuid::from_bytes(uuid_bytes))
96}
97
98fn filter_and_sort_connections(mut connections: Vec<(Uuid, f32)>, config: &PathfindingConfig) -> Vec<(Uuid, f32)> {
99    if config.min_match > 0.0 {
100        connections.retain(|(_, similarity)| *similarity >= config.min_match);
101    }
102    
103    connections.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
104    connections.truncate(config.top_related);
105    
106    connections
107}
108
109pub fn reconstruct_path(
110    parent_map: &FxHashMap<Uuid, (Uuid, f32)>,
111    start: Uuid,
112    target: Uuid,
113) -> Vec<PathStep> {
114    let mut path = Vec::new();
115    let mut current_node = target;
116    
117    while current_node != start {
118        let (parent_node, similarity) = parent_map[&current_node];
119        path.push((current_node, similarity));
120        current_node = parent_node;
121    }
122    
123    path.push((start, 0.0));
124    path.reverse();
125    path
126}