1use dirs::home_dir;
2use std::{
3 collections::BTreeMap,
4 path::{Path, PathBuf},
5};
6use sway_types::{LineCol, SourceEngine};
7
8use serde::{Deserialize, Serialize};
9
10use sway_types::span::Span;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
14#[serde(transparent)]
15pub struct PathIndex(pub usize);
16
17#[derive(Debug, Clone, Default, Serialize, Deserialize)]
18pub struct SourceMap {
19 pub dependency_paths: Vec<PathBuf>,
22 pub paths: Vec<PathBuf>,
24 pub map: BTreeMap<usize, SourceMapSpan>,
27}
28impl SourceMap {
29 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn insert_dependency<P: AsRef<Path>>(&mut self, path: P) {
35 if let Some(home) = home_dir() {
36 let forc = home.join(".forc/");
37 if let Ok(unprefixed) = path.as_ref().strip_prefix(forc) {
38 self.dependency_paths.push(unprefixed.to_owned());
39 }
40 }
41 }
43
44 pub fn insert(&mut self, source_engine: &SourceEngine, pc: usize, span: &Span) {
45 if let Some(source_id) = span.source_id() {
46 let path = source_engine.get_path(source_id);
47 let path_index = self
48 .paths
49 .iter()
50 .position(|p| *p == *path)
51 .unwrap_or_else(|| {
52 self.paths.push((*path).to_owned());
53 self.paths.len() - 1
54 });
55 self.map.insert(
56 pc,
57 SourceMapSpan {
58 path: PathIndex(path_index),
59 range: LocationRange {
60 start: span.start_pos().line_col(),
61 end: span.end_pos().line_col(),
62 },
63 },
64 );
65 }
66 }
67
68 pub fn addr_to_span(&self, pc: usize) -> Option<(PathBuf, LocationRange)> {
70 self.map
71 .get(&pc)
72 .map(|sms| sms.to_span(&self.paths, &self.dependency_paths))
73 }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct SourceMapSpan {
78 pub path: PathIndex,
79 pub range: LocationRange,
80}
81
82impl SourceMapSpan {
83 pub fn to_span(
84 &self,
85 paths: &[PathBuf],
86 dependency_paths: &[PathBuf],
87 ) -> (PathBuf, LocationRange) {
88 let p = &paths[self.path.0];
89 for dep in dependency_paths {
90 if p.starts_with(dep.file_name().unwrap()) {
91 let mut path = home_dir().expect("Could not get homedir").join(".forc");
92
93 if let Some(dp) = dep.parent() {
94 path = path.join(dp);
95 }
96
97 return (path.join(p), self.range);
98 }
99 }
100
101 (p.to_owned(), self.range)
102 }
103}
104
105#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
106pub struct LocationRange {
107 pub start: LineCol,
108 pub end: LineCol,
109}