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 EnumVariant,
62 Field,
63 Static,
64 Const,
65 Trait,
66 TypeAlias,
67 Macro,
68 Function,
69 Adt,
70 Module,
71 Impl,
72 Crate,
73 AssocItem,
74 Struct,
75 Union,
76 Enum,
77];
78
79impl HasDocs for hir::ExternCrateDecl {
80 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation<'_>> {
81 let crate_docs = self.resolved_crate(db)?.hir_docs(db);
82 let decl_docs = self.hir_docs(db);
83 match (decl_docs, crate_docs) {
84 (None, None) => None,
85 (Some(docs), None) | (None, Some(docs)) => {
86 Some(Documentation::new_borrowed(docs.docs()))
87 }
88 (Some(decl_docs), Some(crate_docs)) => {
89 let mut docs = String::with_capacity(
90 decl_docs.docs().len() + "\n\n".len() + crate_docs.docs().len(),
91 );
92 docs.push_str(decl_docs.docs());
93 docs.push_str("\n\n");
94 docs.push_str(crate_docs.docs());
95 Some(Documentation::new_owned(docs))
96 }
97 }
98 }
99
100 fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<Cow<'_, hir::Docs>> {
101 let crate_docs = self.resolved_crate(db)?.hir_docs(db);
102 let decl_docs = self.hir_docs(db);
103 match (decl_docs, crate_docs) {
104 (None, None) => None,
105 (Some(docs), None) | (None, Some(docs)) => Some(Cow::Borrowed(docs)),
106 (Some(decl_docs), Some(crate_docs)) => {
107 let mut docs = decl_docs.clone();
108 docs.append_str("\n\n");
109 docs.append(crate_docs);
110 Some(Cow::Owned(docs))
111 }
112 }
113 }
114}