1use std::borrow::Cow;
3
4use hir::{HasAttrs, db::HirDatabase, resolve_doc_path_on};
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub struct Documentation<'db>(Cow<'db, str>);
9
10impl<'db> Documentation<'db> {
11 #[inline]
12 pub fn new_owned(s: String) -> Self {
13 Documentation(Cow::Owned(s))
14 }
15
16 #[inline]
17 pub fn new_borrowed(s: &'db str) -> Self {
18 Documentation(Cow::Borrowed(s))
19 }
20
21 #[inline]
22 pub fn into_owned(self) -> Documentation<'static> {
23 Documentation::new_owned(self.0.into_owned())
24 }
25
26 #[inline]
27 pub fn as_str(&self) -> &str {
28 &self.0
29 }
30}
31
32pub trait HasDocs: HasAttrs + Copy {
33 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation<'_>> {
34 let docs = match self.docs_with_rangemap(db)? {
35 Cow::Borrowed(docs) => Documentation::new_borrowed(docs.docs()),
36 Cow::Owned(docs) => Documentation::new_owned(docs.into_docs()),
37 };
38 Some(docs)
39 }
40 fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<Cow<'_, hir::Docs>> {
41 self.hir_docs(db).map(Cow::Borrowed)
42 }
43 fn resolve_doc_path(
44 self,
45 db: &dyn HirDatabase,
46 link: &str,
47 ns: Option<hir::Namespace>,
48 is_inner_doc: hir::IsInnerDoc,
49 ) -> Option<hir::DocLinkDef> {
50 resolve_doc_path_on(db, self, link, ns, is_inner_doc)
51 }
52}
53
54macro_rules! impl_has_docs {
55 ($($def:ident,)*) => {$(
56 impl HasDocs for hir::$def {}
57 )*};
58}
59
60impl_has_docs![
61 Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate,
62 AssocItem, Struct, Union, Enum,
63];
64
65impl HasDocs for hir::ExternCrateDecl {
66 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation<'_>> {
67 let crate_docs = self.resolved_crate(db)?.hir_docs(db);
68 let decl_docs = self.hir_docs(db);
69 match (decl_docs, crate_docs) {
70 (None, None) => None,
71 (Some(docs), None) | (None, Some(docs)) => {
72 Some(Documentation::new_borrowed(docs.docs()))
73 }
74 (Some(decl_docs), Some(crate_docs)) => {
75 let mut docs = String::with_capacity(
76 decl_docs.docs().len() + "\n\n".len() + crate_docs.docs().len(),
77 );
78 docs.push_str(decl_docs.docs());
79 docs.push_str("\n\n");
80 docs.push_str(crate_docs.docs());
81 Some(Documentation::new_owned(docs))
82 }
83 }
84 }
85
86 fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<Cow<'_, hir::Docs>> {
87 let crate_docs = self.resolved_crate(db)?.hir_docs(db);
88 let decl_docs = self.hir_docs(db);
89 match (decl_docs, crate_docs) {
90 (None, None) => None,
91 (Some(docs), None) | (None, Some(docs)) => Some(Cow::Borrowed(docs)),
92 (Some(decl_docs), Some(crate_docs)) => {
93 let mut docs = decl_docs.clone();
94 docs.append_str("\n\n");
95 docs.append(crate_docs);
96 Some(Cow::Owned(docs))
97 }
98 }
99 }
100}