use core::fmt;
use crate as rune;
use crate::alloc::borrow::Cow;
use crate::alloc::path::Path;
use crate::alloc::prelude::*;
use crate::alloc::{self, Box, Vec};
use crate::ast;
use crate::ast::{Span, Spanned};
use crate::compile::attrs::Parser;
#[cfg(feature = "doc")]
use crate::compile::meta;
use crate::compile::{self, ItemId, Location, MetaInfo, ModId, Pool, Visibility};
use crate::module::{DocFunction, ModuleItemCommon};
use crate::parse::ResolveContext;
use crate::runtime::{Call, FieldMap, Protocol};
use crate::{Hash, Item, ItemBuf};
#[derive(Debug, TryClone, Clone, Copy)]
#[try_clone(copy)]
#[non_exhaustive]
pub struct MetaRef<'a> {
pub context: bool,
pub hash: Hash,
pub item: &'a Item,
pub kind: &'a Kind,
pub source: Option<&'a SourceMeta>,
}
#[derive(Debug, TryClone)]
#[non_exhaustive]
pub struct SourceMeta {
pub location: Location,
pub path: Option<Box<Path>>,
}
#[derive(Debug, TryClone, Clone, Copy, Spanned)]
#[try_clone(copy)]
pub(crate) struct Doc {
#[rune(span)]
pub(crate) span: Span,
pub(crate) doc_string: ast::LitStr,
}
impl Doc {
pub(crate) fn collect_from(
cx: ResolveContext<'_>,
attrs: &mut Parser,
attributes: &[ast::Attribute],
) -> compile::Result<Vec<Doc>> {
let docs = attrs
.parse_all::<crate::compile::attrs::Doc>(cx, attributes)?
.map(|result| {
result.map(|(span, doc)| Doc {
span: span.span(),
doc_string: doc.doc_string,
})
})
.try_collect::<compile::Result<_>>()??;
Ok(docs)
}
}
#[derive(Debug, TryClone)]
#[non_exhaustive]
pub(crate) struct Meta {
pub(crate) context: bool,
pub(crate) hash: Hash,
pub(crate) item_meta: ItemMeta,
pub(crate) kind: Kind,
pub(crate) source: Option<SourceMeta>,
pub(crate) parameters: Hash,
}
impl Meta {
pub(crate) fn info(&self, pool: &Pool) -> alloc::Result<MetaInfo> {
MetaInfo::new(&self.kind, self.hash, Some(pool.item(self.item_meta.item)))
}
pub(crate) fn as_meta_ref<'a>(&'a self, pool: &'a Pool) -> MetaRef<'a> {
MetaRef {
context: self.context,
hash: self.hash,
item: pool.item(self.item_meta.item),
kind: &self.kind,
source: self.source.as_ref(),
}
}
pub(crate) fn type_hash_of(&self) -> Option<Hash> {
match &self.kind {
Kind::Type { .. } => Some(self.hash),
Kind::Struct { .. } => Some(self.hash),
Kind::Enum { .. } => Some(self.hash),
Kind::Function { .. } => Some(self.hash),
Kind::Closure { .. } => Some(self.hash),
Kind::AsyncBlock { .. } => Some(self.hash),
Kind::Variant { .. } => None,
Kind::Const { .. } => None,
Kind::ConstFn { .. } => None,
Kind::Macro => None,
Kind::AttributeMacro => None,
Kind::Import { .. } => None,
Kind::Alias { .. } => None,
Kind::Module => None,
Kind::Trait => None,
}
}
}
#[derive(Debug, TryClone)]
pub enum Fields {
Named(FieldsNamed),
Unnamed(usize),
Empty,
}
#[derive(Debug, TryClone)]
#[non_exhaustive]
pub enum Kind {
Type {
parameters: Hash,
},
Struct {
fields: Fields,
constructor: Option<Signature>,
parameters: Hash,
},
Variant {
enum_hash: Hash,
index: usize,
fields: Fields,
constructor: Option<Signature>,
},
Enum {
parameters: Hash,
},
Macro,
AttributeMacro,
Function {
associated: Option<AssociatedKind>,
trait_hash: Option<Hash>,
signature: Signature,
is_test: bool,
is_bench: bool,
parameters: Hash,
#[cfg(feature = "doc")]
container: Option<Hash>,
#[cfg(feature = "doc")]
parameter_types: Vec<Hash>,
},
Closure {
call: Call,
do_move: bool,
},
AsyncBlock {
call: Call,
do_move: bool,
},
Const,
ConstFn,
Import(Import),
Alias(Alias),
Module,
Trait,
}
impl Kind {
#[cfg(all(feature = "doc", any(feature = "languageserver", feature = "cli")))]
pub(crate) fn as_signature(&self) -> Option<&Signature> {
match self {
Kind::Struct { constructor, .. } => constructor.as_ref(),
Kind::Variant { constructor, .. } => constructor.as_ref(),
Kind::Function { signature, .. } => Some(signature),
_ => None,
}
}
pub(crate) fn as_parameters(&self) -> Hash {
match self {
Kind::Function { parameters, .. } => *parameters,
Kind::Type { parameters, .. } => *parameters,
Kind::Enum { parameters, .. } => *parameters,
Kind::Struct { parameters, .. } => *parameters,
_ => Hash::EMPTY,
}
}
#[cfg(feature = "doc")]
pub(crate) fn associated_container(&self) -> Option<Hash> {
match self {
Kind::Variant { enum_hash, .. } => Some(*enum_hash),
Kind::Function { container, .. } => *container,
_ => None,
}
}
}
#[derive(Debug, TryClone, Clone, Copy)]
#[try_clone(copy)]
#[non_exhaustive]
pub struct Import {
pub(crate) location: Location,
pub(crate) target: ItemId,
pub(crate) module: ModId,
}
#[derive(Debug, TryClone)]
pub struct Alias {
pub(crate) to: ItemBuf,
}
#[derive(Debug, TryClone)]
#[non_exhaustive]
pub struct FieldsNamed {
pub(crate) fields: Box<[FieldMeta]>,
}
impl FieldsNamed {
pub(crate) fn to_fields(&self) -> alloc::Result<FieldMap<Box<str>, usize>> {
let mut fields = crate::runtime::new_field_hash_map_with_capacity(self.fields.len())?;
for f in self.fields.iter() {
fields.try_insert(f.name.try_clone()?, f.position)?;
}
Ok(fields)
}
}
#[derive(Debug, TryClone)]
pub struct FieldMeta {
pub(crate) name: Box<str>,
pub(crate) position: usize,
}
#[derive(Debug, TryClone, Clone, Copy)]
#[try_clone(copy)]
#[non_exhaustive]
pub(crate) struct ItemMeta {
pub(crate) location: Location,
pub(crate) item: ItemId,
pub(crate) visibility: Visibility,
pub(crate) module: ModId,
pub(crate) impl_item: Option<ItemId>,
}
impl ItemMeta {
pub(crate) fn is_public(&self, pool: &Pool) -> bool {
self.visibility.is_public() && pool.module(self.module).is_public(pool)
}
}
#[derive(Debug, TryClone)]
pub struct Signature {
#[cfg(feature = "doc")]
pub(crate) is_async: bool,
#[cfg(feature = "doc")]
pub(crate) arguments: Option<Box<[DocArgument]>>,
#[cfg(feature = "doc")]
pub(crate) return_type: DocType,
}
impl Signature {
#[cfg_attr(not(feature = "doc"), allow(unused_variables))]
pub(crate) fn from_context(
doc: &DocFunction,
common: &ModuleItemCommon,
) -> alloc::Result<Self> {
Ok(Self {
#[cfg(feature = "doc")]
is_async: doc.is_async,
#[cfg(feature = "doc")]
arguments: context_to_arguments(
doc.args,
doc.argument_types.as_ref(),
common.docs.args(),
)?,
#[cfg(feature = "doc")]
return_type: doc.return_type.try_clone()?,
})
}
}
#[cfg(feature = "doc")]
fn context_to_arguments(
args: Option<usize>,
types: &[meta::DocType],
names: &[String],
) -> alloc::Result<Option<Box<[meta::DocArgument]>>> {
use core::iter;
let Some(args) = args else {
return Ok(None);
};
let len = args.max(types.len()).max(names.len()).max(names.len());
let mut out = Vec::try_with_capacity(len)?;
let mut types = types.iter();
let names = names
.iter()
.map(|name| Some(name.as_str()))
.chain(iter::repeat(None));
for (n, name) in (0..len).zip(names) {
let empty;
let ty = match types.next() {
Some(ty) => ty,
None => {
empty = meta::DocType::empty();
&empty
}
};
out.try_push(meta::DocArgument {
name: match name {
Some(name) => meta::DocName::Name(Box::try_from(name)?),
None => meta::DocName::Index(n),
},
base: ty.base,
generics: ty.generics.try_clone()?,
})?;
}
Ok(Some(Box::try_from(out)?))
}
#[derive(Debug, TryClone)]
#[cfg(feature = "doc")]
pub(crate) enum DocName {
Name(Box<str>),
Index(#[try_clone(copy)] usize),
}
#[cfg(feature = "cli")]
impl DocName {
pub(crate) fn is_self(&self) -> bool {
match self {
DocName::Name(name) => name.as_ref() == "self",
DocName::Index(..) => false,
}
}
}
#[cfg(feature = "doc")]
impl fmt::Display for DocName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DocName::Name(name) => write!(f, "{name}"),
DocName::Index(index) if *index == 0 => write!(f, "value"),
DocName::Index(index) => write!(f, "value{index}"),
}
}
}
#[derive(Debug, TryClone)]
#[cfg(feature = "doc")]
pub(crate) struct DocArgument {
pub(crate) name: DocName,
pub(crate) base: Hash,
pub(crate) generics: Box<[DocType]>,
}
#[derive(Default, Debug, TryClone)]
pub struct DocType {
#[cfg(feature = "doc")]
pub(crate) base: Hash,
#[cfg(feature = "doc")]
pub(crate) generics: Box<[DocType]>,
}
impl DocType {
pub(crate) fn empty() -> Self {
Self::new(Hash::EMPTY)
}
#[cfg_attr(not(feature = "doc"), allow(unused_variables))]
pub fn with_generics<const N: usize>(
base: Hash,
generics: [DocType; N],
) -> alloc::Result<Self> {
Ok(Self {
#[cfg(feature = "doc")]
base,
#[cfg(feature = "doc")]
generics: Box::try_from(generics)?,
})
}
#[cfg_attr(not(feature = "doc"), allow(unused_variables))]
pub(crate) fn new(base: Hash) -> Self {
Self {
#[cfg(feature = "doc")]
base,
#[cfg(feature = "doc")]
generics: Box::default(),
}
}
}
#[derive(Debug, TryClone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum AssociatedKind {
Protocol(&'static Protocol),
FieldFn(&'static Protocol, Cow<'static, str>),
IndexFn(&'static Protocol, usize),
Instance(Cow<'static, str>),
}
impl AssociatedKind {
pub(crate) fn hash(&self, instance_type: Hash) -> Hash {
match self {
Self::Protocol(protocol) => Hash::associated_function(instance_type, protocol.hash),
Self::IndexFn(protocol, index) => {
Hash::index_function(protocol.hash, instance_type, Hash::index(*index))
}
Self::FieldFn(protocol, field) => {
Hash::field_function(protocol.hash, instance_type, field.as_ref())
}
Self::Instance(name) => Hash::associated_function(instance_type, name.as_ref()),
}
}
}
impl fmt::Display for AssociatedKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AssociatedKind::Protocol(protocol) => write!(f, "<{}>", protocol.name),
AssociatedKind::FieldFn(protocol, field) => {
write!(f, ".{field}<{}>", protocol.name)
}
AssociatedKind::IndexFn(protocol, index) => {
write!(f, ".{index}<{}>", protocol.name)
}
AssociatedKind::Instance(name) => write!(f, "{}", name),
}
}
}