use crate::{AsName, InFile, Name};
use mun_syntax::ast;
use mun_syntax::ast::{NameOwner, PathSegmentKind};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Path {
pub kind: PathKind,
pub segments: Vec<Name>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PathKind {
Plain,
Super(u8),
Package,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImportAlias {
Underscore,
Alias(Name),
}
impl Path {
pub fn from_ast(mut path: ast::Path) -> Option<Path> {
let mut kind = PathKind::Plain;
let mut segments = Vec::new();
loop {
let segment = path.segment()?;
match segment.kind()? {
ast::PathSegmentKind::Name(name) => {
segments.push(name.as_name());
}
ast::PathSegmentKind::SelfKw => {
kind = PathKind::Super(0);
break;
}
ast::PathSegmentKind::SuperKw => {
kind = PathKind::Super(1);
break;
}
ast::PathSegmentKind::PackageKw => {
kind = PathKind::Package;
break;
}
}
path = match path.qualifier() {
Some(p) => p,
None => break,
}
}
segments.reverse();
Some(Path { kind, segments })
}
pub fn from_name_ref(name_ref: &ast::NameRef) -> Path {
name_ref.as_name().into()
}
pub fn is_ident(&self) -> bool {
self.kind == PathKind::Plain && self.segments.len() == 1
}
pub fn as_ident(&self) -> Option<&Name> {
if self.is_ident() {
return self.segments.first();
}
None
}
pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> Path {
let segments = segments.into_iter().collect::<Vec<_>>();
Path { kind, segments }
}
pub(crate) fn expand_use_item(
item_src: InFile<ast::Use>,
mut cb: impl FnMut(Path, &ast::UseTree, /* is_glob */ bool, Option<ImportAlias>),
) {
if let Some(tree) = item_src.value.use_tree() {
lower_use_tree(None, &tree, &mut cb);
}
}
}
fn lower_use_tree(
prefix: Option<Path>,
tree: &ast::UseTree,
cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option<ImportAlias>),
) {
if let Some(use_tree_list) = tree.use_tree_list() {
let prefix = match tree.path() {
None => prefix,
Some(path) => convert_path(prefix, &path),
};
for child_tree in use_tree_list.use_trees() {
lower_use_tree(prefix.clone(), &child_tree, cb);
}
} else {
let alias = tree.rename().map(|a| {
a.name()
.map(|it| it.as_name())
.map_or(ImportAlias::Underscore, ImportAlias::Alias)
});
let is_glob = tree.has_star_token();
if let Some(ast_path) = tree.path() {
if ast_path.qualifier().is_none() {
if let Some(segment) = ast_path.segment() {
if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
if let Some(prefix) = prefix {
cb(prefix, tree, false, alias);
return;
}
}
}
}
if let Some(path) = convert_path(prefix, &ast_path) {
cb(path, tree, is_glob, alias)
}
} else if is_glob {
if let Some(prefix) = prefix {
cb(prefix, tree, is_glob, None)
}
}
}
}
fn convert_path(prefix: Option<Path>, path: &ast::Path) -> Option<Path> {
let prefix = if let Some(qualifier) = path.qualifier() {
Some(convert_path(prefix, &qualifier)?)
} else {
prefix
};
let segment = path.segment()?;
let res = match segment.kind()? {
ast::PathSegmentKind::Name(name_ref) => {
let mut res = prefix.unwrap_or_else(|| Path {
kind: PathKind::Plain,
segments: Vec::with_capacity(1),
});
res.segments.push(name_ref.as_name());
res
}
ast::PathSegmentKind::PackageKw => {
if prefix.is_some() {
return None;
}
Path::from_segments(PathKind::Package, std::iter::empty())
}
PathSegmentKind::SelfKw => {
if prefix.is_some() {
return None;
}
Path::from_segments(PathKind::Super(0), std::iter::empty())
}
PathSegmentKind::SuperKw => {
if prefix.is_some() {
return None;
}
Path::from_segments(PathKind::Super(1), std::iter::empty())
}
};
Some(res)
}
impl From<Name> for Path {
fn from(name: Name) -> Path {
Path {
kind: PathKind::Plain,
segments: vec![name],
}
}
}