Skip to content

Commit

Permalink
[scarb-doc] Add initial crate structures (#1309)
Browse files Browse the repository at this point in the history
Next PR will add nested attributes to the structures:
- struct members
- enum variants
- impl items
- trait items
  • Loading branch information
drknzz authored Jun 4, 2024
1 parent 43f76f4 commit 0a36ebc
Show file tree
Hide file tree
Showing 6 changed files with 573 additions and 199 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions extensions/scarb-doc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ clap.workspace = true
cairo-lang-compiler.workspace = true
cairo-lang-defs.workspace = true
cairo-lang-semantic.workspace = true
cairo-lang-syntax.workspace = true
cairo-lang-filesystem.workspace = true
cairo-lang-utils.workspace = true
scarb-metadata = { path = "../../scarb-metadata" }
Expand Down
137 changes: 137 additions & 0 deletions extensions/scarb-doc/src/compilation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use scarb_metadata::{
CompilationUnitComponentMetadata, CompilationUnitMetadata, Metadata, PackageId, PackageMetadata,
};
use smol_str::{SmolStr, ToSmolStr};
use std::path::PathBuf;

use cairo_lang_compiler::project::{AllCratesConfig, ProjectConfig, ProjectConfigContent};
use cairo_lang_filesystem::cfg::{Cfg, CfgSet};
use cairo_lang_filesystem::db::{CrateSettings, Edition, ExperimentalFeaturesConfig};
use cairo_lang_filesystem::ids::Directory;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;

const LIB_TARGET_KIND: &str = "lib";
const CORELIB_CRATE_NAME: &str = "core";

pub fn get_project_config(
metadata: &Metadata,
package_metadata: &PackageMetadata,
) -> ProjectConfig {
let compilation_unit_metadata =
package_lib_compilation_unit(metadata, package_metadata.id.clone())
.expect("failed to find compilation unit for package");
let corelib = get_corelib(compilation_unit_metadata);
let dependencies = get_dependencies(compilation_unit_metadata);
let crates_config = get_crates_config(metadata, compilation_unit_metadata);

ProjectConfig {
base_path: package_metadata.root.clone().into(),
corelib: Some(Directory::Real(corelib.source_root().into())),
content: ProjectConfigContent {
crate_roots: dependencies,
crates_config,
},
}
}

fn package_lib_compilation_unit(
metadata: &Metadata,
package_id: PackageId,
) -> Option<&CompilationUnitMetadata> {
metadata
.compilation_units
.iter()
.find(|m| m.package == package_id && m.target.kind == LIB_TARGET_KIND)
}

fn get_corelib(
compilation_unit_metadata: &CompilationUnitMetadata,
) -> &CompilationUnitComponentMetadata {
compilation_unit_metadata
.components
.iter()
.find(|du| du.name == CORELIB_CRATE_NAME)
.expect("Corelib could not be found")
}

fn get_dependencies(
compilation_unit_metadata: &CompilationUnitMetadata,
) -> OrderedHashMap<SmolStr, PathBuf> {
compilation_unit_metadata
.components
.iter()
.filter(|du| du.name != CORELIB_CRATE_NAME)
.map(|cu| {
(
cu.name.to_smolstr(),
cu.source_root().to_owned().into_std_path_buf(),
)
})
.collect()
}

fn get_crates_config(
metadata: &Metadata,
compilation_unit_metadata: &CompilationUnitMetadata,
) -> AllCratesConfig {
let crates_config: OrderedHashMap<SmolStr, CrateSettings> = compilation_unit_metadata
.components
.iter()
.map(|component| {
let pkg = metadata.get_package(&component.package).unwrap_or_else(|| {
panic!(
"failed to find = {} package",
&component.package.to_string()
)
});
(
SmolStr::from(&component.name),
get_crate_settings_for_package(
pkg,
component.cfg.as_ref().map(|cfg_vec| build_cfg_set(cfg_vec)),
),
)
})
.collect();

AllCratesConfig {
override_map: crates_config,
..Default::default()
}
}

fn get_crate_settings_for_package(
package: &PackageMetadata,
cfg_set: Option<CfgSet>,
) -> CrateSettings {
let edition = package
.edition
.clone()
.map_or(Edition::default(), |edition| {
let edition_value = serde_json::Value::String(edition);
serde_json::from_value(edition_value).unwrap()
});

let experimental_features = ExperimentalFeaturesConfig {
negative_impls: package
.experimental_features
.contains(&String::from("negative_impls")),
coupons: package
.experimental_features
.contains(&String::from("coupons")),
};

CrateSettings {
edition,
cfg_set,
experimental_features,
}
}

fn build_cfg_set(cfg: &[scarb_metadata::Cfg]) -> CfgSet {
CfgSet::from_iter(cfg.iter().map(|cfg| {
serde_json::to_value(cfg)
.and_then(serde_json::from_value::<Cfg>)
.expect("Cairo's `Cfg` must serialize identically as Scarb Metadata's `Cfg`.")
}))
}
217 changes: 46 additions & 171 deletions extensions/scarb-doc/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
use anyhow::Result;
use cairo_lang_compiler::project::{AllCratesConfig, ProjectConfig, ProjectConfigContent};
use cairo_lang_filesystem::cfg::{Cfg, CfgSet};
use cairo_lang_filesystem::db::{CrateSettings, Edition, ExperimentalFeaturesConfig, FilesGroup};
use clap::Parser;
use scarb_metadata::{
CompilationUnitComponentMetadata, CompilationUnitMetadata, Metadata, MetadataCommand,
PackageId, PackageMetadata,
};

use scarb_metadata::MetadataCommand;
use scarb_ui::args::PackagesFilter;
use smol_str::{SmolStr, ToSmolStr};
use std::collections::HashMap;
use std::path::PathBuf;

use cairo_lang_compiler::db::RootDatabase;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use cairo_lang_filesystem::db::FilesGroup;
use cairo_lang_filesystem::ids::CrateLongId;

use cairo_lang_defs::db::DefsGroup;
use cairo_lang_defs::ids::{LookupItemId, ModuleItemId, NamedLanguageElementId};
use compilation::get_project_config;
use types::Crate;

use cairo_lang_filesystem::ids::{CrateLongId, Directory};
use cairo_lang_semantic::db::SemanticGroup;
mod compilation;
mod types;

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
Expand All @@ -28,6 +21,16 @@ struct Args {
packages_filter: PackagesFilter,
}

macro_rules! print_names {
($label:expr, $var:expr) => {
println!(
"{}: {:?}",
$label,
$var.iter().map(|x| &x.item_data.name).collect::<Vec<_>>()
);
};
}

fn main() -> Result<()> {
let args = Args::parse();

Expand All @@ -42,166 +45,38 @@ fn main() -> Result<()> {
b.build()?
};

let main_crate_id = db.intern_crate(CrateLongId::Real(package_metadata.name.into()));

let crate_modules = db.crate_modules(main_crate_id);

let mut item_documentation = HashMap::new();

for module_id in crate_modules.iter() {
let module_items = db.module_items(*module_id).unwrap();

for item in module_items.iter() {
let item_doc = db.get_item_documentation(LookupItemId::ModuleItem(*item));
item_documentation.insert(LookupItemId::ModuleItem(*item), item_doc);

if let ModuleItemId::Trait(trait_id) = *item {
let trait_items_names = db.trait_required_item_names(trait_id).unwrap();
let main_crate_id = db.intern_crate(CrateLongId::Real(package_metadata.name.clone().into()));
let crate_ = Crate::new(db, main_crate_id);

for trait_item_name in trait_items_names.into_iter() {
let trait_item_id = db
.trait_item_by_name(trait_id, trait_item_name)
.unwrap()
.unwrap();

let doc = db.get_item_documentation(LookupItemId::TraitItem(trait_item_id));
item_documentation.insert(LookupItemId::TraitItem(trait_item_id), doc);
}
}
}
}

for (item_id, doc) in item_documentation.iter() {
let name = match item_id {
LookupItemId::ModuleItem(item_id) => item_id.name(db),
LookupItemId::TraitItem(item_id) => item_id.name(db),
LookupItemId::ImplItem(item_id) => item_id.name(db),
};
println!("{:?}: {:?} -> {:?}", name, item_id, doc);
}
print_module(&crate_.root_module);

Ok(())
}

fn get_project_config(metadata: &Metadata, package_metadata: &PackageMetadata) -> ProjectConfig {
let compilation_unit_metadata =
package_lib_compilation_unit(metadata, package_metadata.id.clone())
.expect("failed to find compilation unit for package");
let corelib = get_corelib(compilation_unit_metadata);
let dependencies = get_dependencies(compilation_unit_metadata);
let crates_config = get_crates_config(metadata, compilation_unit_metadata);

ProjectConfig {
base_path: package_metadata.root.clone().into(),
corelib: Some(Directory::Real(corelib.source_root().into())),
content: ProjectConfigContent {
crate_roots: dependencies,
crates_config,
},
fn print_module(module: &types::Module) {
println!("Module: {}", module.full_path);
println!(
"Submodules : {:?}",
module
.submodules
.iter()
.map(|x| &x.full_path)
.collect::<Vec<_>>()
);
print_names!("Constants ", module.constants);
print_names!("Uses ", module.uses);
print_names!("Free Functions ", module.free_functions);
print_names!("Structs ", module.structs);
print_names!("Enums ", module.enums);
print_names!("Type Aliases ", module.type_aliases);
print_names!("Impl Aliases ", module.impl_aliases);
print_names!("Traits ", module.traits);
print_names!("Impls ", module.impls);
print_names!("Extern Types ", module.extern_types);
print_names!("Extern Functions", module.extern_functions);

for submodule in &module.submodules {
println!();
print_module(submodule);
}
}

fn package_lib_compilation_unit(
metadata: &Metadata,
package_id: PackageId,
) -> Option<&CompilationUnitMetadata> {
metadata
.compilation_units
.iter()
.find(|m| m.package == package_id && m.target.kind == LIB_TARGET_KIND)
}

fn get_corelib(
compilation_unit_metadata: &CompilationUnitMetadata,
) -> &CompilationUnitComponentMetadata {
compilation_unit_metadata
.components
.iter()
.find(|du| du.name == CORELIB_CRATE_NAME)
.expect("Corelib could not be found")
}

fn get_dependencies(
compilation_unit_metadata: &CompilationUnitMetadata,
) -> OrderedHashMap<SmolStr, PathBuf> {
compilation_unit_metadata
.components
.iter()
.filter(|du| du.name != CORELIB_CRATE_NAME)
.map(|cu| {
(
cu.name.to_smolstr(),
cu.source_root().to_owned().into_std_path_buf(),
)
})
.collect()
}

fn get_crates_config(
metadata: &Metadata,
compilation_unit_metadata: &CompilationUnitMetadata,
) -> AllCratesConfig {
let crates_config: OrderedHashMap<SmolStr, CrateSettings> = compilation_unit_metadata
.components
.iter()
.map(|component| {
let pkg = metadata.get_package(&component.package).unwrap_or_else(|| {
panic!(
"failed to find = {} package",
&component.package.to_string()
)
});
(
SmolStr::from(&component.name),
get_crate_settings_for_package(
pkg,
component.cfg.as_ref().map(|cfg_vec| build_cfg_set(cfg_vec)),
),
)
})
.collect();

AllCratesConfig {
override_map: crates_config,
..Default::default()
}
}

fn get_crate_settings_for_package(
package: &PackageMetadata,
cfg_set: Option<CfgSet>,
) -> CrateSettings {
let edition = package
.edition
.clone()
.map_or(Edition::default(), |edition| {
let edition_value = serde_json::Value::String(edition);
serde_json::from_value(edition_value).unwrap()
});

let experimental_features = ExperimentalFeaturesConfig {
negative_impls: package
.experimental_features
.contains(&String::from("negative_impls")),
coupons: package
.experimental_features
.contains(&String::from("coupons")),
};

CrateSettings {
edition,
cfg_set,
experimental_features,
}
}

fn build_cfg_set(cfg: &[scarb_metadata::Cfg]) -> CfgSet {
CfgSet::from_iter(cfg.iter().map(|cfg| {
serde_json::to_value(cfg)
.and_then(serde_json::from_value::<Cfg>)
.expect("Cairo's `Cfg` must serialize identically as Scarb Metadata's `Cfg`.")
}))
}

const LIB_TARGET_KIND: &str = "lib";
const CORELIB_CRATE_NAME: &str = "core";
Loading

0 comments on commit 0a36ebc

Please sign in to comment.