1use core::fmt;
4
5use crate as rune;
6use crate::alloc::borrow::Cow;
7use crate::alloc::path::Path;
8use crate::alloc::prelude::*;
9use crate::alloc::{self, Box, Vec};
10use crate::ast;
11use crate::ast::{Span, Spanned};
12use crate::compile::attrs::Parser;
13#[cfg(feature = "doc")]
14use crate::compile::meta;
15use crate::compile::{self, ItemId, Location, MetaInfo, ModId, Pool, Visibility};
16use crate::module::{DocFunction, ModuleItemCommon};
17use crate::parse::ResolveContext;
18use crate::runtime::{Call, FieldMap, Protocol};
19use crate::{Hash, Item, ItemBuf};
20
21#[derive(Debug, TryClone, Clone, Copy)]
23#[try_clone(copy)]
24#[non_exhaustive]
25pub struct MetaRef<'a> {
26 pub context: bool,
28 pub hash: Hash,
30 pub item: &'a Item,
32 pub kind: &'a Kind,
34 pub source: Option<&'a SourceMeta>,
36}
37
38#[derive(Debug, TryClone)]
40#[non_exhaustive]
41pub struct SourceMeta {
42 pub location: Location,
44 pub path: Option<Box<Path>>,
46}
47
48#[derive(Debug, TryClone, Clone, Copy, Spanned)]
50#[try_clone(copy)]
51pub(crate) struct Doc {
52 #[rune(span)]
53 pub(crate) span: Span,
54 pub(crate) doc_string: ast::LitStr,
56}
57
58impl Doc {
59 pub(crate) fn collect_from(
60 cx: ResolveContext<'_>,
61 attrs: &mut Parser,
62 attributes: &[ast::Attribute],
63 ) -> compile::Result<Vec<Doc>> {
64 let docs = attrs
65 .parse_all::<crate::compile::attrs::Doc>(cx, attributes)?
66 .map(|result| {
67 result.map(|(span, doc)| Doc {
68 span: span.span(),
69 doc_string: doc.doc_string,
70 })
71 })
72 .try_collect::<compile::Result<_>>()??;
73
74 Ok(docs)
75 }
76}
77
78#[derive(Debug, TryClone)]
80#[non_exhaustive]
81pub(crate) struct Meta {
82 pub(crate) context: bool,
84 pub(crate) hash: Hash,
86 pub(crate) item_meta: ItemMeta,
88 pub(crate) kind: Kind,
90 pub(crate) source: Option<SourceMeta>,
92 pub(crate) parameters: Hash,
94}
95
96impl Meta {
97 pub(crate) fn info(&self, pool: &Pool) -> alloc::Result<MetaInfo> {
99 MetaInfo::new(&self.kind, self.hash, Some(pool.item(self.item_meta.item)))
100 }
101
102 pub(crate) fn as_meta_ref<'a>(&'a self, pool: &'a Pool) -> MetaRef<'a> {
104 MetaRef {
105 context: self.context,
106 hash: self.hash,
107 item: pool.item(self.item_meta.item),
108 kind: &self.kind,
109 source: self.source.as_ref(),
110 }
111 }
112
113 pub(crate) fn type_hash_of(&self) -> Option<Hash> {
119 match &self.kind {
120 Kind::Type { .. } => Some(self.hash),
121 Kind::Struct {
122 enum_hash: Hash::EMPTY,
123 ..
124 } => Some(self.hash),
125 Kind::Struct { .. } => None,
126 Kind::Enum { .. } => Some(self.hash),
127 Kind::Function { .. } => Some(self.hash),
128 Kind::Closure { .. } => Some(self.hash),
129 Kind::AsyncBlock { .. } => Some(self.hash),
130 Kind::Const => None,
131 Kind::ConstFn => None,
132 Kind::Macro => None,
133 Kind::AttributeMacro => None,
134 Kind::Import { .. } => None,
135 Kind::Alias { .. } => None,
136 Kind::Module => None,
137 Kind::Trait => None,
138 }
139 }
140}
141
142#[derive(Debug, TryClone)]
144pub enum Fields {
145 Named(FieldsNamed),
147 Unnamed(usize),
149 Empty,
151}
152
153impl Fields {
154 pub(crate) fn as_tuple(&self) -> Option<usize> {
156 match *self {
157 Fields::Unnamed(count) => Some(count),
158 Fields::Empty => Some(0),
159 _ => None,
160 }
161 }
162}
163
164#[derive(Debug, TryClone)]
166#[non_exhaustive]
167pub enum Kind {
168 Type {
171 parameters: Hash,
173 },
174 Struct {
176 fields: Fields,
178 constructor: Option<Signature>,
180 parameters: Hash,
182 enum_hash: Hash,
186 },
187 Enum {
189 parameters: Hash,
191 },
192 Macro,
194 AttributeMacro,
196 Function {
198 associated: Option<AssociatedKind>,
201 trait_hash: Option<Hash>,
203 signature: Signature,
205 is_test: bool,
207 is_bench: bool,
209 parameters: Hash,
211 #[cfg(feature = "doc")]
213 container: Option<Hash>,
214 #[cfg(feature = "doc")]
216 parameter_types: Vec<Hash>,
217 },
218 Closure {
220 call: Call,
222 do_move: bool,
224 },
225 AsyncBlock {
227 call: Call,
229 do_move: bool,
231 },
232 Const,
234 ConstFn,
236 Import(Import),
238 Alias(Alias),
240 Module,
242 Trait,
244}
245
246impl Kind {
247 #[cfg(all(feature = "doc", any(feature = "languageserver", feature = "cli")))]
249 pub(crate) fn as_signature(&self) -> Option<&Signature> {
250 match self {
251 Kind::Struct { constructor, .. } => constructor.as_ref(),
252 Kind::Function { signature, .. } => Some(signature),
253 _ => None,
254 }
255 }
256
257 pub(crate) fn as_parameters(&self) -> Hash {
259 match self {
260 Kind::Function { parameters, .. } => *parameters,
261 Kind::Type { parameters, .. } => *parameters,
262 Kind::Enum { parameters, .. } => *parameters,
263 Kind::Struct { parameters, .. } => *parameters,
264 _ => Hash::EMPTY,
265 }
266 }
267
268 #[cfg(feature = "doc")]
270 pub(crate) fn associated_container(&self) -> Option<Hash> {
271 match *self {
272 Kind::Struct { enum_hash, .. } if enum_hash != Hash::EMPTY => Some(enum_hash),
273 Kind::Function { container, .. } => container,
274 _ => None,
275 }
276 }
277}
278
279#[derive(Debug, TryClone, Clone, Copy)]
281#[try_clone(copy)]
282#[non_exhaustive]
283pub struct Import {
284 pub(crate) location: Location,
286 pub(crate) target: ItemId,
288 pub(crate) module: ModId,
290}
291
292#[derive(Debug, TryClone)]
294pub struct Alias {
295 pub(crate) to: ItemBuf,
297}
298
299#[derive(Debug, TryClone)]
301#[non_exhaustive]
302pub struct FieldsNamed {
303 pub(crate) fields: Box<[FieldMeta]>,
305}
306
307impl FieldsNamed {
308 pub(crate) fn to_fields(&self) -> alloc::Result<FieldMap<Box<str>, usize>> {
310 let mut fields = crate::runtime::new_field_hash_map_with_capacity(self.fields.len())?;
311
312 for f in self.fields.iter() {
313 fields.try_insert(f.name.try_clone()?, f.position)?;
314 }
315
316 Ok(fields)
317 }
318}
319
320#[derive(Debug, TryClone)]
322pub struct FieldMeta {
323 pub(crate) name: Box<str>,
325 pub(crate) position: usize,
327}
328
329#[derive(Debug, TryClone, Clone, Copy)]
331#[try_clone(copy)]
332#[non_exhaustive]
333pub(crate) struct ItemMeta {
334 pub(crate) location: Location,
336 pub(crate) item: ItemId,
338 pub(crate) visibility: Visibility,
340 pub(crate) module: ModId,
342 pub(crate) impl_item: Option<ItemId>,
344}
345
346impl ItemMeta {
347 pub(crate) fn is_public(&self, pool: &Pool) -> bool {
349 self.visibility.is_public() && pool.module(self.module).is_public(pool)
350 }
351}
352
353#[derive(Debug, TryClone)]
355pub struct Signature {
356 #[cfg(feature = "doc")]
358 pub(crate) is_async: bool,
359 #[cfg(feature = "doc")]
361 pub(crate) arguments: Option<Box<[DocArgument]>>,
362 #[cfg(feature = "doc")]
364 pub(crate) return_type: DocType,
365}
366
367impl Signature {
368 #[cfg_attr(not(feature = "doc"), allow(unused_variables))]
370 pub(crate) fn from_context(
371 doc: &DocFunction,
372 common: &ModuleItemCommon,
373 ) -> alloc::Result<Self> {
374 Ok(Self {
375 #[cfg(feature = "doc")]
376 is_async: doc.is_async,
377 #[cfg(feature = "doc")]
378 arguments: context_to_arguments(
379 doc.args,
380 doc.argument_types.as_ref(),
381 common.docs.args(),
382 )?,
383 #[cfg(feature = "doc")]
384 return_type: doc.return_type.try_clone()?,
385 })
386 }
387}
388
389#[cfg(feature = "doc")]
390fn context_to_arguments(
391 args: Option<usize>,
392 types: &[meta::DocType],
393 names: &[String],
394) -> alloc::Result<Option<Box<[meta::DocArgument]>>> {
395 use core::iter;
396
397 let Some(args) = args else {
398 return Ok(None);
399 };
400
401 let len = args.max(types.len()).max(names.len()).max(names.len());
402 let mut out = Vec::try_with_capacity(len)?;
403
404 let mut types = types.iter();
405
406 let names = names
407 .iter()
408 .map(|name| Some(name.as_str()))
409 .chain(iter::repeat(None));
410
411 for (n, name) in (0..len).zip(names) {
412 let empty;
413
414 let ty = match types.next() {
415 Some(ty) => ty,
416 None => {
417 empty = meta::DocType::empty();
418 &empty
419 }
420 };
421
422 out.try_push(meta::DocArgument {
423 name: match name {
424 Some(name) => meta::DocName::Name(Box::try_from(name)?),
425 None => meta::DocName::Index(n),
426 },
427 base: ty.base,
428 generics: ty.generics.try_clone()?,
429 })?;
430 }
431
432 Ok(Some(Box::try_from(out)?))
433}
434
435#[derive(Debug, TryClone)]
437#[cfg(feature = "doc")]
438pub(crate) enum DocName {
439 Name(Box<str>),
441 Index(#[try_clone(copy)] usize),
443}
444
445#[cfg(feature = "cli")]
446impl DocName {
447 pub(crate) fn is_self(&self) -> bool {
448 match self {
449 DocName::Name(name) => name.as_ref() == "self",
450 DocName::Index(..) => false,
451 }
452 }
453}
454
455#[cfg(feature = "doc")]
456impl fmt::Display for DocName {
457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458 match self {
459 DocName::Name(name) => write!(f, "{name}"),
460 DocName::Index(index) if *index == 0 => write!(f, "value"),
461 DocName::Index(index) => write!(f, "value{index}"),
462 }
463 }
464}
465
466#[derive(Debug, TryClone)]
468#[cfg(feature = "doc")]
469pub(crate) struct DocArgument {
470 pub(crate) name: DocName,
472 pub(crate) base: Hash,
474 pub(crate) generics: Box<[DocType]>,
476}
477
478#[derive(Default, Debug, TryClone)]
480pub struct DocType {
481 #[cfg(feature = "doc")]
483 pub(crate) base: Hash,
484 #[cfg(feature = "doc")]
486 pub(crate) generics: Box<[DocType]>,
487}
488
489impl DocType {
490 pub(crate) fn empty() -> Self {
492 Self::new(Hash::EMPTY)
493 }
494
495 #[cfg_attr(not(feature = "doc"), allow(unused_variables))]
497 pub fn with_generics<const N: usize>(
498 base: Hash,
499 generics: [DocType; N],
500 ) -> alloc::Result<Self> {
501 Ok(Self {
502 #[cfg(feature = "doc")]
503 base,
504 #[cfg(feature = "doc")]
505 generics: Box::try_from(generics)?,
506 })
507 }
508
509 #[cfg_attr(not(feature = "doc"), allow(unused_variables))]
511 pub(crate) fn new(base: Hash) -> Self {
512 Self {
513 #[cfg(feature = "doc")]
514 base,
515 #[cfg(feature = "doc")]
516 generics: Box::default(),
517 }
518 }
519}
520
521#[derive(Debug, TryClone, PartialEq, Eq, Hash)]
523#[non_exhaustive]
524pub enum AssociatedKind {
525 Protocol(&'static Protocol),
527 FieldFn(&'static Protocol, Cow<'static, str>),
529 IndexFn(&'static Protocol, usize),
531 Instance(Cow<'static, str>),
533}
534
535impl AssociatedKind {
536 pub(crate) fn hash(&self, instance_type: Hash) -> Hash {
538 match self {
539 Self::Protocol(protocol) => Hash::associated_function(instance_type, protocol.hash),
540 Self::IndexFn(protocol, index) => {
541 Hash::index_function(protocol.hash, instance_type, Hash::index(*index))
542 }
543 Self::FieldFn(protocol, field) => {
544 Hash::field_function(protocol.hash, instance_type, field.as_ref())
545 }
546 Self::Instance(name) => Hash::associated_function(instance_type, name.as_ref()),
547 }
548 }
549}
550
551impl fmt::Display for AssociatedKind {
552 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553 match self {
554 AssociatedKind::Protocol(protocol) => write!(f, "<{}>", protocol.name),
555 AssociatedKind::FieldFn(protocol, field) => {
556 write!(f, ".{field}<{}>", protocol.name)
557 }
558 AssociatedKind::IndexFn(protocol, index) => {
559 write!(f, ".{index}<{}>", protocol.name)
560 }
561 AssociatedKind::Instance(name) => write!(f, "{}", name),
562 }
563 }
564}