cairo_lang_compiler/
project.rs1use std::ffi::OsStr;
2use std::path::Path;
3
4use cairo_lang_defs::ids::ModuleId;
5use cairo_lang_filesystem::db::{
6 CORELIB_CRATE_NAME, CrateConfiguration, CrateIdentifier, CrateSettings, FilesGroupEx,
7};
8use cairo_lang_filesystem::ids::{CrateId, CrateLongId, Directory};
9pub use cairo_lang_project::*;
10use cairo_lang_semantic::db::SemanticGroup;
11use cairo_lang_utils::Intern;
12
13#[derive(thiserror::Error, Debug)]
14pub enum ProjectError {
15 #[error("Only files with .cairo extension can be compiled.")]
16 BadFileExtension,
17 #[error("Couldn't read {path}: No such file.")]
18 NoSuchFile { path: String },
19 #[error("Couldn't handle {path}: Not a legal path.")]
20 BadPath { path: String },
21 #[error("Failed to load project config: {0}")]
22 LoadProjectError(DeserializationError),
23}
24
25pub fn setup_single_file_project(
28 db: &mut dyn SemanticGroup,
29 path: &Path,
30) -> Result<CrateId, ProjectError> {
31 match path.extension().and_then(OsStr::to_str) {
32 Some("cairo") => (),
33 _ => {
34 return Err(ProjectError::BadFileExtension);
35 }
36 }
37 if !path.exists() {
38 return Err(ProjectError::NoSuchFile { path: path.to_string_lossy().to_string() });
39 }
40 let bad_path_err = || ProjectError::BadPath { path: path.to_string_lossy().to_string() };
41 let canonical = path.canonicalize().map_err(|_| bad_path_err())?;
42 let file_dir = canonical.parent().ok_or_else(bad_path_err)?;
43 let file_stem = path.file_stem().and_then(OsStr::to_str).ok_or_else(bad_path_err)?;
44 if file_stem == "lib" {
45 let crate_name = file_dir.to_str().ok_or_else(bad_path_err)?;
46 let crate_id = CrateId::plain(db, crate_name);
47 db.set_crate_config(
48 crate_id,
49 Some(CrateConfiguration::default_for_root(Directory::Real(file_dir.to_path_buf()))),
50 );
51 Ok(crate_id)
52 } else {
53 let crate_id = CrateId::plain(db, file_stem);
55 db.set_crate_config(
56 crate_id,
57 Some(CrateConfiguration::default_for_root(Directory::Real(file_dir.to_path_buf()))),
58 );
59
60 let module_id = ModuleId::CrateRoot(crate_id);
61 let file_id = db.module_main_file(module_id).unwrap();
62 db.override_file_content(file_id, Some(format!("mod {file_stem};").into()));
63 Ok(crate_id)
64 }
65}
66
67pub fn update_crate_roots_from_project_config(db: &mut dyn SemanticGroup, config: &ProjectConfig) {
69 for (crate_identifier, directory_path) in config.content.crate_roots.iter() {
70 let root = Directory::Real(config.absolute_crate_root(directory_path));
71 update_crate_root(db, config, crate_identifier, root);
72 }
73}
74
75pub fn update_crate_root(
79 db: &mut dyn SemanticGroup,
80 config: &ProjectConfig,
81 crate_identifier: &CrateIdentifier,
82 root: Directory,
83) {
84 let (crate_id, crate_settings) = get_crate_id_and_settings(db, crate_identifier, config);
85 db.set_crate_config(
86 crate_id,
87 Some(CrateConfiguration { root, settings: crate_settings.clone(), cache_file: None }),
88 );
89}
90
91pub fn setup_project(
95 db: &mut dyn SemanticGroup,
96 path: &Path,
97) -> Result<Vec<CrateId>, ProjectError> {
98 if path.is_dir() {
99 let config = ProjectConfig::from_directory(path).map_err(ProjectError::LoadProjectError)?;
100 let main_crate_ids = get_main_crate_ids_from_project(db, &config);
101 update_crate_roots_from_project_config(db, &config);
102 Ok(main_crate_ids)
103 } else {
104 Ok(vec![setup_single_file_project(db, path)?])
105 }
106}
107
108pub fn check_compiler_path(single_file: bool, path: &Path) -> anyhow::Result<()> {
110 if path.is_file() {
111 if !single_file {
112 anyhow::bail!("The given path is a file, but --single-file was not supplied.");
113 }
114 } else if path.is_dir() {
115 if single_file {
116 anyhow::bail!("The given path is a directory, but --single-file was supplied.");
117 }
118 } else {
119 anyhow::bail!("The given path does not exist.");
120 }
121 Ok(())
122}
123
124pub fn get_main_crate_ids_from_project(
125 db: &mut dyn SemanticGroup,
126 config: &ProjectConfig,
127) -> Vec<CrateId> {
128 config
129 .content
130 .crate_roots
131 .keys()
132 .map(|crate_identifier| get_crate_id_and_settings(db, crate_identifier, config).0)
133 .collect()
134}
135
136fn get_crate_id_and_settings<'a>(
137 db: &mut dyn SemanticGroup,
138 crate_identifier: &CrateIdentifier,
139 config: &'a ProjectConfig,
140) -> (CrateId, &'a CrateSettings) {
141 let crate_settings = config.content.crates_config.get(crate_identifier);
142 let name = crate_settings.name.clone().unwrap_or_else(|| crate_identifier.clone().into());
143 let discriminator =
145 if name == CORELIB_CRATE_NAME { None } else { Some(crate_identifier.clone().into()) };
146
147 let crate_id = CrateLongId::Real { name, discriminator }.intern(db);
148
149 (crate_id, crate_settings)
150}