1use core::fmt;
4
5#[cfg(feature = "std")]
6use std::path::Path;
7
8use crate as rune;
9use crate::alloc::borrow::Cow;
10use crate::alloc::prelude::*;
11use crate::alloc::{self, Box, Vec};
12use crate::ast;
13use crate::ast::{Span, Spanned};
14use crate::compile::attrs::Parser;
15#[cfg(feature = "doc")]
16use crate::compile::meta;
17use crate::compile::{self, ItemId, Location, MetaInfo, ModId, Pool, Visibility};
18use crate::module::{DocFunction, ModuleItemCommon};
19use crate::parse::ResolveContext;
20use crate::runtime::{Call, FieldMap, Protocol};
21use crate::{Hash, Item, ItemBuf};
22
23#[derive(Debug, TryClone, Clone, Copy)]
25#[try_clone(copy)]
26#[non_exhaustive]
27pub struct MetaRef<'a> {
28 pub context: bool,
30 pub hash: Hash,
32 pub item: &'a Item,
34 pub kind: &'a Kind,
36 pub source: Option<&'a SourceMeta>,
38}
39
40#[derive(Debug, TryClone)]
42#[non_exhaustive]
43pub struct SourceMeta {
44 pub location: Location,
46 #[cfg(feature = "std")]
48 #[cfg_attr(rune_docsrs, doc(cfg(feature = "std")))]
49 pub path: Option<Box<Path>>,
50}
51
52#[derive(Debug, TryClone, Clone, Copy, Spanned)]
54#[try_clone(copy)]
55pub(crate) struct Doc {
56 #[rune(span)]
57 pub(crate) span: Span,
58 pub(crate) doc_string: ast::LitStr,
60}
61
62impl Doc {
63 pub(crate) fn collect_from(
64 cx: ResolveContext<'_, '_>,
65 attrs: &mut Parser,
66 attributes: &[ast::Attribute],
67 ) -> compile::Result<Vec<Doc>> {
68 let docs = attrs
69 .parse_all::<crate::compile::attrs::Doc>(cx, attributes)?
70 .map(|result| {
71 result.map(|(span, doc)| Doc {
72 span: span.span(),
73 doc_string: doc.doc_string,
74 })
75 })
76 .try_collect::<compile::Result<_>>()??;
77
78 Ok(docs)
79 }
80}
81
82#[derive(Debug, TryClone)]
84#[non_exhaustive]
85pub(crate) struct Meta {
86 pub(crate) context: bool,
88 pub(crate) hash: Hash,
90 pub(crate) item_meta: ItemMeta,
92 pub(crate) kind: Kind,
94 pub(crate) source: Option<SourceMeta>,
96 pub(crate) parameters: Hash,
98}
99
100impl Meta {
101 pub(crate) fn info(&self, pool: &Pool) -> alloc::Result<MetaInfo> {
103 MetaInfo::new(&self.kind, self.hash, Some(pool.item(self.item_meta.item)))
104 }
105
106 pub(crate) fn as_meta_ref<'a>(&'a self, pool: &'a Pool) -> MetaRef<'a> {
108 MetaRef {
109 context: self.context,
110 hash: self.hash,
111 item: pool.item(self.item_meta.item),
112 kind: &self.kind,
113 source: self.source.as_ref(),
114 }
115 }
116
117 pub(crate) fn type_hash_of(&self) -> Option<Hash> {
123 match &self.kind {
124 Kind::Type { .. } => Some(self.hash),
125 Kind::Struct {
126 enum_hash: Hash::EMPTY,
127 ..
128 } => Some(self.hash),
129 Kind::Struct { .. } => None,
130 Kind::Enum { .. } => Some(self.hash),
131 Kind::Function { .. } => Some(self.hash),
132 Kind::Closure { .. } => Some(self.hash),
133 Kind::AsyncBlock { .. } => Some(self.hash),
134 Kind::Const => None,
135 Kind::Static => None,
136 Kind::ConstFn => None,
137 Kind::Macro => None,
138 Kind::AttributeMacro => None,
139 Kind::Import { .. } => None,
140 Kind::Alias { .. } => None,
141 Kind::Module => None,
142 Kind::Trait => None,
143 }
144 }
145}
146
147#[derive(Debug, TryClone)]
149pub enum Fields {
150 Named(FieldsNamed),
152 Unnamed(usize),
154 Empty,
156}
157
158impl Fields {
159 pub(crate) fn as_tuple(&self) -> Option<usize> {
161 match *self {
162 Fields::Unnamed(count) => Some(count),
163 Fields::Empty => Some(0),
164 _ => None,
165 }
166 }
167}
168
169#[derive(Debug, TryClone)]
171#[non_exhaustive]
172pub enum Kind {
173 Type {
176 parameters: Hash,
178 },
179 Struct {
181 fields: Fields,
183 constructor: Option<Signature>,
185 parameters: Hash,
187 enum_hash: Hash,
191 },
192 Enum {
194 parameters: Hash,
196 },
197 Macro,
199 AttributeMacro,
201 Function {
203 associated: Option<AssociatedKind>,
206 trait_hash: Option<Hash>,
208 signature: Signature,
210 is_test: bool,
212 is_bench: bool,
214 parameters: Hash,
216 #[cfg(feature = "doc")]
218 container: Option<Hash>,
219 #[cfg(feature = "doc")]
221 parameter_types: Vec<Hash>,
222 },
223 Closure {
225 call: Call,
227 do_move: bool,
229 },
230 AsyncBlock {
232 call: Call,
234 do_move: bool,
236 },
237 Const,
239 Static,
241 ConstFn,
243 Import(Import),
245 Alias(Alias),
247 Module,
249 Trait,
251}
252
253impl Kind {
254 #[cfg(all(feature = "doc", any(feature = "languageserver", feature = "cli")))]
256 pub(crate) fn as_signature(&self) -> Option<&Signature> {
257 match self {
258 Kind::Struct { constructor, .. } => constructor.as_ref(),
259 Kind::Function { signature, .. } => Some(signature),
260 _ => None,
261 }
262 }
263
264 pub(crate) fn as_parameters(&self) -> Hash {
266 match self {
267 Kind::Function { parameters, .. } => *parameters,
268 Kind::Type { parameters, .. } => *parameters,
269 Kind::Enum { parameters, .. } => *parameters,
270 Kind::Struct { parameters, .. } => *parameters,
271 _ => Hash::EMPTY,
272 }
273 }
274
275 #[cfg(feature = "doc")]
277 pub(crate) fn associated_container(&self) -> Option<Hash> {
278 match *self {
279 Kind::Struct { enum_hash, .. } if enum_hash != Hash::EMPTY => Some(enum_hash),
280 Kind::Function { container, .. } => container,
281 _ => None,
282 }
283 }
284}
285
286#[derive(Debug, TryClone, Clone, Copy)]
288#[try_clone(copy)]
289#[non_exhaustive]
290pub struct Import {
291 pub(crate) location: Location,
293 pub(crate) target: ItemId,
295 pub(crate) module: ModId,
297}
298
299#[derive(Debug, TryClone)]
301pub struct Alias {
302 pub(crate) to: ItemBuf,
304}
305
306#[derive(Debug, TryClone)]
308#[non_exhaustive]
309pub struct FieldsNamed {
310 pub(crate) fields: Box<[FieldMeta]>,
312}
313
314impl FieldsNamed {
315 pub(crate) fn to_fields(&self) -> alloc::Result<FieldMap<Box<str>, usize>> {
317 let mut fields = crate::runtime::new_field_hash_map_with_capacity(self.fields.len())?;
318
319 for f in self.fields.iter() {
320 fields.try_insert(f.name.try_clone()?, f.position)?;
321 }
322
323 Ok(fields)
324 }
325}
326
327#[derive(Debug, TryClone)]
329pub struct FieldMeta {
330 pub(crate) name: Box<str>,
332 pub(crate) position: usize,
334}
335
336#[derive(Debug, TryClone, Clone, Copy)]
338#[try_clone(copy)]
339#[non_exhaustive]
340pub(crate) struct ItemMeta {
341 pub(crate) location: Location,
343 pub(crate) item: ItemId,
345 pub(crate) visibility: Visibility,
347 pub(crate) module: ModId,
349 pub(crate) impl_item: Option<ItemId>,
351}
352
353impl ItemMeta {
354 pub(crate) fn is_public(&self, pool: &Pool) -> bool {
356 self.visibility.is_public() && pool.module(self.module).is_public(pool)
357 }
358}
359
360#[derive(Debug, TryClone)]
362pub struct Signature {
363 #[cfg(feature = "doc")]
365 pub(crate) is_async: bool,
366 #[cfg(feature = "doc")]
368 pub(crate) arguments: Option<Box<[DocArgument]>>,
369 #[cfg(feature = "doc")]
371 pub(crate) return_type: DocType,
372}
373
374impl Signature {
375 #[cfg_attr(not(feature = "doc"), allow(unused_variables))]
377 pub(crate) fn from_context(
378 doc: &DocFunction,
379 common: &ModuleItemCommon,
380 ) -> alloc::Result<Self> {
381 Ok(Self {
382 #[cfg(feature = "doc")]
383 is_async: doc.is_async,
384 #[cfg(feature = "doc")]
385 arguments: context_to_arguments(
386 doc.args,
387 doc.argument_types.as_ref(),
388 common.docs.args(),
389 )?,
390 #[cfg(feature = "doc")]
391 return_type: doc.return_type.try_clone()?,
392 })
393 }
394}
395
396#[cfg(feature = "doc")]
397fn context_to_arguments(
398 args: Option<usize>,
399 types: &[meta::DocType],
400 names: &[String],
401) -> alloc::Result<Option<Box<[meta::DocArgument]>>> {
402 use core::iter;
403
404 let Some(args) = args else {
405 return Ok(None);
406 };
407
408 let len = args.max(types.len()).max(names.len()).max(names.len());
409 let mut out = Vec::try_with_capacity(len)?;
410
411 let mut types = types.iter();
412
413 let names = names
414 .iter()
415 .map(|name| Some(name.as_str()))
416 .chain(iter::repeat(None));
417
418 for (n, name) in (0..len).zip(names) {
419 let empty;
420
421 let ty = match types.next() {
422 Some(ty) => ty,
423 None => {
424 empty = meta::DocType::empty();
425 &empty
426 }
427 };
428
429 out.try_push(meta::DocArgument {
430 name: match name {
431 Some(name) => meta::DocName::Name(Box::try_from(name)?),
432 None => meta::DocName::Index(n),
433 },
434 base: ty.base,
435 generics: ty.generics.try_clone()?,
436 })?;
437 }
438
439 Ok(Some(Box::try_from(out)?))
440}
441
442#[derive(Debug, TryClone)]
444#[cfg(feature = "doc")]
445pub(crate) enum DocName {
446 Name(Box<str>),
448 Index(#[try_clone(copy)] usize),
450}
451
452#[cfg(feature = "cli")]
453impl DocName {
454 pub(crate) fn is_self(&self) -> bool {
455 match self {
456 DocName::Name(name) => name.as_ref() == "self",
457 DocName::Index(..) => false,
458 }
459 }
460}
461
462#[cfg(feature = "doc")]
463impl fmt::Display for DocName {
464 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465 match self {
466 DocName::Name(name) => write!(f, "{name}"),
467 DocName::Index(index) if *index == 0 => write!(f, "value"),
468 DocName::Index(index) => write!(f, "value{index}"),
469 }
470 }
471}
472
473#[derive(Debug, TryClone)]
475#[cfg(feature = "doc")]
476pub(crate) struct DocArgument {
477 pub(crate) name: DocName,
479 pub(crate) base: Hash,
481 pub(crate) generics: Box<[DocType]>,
483}
484
485#[derive(Default, Debug, TryClone)]
487pub struct DocType {
488 #[cfg(feature = "doc")]
490 pub(crate) base: Hash,
491 #[cfg(feature = "doc")]
493 pub(crate) generics: Box<[DocType]>,
494}
495
496impl DocType {
497 pub(crate) fn empty() -> Self {
499 Self::new(Hash::EMPTY)
500 }
501
502 #[cfg_attr(not(feature = "doc"), allow(unused_variables))]
504 pub fn with_generics<const N: usize>(
505 base: Hash,
506 generics: [DocType; N],
507 ) -> alloc::Result<Self> {
508 Ok(Self {
509 #[cfg(feature = "doc")]
510 base,
511 #[cfg(feature = "doc")]
512 generics: Box::try_from(generics)?,
513 })
514 }
515
516 #[cfg_attr(not(feature = "doc"), allow(unused_variables))]
518 pub(crate) fn new(base: Hash) -> Self {
519 Self {
520 #[cfg(feature = "doc")]
521 base,
522 #[cfg(feature = "doc")]
523 generics: Box::default(),
524 }
525 }
526}
527
528#[derive(Debug, TryClone, PartialEq, Eq, Hash)]
530#[non_exhaustive]
531pub enum AssociatedKind {
532 Protocol(&'static Protocol),
534 FieldFn(&'static Protocol, Cow<'static, str>),
536 IndexFn(&'static Protocol, usize),
538 Instance(Cow<'static, str>),
540}
541
542impl AssociatedKind {
543 pub(crate) fn hash(&self, instance_type: Hash) -> Hash {
545 match self {
546 Self::Protocol(protocol) => Hash::associated_function(instance_type, protocol.hash),
547 Self::IndexFn(protocol, index) => {
548 Hash::index_function(protocol.hash, instance_type, Hash::index(*index))
549 }
550 Self::FieldFn(protocol, field) => {
551 Hash::field_function(protocol.hash, instance_type, field.as_ref())
552 }
553 Self::Instance(name) => Hash::associated_function(instance_type, name.as_ref()),
554 }
555 }
556}
557
558impl fmt::Display for AssociatedKind {
559 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
560 match self {
561 AssociatedKind::Protocol(protocol) => write!(f, "<{}>", protocol.name),
562 AssociatedKind::FieldFn(protocol, field) => {
563 write!(f, ".{field}<{}>", protocol.name)
564 }
565 AssociatedKind::IndexFn(protocol, index) => {
566 write!(f, ".{index}<{}>", protocol.name)
567 }
568 AssociatedKind::Instance(name) => write!(f, "{name}"),
569 }
570 }
571}