1use std::collections::BTreeMap;
2use std::path::PathBuf;
3use std::sync::Arc;
4
5use cairo_lang_utils::{Intern, LookupIntern, define_short_id};
6use path_clean::PathClean;
7use serde::{Deserialize, Serialize};
8use smol_str::SmolStr;
9
10use crate::db::{CORELIB_CRATE_NAME, FilesGroup};
11use crate::span::{TextOffset, TextSpan};
12
13pub const CAIRO_FILE_EXTENSION: &str = "cairo";
14
15#[derive(Clone, Debug, Hash, PartialEq, Eq)]
17pub enum CrateLongId {
18 Real { name: SmolStr, discriminator: Option<SmolStr> },
20 Virtual { name: SmolStr, file_id: FileId, settings: String, cache_file: Option<BlobId> },
22}
23impl CrateLongId {
24 pub fn name(&self) -> SmolStr {
25 match self {
26 CrateLongId::Real { name, .. } | CrateLongId::Virtual { name, .. } => name.clone(),
27 }
28 }
29}
30define_short_id!(CrateId, CrateLongId, FilesGroup, lookup_intern_crate, intern_crate);
31impl CrateId {
32 pub fn plain(
34 db: &(impl cairo_lang_utils::Upcast<dyn FilesGroup> + ?Sized),
35 name: &str,
36 ) -> Self {
37 CrateLongId::Real { name: name.into(), discriminator: None }.intern(db)
38 }
39
40 pub fn core(db: &(impl cairo_lang_utils::Upcast<dyn FilesGroup> + ?Sized)) -> Self {
42 CrateLongId::Real { name: CORELIB_CRATE_NAME.into(), discriminator: None }.intern(db)
43 }
44
45 pub fn name(&self, db: &dyn FilesGroup) -> SmolStr {
46 self.lookup_intern(db).name()
47 }
48}
49
50pub trait UnstableSalsaId {
55 fn get_internal_id(&self) -> &salsa::InternId;
56}
57impl UnstableSalsaId for CrateId {
58 fn get_internal_id(&self) -> &salsa::InternId {
59 &self.0
60 }
61}
62
63#[derive(Clone, Debug, Hash, PartialEq, Eq)]
65pub struct FlagLongId(pub SmolStr);
66define_short_id!(FlagId, FlagLongId, FilesGroup, lookup_intern_flag, intern_flag);
67impl FlagId {
68 pub fn new(db: &dyn FilesGroup, name: &str) -> Self {
69 FlagLongId(name.into()).intern(db)
70 }
71}
72
73#[derive(Clone, Debug, Hash, PartialEq, Eq)]
76pub enum FileLongId {
77 OnDisk(PathBuf),
78 Virtual(VirtualFile),
79 External(salsa::InternId),
80}
81#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
83pub enum FileKind {
84 Module,
85 Expr,
86 StatementList,
87}
88
89#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
91pub struct CodeMapping {
92 pub span: TextSpan,
93 pub origin: CodeOrigin,
94}
95impl CodeMapping {
96 pub fn translate(&self, span: TextSpan) -> Option<TextSpan> {
97 if self.span.contains(span) {
98 Some(match self.origin {
99 CodeOrigin::Start(origin_start) => {
100 let start = origin_start.add_width(span.start - self.span.start);
101 TextSpan { start, end: start.add_width(span.width()) }
102 }
103 CodeOrigin::Span(span) => span,
104 CodeOrigin::CallSite(span) => span,
105 })
106 } else {
107 None
108 }
109 }
110}
111
112#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
114pub enum CodeOrigin {
115 Start(TextOffset),
117 Span(TextSpan),
119 CallSite(TextSpan),
122}
123
124impl CodeOrigin {
125 pub fn as_span(&self) -> Option<TextSpan> {
126 match self {
127 CodeOrigin::Start(_) => None,
128 CodeOrigin::CallSite(_) => None,
129 CodeOrigin::Span(span) => Some(*span),
130 }
131 }
132
133 pub fn start(&self) -> TextOffset {
134 match self {
135 CodeOrigin::Start(start) => *start,
136 CodeOrigin::CallSite(span) => span.start,
137 CodeOrigin::Span(span) => span.start,
138 }
139 }
140}
141
142#[derive(Clone, Debug, Hash, PartialEq, Eq)]
143pub struct VirtualFile {
144 pub parent: Option<FileId>,
145 pub name: SmolStr,
146 pub content: Arc<str>,
147 pub code_mappings: Arc<[CodeMapping]>,
148 pub kind: FileKind,
149 pub original_item_removed: bool,
153}
154impl VirtualFile {
155 fn full_path(&self, db: &dyn FilesGroup) -> String {
156 if let Some(parent) = self.parent {
157 format!("{}[{}]", parent.full_path(db), self.name)
159 } else {
160 self.name.clone().into()
161 }
162 }
163}
164
165define_short_id!(FileId, FileLongId, FilesGroup, lookup_intern_file, intern_file);
166impl FileId {
167 pub fn new(db: &dyn FilesGroup, path: PathBuf) -> FileId {
168 FileLongId::OnDisk(path.clean()).intern(db)
169 }
170 pub fn file_name(self, db: &dyn FilesGroup) -> String {
171 match self.lookup_intern(db) {
172 FileLongId::OnDisk(path) => {
173 path.file_name().and_then(|x| x.to_str()).unwrap_or("<unknown>").to_string()
174 }
175 FileLongId::Virtual(vf) => vf.name.to_string(),
176 FileLongId::External(external_id) => db.ext_as_virtual(external_id).name.to_string(),
177 }
178 }
179 pub fn full_path(self, db: &dyn FilesGroup) -> String {
180 match self.lookup_intern(db) {
181 FileLongId::OnDisk(path) => path.to_str().unwrap_or("<unknown>").to_string(),
182 FileLongId::Virtual(vf) => vf.full_path(db),
183 FileLongId::External(external_id) => db.ext_as_virtual(external_id).full_path(db),
184 }
185 }
186 pub fn kind(self, db: &dyn FilesGroup) -> FileKind {
187 match self.lookup_intern(db) {
188 FileLongId::OnDisk(_) => FileKind::Module,
189 FileLongId::Virtual(vf) => vf.kind,
190 FileLongId::External(_) => FileKind::Module,
191 }
192 }
193}
194
195#[derive(Clone, Debug, Hash, PartialEq, Eq)]
196pub enum Directory {
197 Real(PathBuf),
199 Virtual { files: BTreeMap<SmolStr, FileId>, dirs: BTreeMap<SmolStr, Box<Directory>> },
201}
202
203impl Directory {
204 pub fn file(&self, db: &dyn FilesGroup, name: SmolStr) -> FileId {
207 match self {
208 Directory::Real(path) => FileId::new(db, path.join(name.as_str())),
209 Directory::Virtual { files, dirs: _ } => files
210 .get(&name)
211 .copied()
212 .unwrap_or_else(|| FileId::new(db, PathBuf::from(name.as_str()))),
213 }
214 }
215
216 pub fn subdir(&self, name: SmolStr) -> Directory {
219 match self {
220 Directory::Real(path) => Directory::Real(path.join(name.as_str())),
221 Directory::Virtual { files: _, dirs } => {
222 if let Some(dir) = dirs.get(&name) {
223 dir.as_ref().clone()
224 } else {
225 Directory::Virtual { files: BTreeMap::new(), dirs: BTreeMap::new() }
226 }
227 }
228 }
229 }
230}
231
232#[derive(Clone, Debug, Hash, PartialEq, Eq)]
234pub enum BlobLongId {
235 OnDisk(PathBuf),
236 Virtual(Arc<[u8]>),
237}
238
239define_short_id!(BlobId, BlobLongId, FilesGroup, lookup_intern_blob, intern_blob);
240
241impl BlobId {
242 pub fn new(db: &dyn FilesGroup, path: PathBuf) -> BlobId {
243 BlobLongId::OnDisk(path.clean()).intern(db)
244 }
245}