1use std::ops::Range;
2
3use dada_util::SalsaSerialize;
4use serde::Serialize;
5use url::Url;
6
7use crate::{
8 ast::Identifier,
9 span::{AbsoluteOffset, AbsoluteSpan, Anchor, Offset, Span, Spanned, ZeroColumn, ZeroLine},
10};
11
12#[derive(SalsaSerialize)]
13#[salsa::input]
14pub struct CompilationRoot {
15 #[return_ref]
16 pub crates: Vec<Krate>,
17}
18
19impl CompilationRoot {
20 pub fn crate_source<'db>(
21 self,
22 db: &'db dyn crate::Db,
23 crate_name: Identifier<'db>,
24 ) -> Option<Krate> {
25 #[salsa::tracked]
26 fn inner<'db>(
27 db: &'db dyn crate::Db,
28 root: CompilationRoot,
29 crate_name: Identifier<'db>,
30 ) -> Option<Krate> {
31 let crate_name = crate_name.text(db);
32 root.crates(db)
33 .iter()
34 .find(|c| c.name(db) == crate_name)
35 .copied()
36 }
37
38 inner(db, self, crate_name)
39 }
40
41 pub fn libdada_crate(self, db: &dyn crate::Db) -> Krate {
44 #[salsa::tracked]
45 fn inner<'db>(db: &'db dyn crate::Db, root: CompilationRoot) -> Krate {
46 root.crate_source(db, Identifier::dada(db))
47 .expect("libdada crate not found")
48 }
49
50 inner(db, self)
51 }
52}
53
54#[derive(SalsaSerialize)]
55#[salsa::input]
56pub struct Krate {
57 #[return_ref]
58 pub name: String,
59}
60
61#[salsa::input(debug)]
62pub struct SourceFile {
63 #[return_ref]
64 pub url: Url,
65
66 #[return_ref]
68 pub contents: Result<String, String>,
69}
70
71impl serde::Serialize for SourceFile {
72 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
73 where
74 S: serde::Serializer,
75 {
76 salsa::with_attached_database(|db| {
77 #[derive(Serialize)]
78 struct SourceFileExport<'a> {
79 url: &'a Url,
80 }
81
82 serde::Serialize::serialize(&SourceFileExport { url: self.url(db) }, serializer)
83 })
84 .unwrap_or_else(|| panic!("cannot serialize without an attached database"))
85 }
86}
87
88impl<'db> Spanned<'db> for SourceFile {
89 fn span(&self, db: &'db dyn crate::Db) -> crate::span::Span<'db> {
90 Span {
91 anchor: Anchor::SourceFile(*self),
92 start: Offset::ZERO,
93 end: Offset::from(self.contents_if_ok(db).len()),
94 }
95 }
96}
97
98#[salsa::tracked]
99impl SourceFile {
100 pub fn contents_if_ok(self, db: &dyn crate::Db) -> &str {
102 match self.contents(db) {
103 Ok(s) => s,
104 Err(_) => "",
105 }
106 }
107
108 pub fn absolute_span(self, db: &dyn crate::Db) -> AbsoluteSpan {
110 AbsoluteSpan {
111 source_file: self,
112 start: AbsoluteOffset::ZERO,
113 end: AbsoluteOffset::from(self.contents_if_ok(db).len()),
114 }
115 }
116
117 pub fn module_name(self, db: &dyn crate::Db) -> Identifier<'_> {
118 let url = self.url(db);
119 let module_name = url
120 .path_segments()
121 .and_then(|mut segments| segments.next_back())
122 .unwrap_or("<input>");
123 Identifier::new(db, module_name)
124 }
125
126 pub fn url_display(self, db: &dyn crate::Db) -> String {
127 db.url_display(self.url(db))
128 }
129
130 #[salsa::tracked(return_ref)]
134 pub fn line_starts(self, db: &dyn crate::Db) -> Vec<AbsoluteOffset> {
135 std::iter::once(0)
136 .chain(
137 self.contents_if_ok(db)
138 .char_indices()
139 .filter(|&(_, ch)| ch == '\n')
140 .map(|(index, _)| index + 1),
141 )
142 .chain(std::iter::once(self.contents_if_ok(db).len()))
143 .map(AbsoluteOffset::from)
144 .collect()
145 }
146
147 pub fn line_range(self, db: &dyn crate::Db, line: ZeroLine) -> Range<AbsoluteOffset> {
148 let line_starts = self.line_starts(db);
149 line_starts[line.as_usize()]..line_starts[line.as_usize() + 1]
150 }
151
152 pub fn line_col(self, db: &dyn crate::Db, offset: AbsoluteOffset) -> (ZeroLine, ZeroColumn) {
153 let line_starts = self.line_starts(db);
154 match line_starts.iter().position(|&s| s > offset) {
155 Some(next_line) => {
156 assert!(next_line > 0);
157 let line_index = next_line - 1;
158 let line_start = line_starts[line_index];
159 (
160 ZeroLine::from(line_index),
161 ZeroColumn::from(offset - line_start),
162 )
163 }
164 None => {
165 let last_line = self.line_starts(db).len() - 1;
167 let last_line_start = line_starts[last_line];
168 (
169 ZeroLine::from(last_line),
170 ZeroColumn::from(offset - last_line_start),
171 )
172 }
173 }
174 }
175}