1use crate::{
18 Docs, Function, IndexMap, InterfaceId, PackageId, Resolve, Stability, TypeDefKind, TypeId,
19 WorldId, WorldItem, WorldKey,
20};
21use alloc::string::{String, ToString};
22#[cfg(feature = "serde")]
23use alloc::vec;
24#[cfg(feature = "serde")]
25use alloc::vec::Vec;
26use anyhow::{Result, bail};
27#[cfg(feature = "serde")]
28use serde_derive::{Deserialize, Serialize};
29
30type StringMap<V> = IndexMap<String, V>;
31
32#[cfg(feature = "serde")]
44const PACKAGE_DOCS_SECTION_VERSION: u8 = 1;
45
46const TRY_TO_EMIT_V0_BY_DEFAULT: bool = false;
51
52#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
54#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
55pub struct PackageMetadata {
56 #[cfg_attr(
57 feature = "serde",
58 serde(default, skip_serializing_if = "Option::is_none")
59 )]
60 docs: Option<String>,
61 #[cfg_attr(
62 feature = "serde",
63 serde(default, skip_serializing_if = "StringMap::is_empty")
64 )]
65 worlds: StringMap<WorldMetadata>,
66 #[cfg_attr(
67 feature = "serde",
68 serde(default, skip_serializing_if = "StringMap::is_empty")
69 )]
70 interfaces: StringMap<InterfaceMetadata>,
71}
72
73impl PackageMetadata {
74 pub const SECTION_NAME: &'static str = "package-docs";
75
76 pub fn extract(resolve: &Resolve, package: PackageId) -> Self {
78 let package = &resolve.packages[package];
79
80 let worlds = package
81 .worlds
82 .iter()
83 .map(|(name, id)| (name.to_string(), WorldMetadata::extract(resolve, *id)))
84 .filter(|(_, item)| !item.is_empty())
85 .collect();
86 let interfaces = package
87 .interfaces
88 .iter()
89 .map(|(name, id)| (name.to_string(), InterfaceMetadata::extract(resolve, *id)))
90 .filter(|(_, item)| !item.is_empty())
91 .collect();
92
93 Self {
94 docs: package.docs.contents.as_deref().map(Into::into),
95 worlds,
96 interfaces,
97 }
98 }
99
100 pub fn inject(&self, resolve: &mut Resolve, package: PackageId) -> Result<()> {
104 for (name, docs) in &self.worlds {
105 let Some(&id) = resolve.packages[package].worlds.get(name) else {
106 bail!("missing world {name:?}");
107 };
108 docs.inject(resolve, id)?;
109 }
110 for (name, docs) in &self.interfaces {
111 let Some(&id) = resolve.packages[package].interfaces.get(name) else {
112 bail!("missing interface {name:?}");
113 };
114 docs.inject(resolve, id)?;
115 }
116 if let Some(docs) = &self.docs {
117 resolve.packages[package].docs.contents = Some(docs.to_string());
118 }
119 Ok(())
120 }
121
122 #[cfg(feature = "serde")]
124 pub fn encode(&self) -> Result<Vec<u8>> {
125 let mut data = vec![
132 if TRY_TO_EMIT_V0_BY_DEFAULT && self.is_compatible_with_v0() {
133 0
134 } else {
135 PACKAGE_DOCS_SECTION_VERSION
136 },
137 ];
138 serde_json::to_writer(&mut data, self)?;
139 Ok(data)
140 }
141
142 #[cfg(feature = "serde")]
144 pub fn decode(data: &[u8]) -> Result<Self> {
145 match data.first().copied() {
146 Some(0) | Some(PACKAGE_DOCS_SECTION_VERSION) => {}
149 version => {
150 bail!(
151 "expected package-docs version {PACKAGE_DOCS_SECTION_VERSION}, got {version:?}"
152 );
153 }
154 }
155 Ok(serde_json::from_slice(&data[1..])?)
156 }
157
158 #[cfg(feature = "serde")]
159 fn is_compatible_with_v0(&self) -> bool {
160 self.worlds.iter().all(|(_, w)| w.is_compatible_with_v0())
161 && self
162 .interfaces
163 .iter()
164 .all(|(_, w)| w.is_compatible_with_v0())
165 }
166}
167
168#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
169#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
170struct WorldMetadata {
171 #[cfg_attr(
172 feature = "serde",
173 serde(default, skip_serializing_if = "Option::is_none")
174 )]
175 docs: Option<String>,
176 #[cfg_attr(
177 feature = "serde",
178 serde(default, skip_serializing_if = "Stability::is_unknown")
179 )]
180 stability: Stability,
181
182 #[cfg_attr(
204 feature = "serde",
205 serde(
206 default,
207 rename = "interfaces",
208 skip_serializing_if = "StringMap::is_empty"
209 )
210 )]
211 interface_imports_or_exports: StringMap<InterfaceMetadata>,
212
213 #[cfg_attr(
217 feature = "serde",
218 serde(default, skip_serializing_if = "StringMap::is_empty")
219 )]
220 types: StringMap<TypeMetadata>,
221
222 #[cfg_attr(
224 feature = "serde",
225 serde(default, rename = "funcs", skip_serializing_if = "StringMap::is_empty")
226 )]
227 func_imports_or_exports: StringMap<FunctionMetadata>,
228
229 #[cfg_attr(
231 feature = "serde",
232 serde(default, skip_serializing_if = "StringMap::is_empty")
233 )]
234 interface_exports: StringMap<InterfaceMetadata>,
235
236 #[cfg_attr(
238 feature = "serde",
239 serde(default, skip_serializing_if = "StringMap::is_empty")
240 )]
241 func_exports: StringMap<FunctionMetadata>,
242
243 #[cfg_attr(
253 feature = "serde",
254 serde(default, skip_serializing_if = "StringMap::is_empty")
255 )]
256 interface_import_stability: StringMap<Stability>,
257
258 #[cfg_attr(
260 feature = "serde",
261 serde(default, skip_serializing_if = "StringMap::is_empty")
262 )]
263 interface_export_stability: StringMap<Stability>,
264
265 #[cfg_attr(
275 feature = "serde",
276 serde(default, skip_serializing_if = "StringMap::is_empty")
277 )]
278 interface_import_docs: StringMap<String>,
279
280 #[cfg_attr(
282 feature = "serde",
283 serde(default, skip_serializing_if = "StringMap::is_empty")
284 )]
285 interface_export_docs: StringMap<String>,
286}
287
288impl WorldMetadata {
289 fn extract(resolve: &Resolve, id: WorldId) -> Self {
290 let world = &resolve.worlds[id];
291
292 let mut interface_imports_or_exports = StringMap::default();
293 let mut types = StringMap::default();
294 let mut func_imports_or_exports = StringMap::default();
295 let mut interface_exports = StringMap::default();
296 let mut func_exports = StringMap::default();
297 let mut interface_import_stability = StringMap::default();
298 let mut interface_export_stability = StringMap::default();
299 let mut interface_import_docs = StringMap::default();
300 let mut interface_export_docs = StringMap::default();
301
302 let mut record_interface_metadata = |key: &WorldKey, item: &WorldItem, import: bool| {
307 let (stability, docs) = match item {
308 WorldItem::Interface {
309 stability, docs, ..
310 } => (stability, docs),
311 _ => return,
312 };
313 let name = resolve.name_world_key(key);
314 if !stability.is_unknown() {
315 let map = if import {
316 &mut interface_import_stability
317 } else {
318 &mut interface_export_stability
319 };
320 map.insert(name.clone(), stability.clone());
321 }
322 if let Some(contents) = &docs.contents {
323 let map = if import {
324 &mut interface_import_docs
325 } else {
326 &mut interface_export_docs
327 };
328 map.insert(name, contents.clone());
329 }
330 };
331
332 for ((key, item), import) in world
333 .imports
334 .iter()
335 .map(|p| (p, true))
336 .chain(world.exports.iter().map(|p| (p, false)))
337 {
338 match key {
339 WorldKey::Name(name) => match item {
342 WorldItem::Interface { id, .. } => {
343 if resolve.interfaces[*id].name.is_some() {
344 record_interface_metadata(key, item, import);
345 continue;
346 }
347 let data = InterfaceMetadata::extract(resolve, *id);
348 if data.is_empty() {
349 continue;
350 }
351 let map = if import {
352 &mut interface_imports_or_exports
353 } else if !TRY_TO_EMIT_V0_BY_DEFAULT
354 || interface_imports_or_exports.contains_key(name)
355 {
356 &mut interface_exports
357 } else {
358 &mut interface_imports_or_exports
359 };
360 let prev = map.insert(name.to_string(), data);
361 assert!(prev.is_none());
362 }
363 WorldItem::Type { id, .. } => {
364 let data = TypeMetadata::extract(resolve, *id);
365 if !data.is_empty() {
366 types.insert(name.to_string(), data);
367 }
368 }
369 WorldItem::Function(f) => {
370 let data = FunctionMetadata::extract(f);
371 if data.is_empty() {
372 continue;
373 }
374 let map = if import {
375 &mut func_imports_or_exports
376 } else if !TRY_TO_EMIT_V0_BY_DEFAULT
377 || func_imports_or_exports.contains_key(name)
378 {
379 &mut func_exports
380 } else {
381 &mut func_imports_or_exports
382 };
383 let prev = map.insert(name.to_string(), data);
384 assert!(prev.is_none());
385 }
386 },
387
388 WorldKey::Interface(_) => {
391 record_interface_metadata(key, item, import);
392 }
393 }
394 }
395
396 Self {
397 docs: world.docs.contents.clone(),
398 stability: world.stability.clone(),
399 interface_imports_or_exports,
400 types,
401 func_imports_or_exports,
402 interface_exports,
403 func_exports,
404 interface_import_stability,
405 interface_export_stability,
406 interface_import_docs,
407 interface_export_docs,
408 }
409 }
410
411 fn inject(&self, resolve: &mut Resolve, id: WorldId) -> Result<()> {
412 for ((name, data), only_export) in self
415 .interface_imports_or_exports
416 .iter()
417 .map(|p| (p, false))
418 .chain(self.interface_exports.iter().map(|p| (p, true)))
419 {
420 let key = WorldKey::Name(name.to_string());
421 let world = &mut resolve.worlds[id];
422
423 let item = if only_export {
424 world.exports.get_mut(&key)
425 } else {
426 match world.imports.get_mut(&key) {
427 Some(item) => Some(item),
428 None => world.exports.get_mut(&key),
429 }
430 };
431 let Some(WorldItem::Interface { id, stability, .. }) = item else {
432 bail!("missing interface {name:?}");
433 };
434 *stability = data.stability.clone();
435 let id = *id;
436 data.inject(resolve, id)?;
437 }
438
439 for (name, data) in &self.types {
441 let key = WorldKey::Name(name.to_string());
442 let Some(WorldItem::Type { id, .. }) = resolve.worlds[id].imports.get(&key) else {
443 bail!("missing type {name:?}");
444 };
445 data.inject(resolve, *id)?;
446 }
447
448 let world = &resolve.worlds[id];
451 let stabilities = world
452 .imports
453 .iter()
454 .map(|i| (i, true))
455 .chain(world.exports.iter().map(|i| (i, false)))
456 .filter_map(|((key, item), import)| match item {
457 WorldItem::Interface { .. } => {
458 Some(((resolve.name_world_key(key), import), key.clone()))
459 }
460 _ => None,
461 })
462 .collect::<IndexMap<_, _>>();
463
464 let world = &mut resolve.worlds[id];
465
466 for ((name, stability), import) in self
469 .interface_import_stability
470 .iter()
471 .map(|p| (p, true))
472 .chain(self.interface_export_stability.iter().map(|p| (p, false)))
473 {
474 let key = match stabilities.get(&(name.clone(), import)) {
475 Some(key) => key.clone(),
476 None => bail!("missing interface `{name}`"),
477 };
478 let item = if import {
479 world.imports.get_mut(&key)
480 } else {
481 world.exports.get_mut(&key)
482 };
483 match item {
484 Some(WorldItem::Interface { stability: s, .. }) => *s = stability.clone(),
485 _ => bail!("item `{name}` wasn't an interface"),
486 }
487 }
488
489 for ((name, docs), import) in self
492 .interface_import_docs
493 .iter()
494 .map(|p| (p, true))
495 .chain(self.interface_export_docs.iter().map(|p| (p, false)))
496 {
497 let key = match stabilities.get(&(name.clone(), import)) {
498 Some(key) => key.clone(),
499 None => bail!("missing interface `{name}`"),
500 };
501 let item = if import {
502 world.imports.get_mut(&key)
503 } else {
504 world.exports.get_mut(&key)
505 };
506 match item {
507 Some(WorldItem::Interface { docs: d, .. }) => {
508 d.contents = Some(docs.clone());
509 }
510 _ => bail!("item `{name}` wasn't an interface"),
511 }
512 }
513
514 for ((name, data), only_export) in self
517 .func_imports_or_exports
518 .iter()
519 .map(|p| (p, false))
520 .chain(self.func_exports.iter().map(|p| (p, true)))
521 {
522 let key = WorldKey::Name(name.to_string());
523 let item = if only_export {
524 world.exports.get_mut(&key)
525 } else {
526 match world.imports.get_mut(&key) {
527 Some(item) => Some(item),
528 None => world.exports.get_mut(&key),
529 }
530 };
531 match item {
532 Some(WorldItem::Function(f)) => data.inject(f)?,
533 _ => bail!("missing func {name:?}"),
534 }
535 }
536 if let Some(docs) = &self.docs {
537 world.docs.contents = Some(docs.to_string());
538 }
539 world.stability = self.stability.clone();
540 Ok(())
541 }
542
543 fn is_empty(&self) -> bool {
544 self.docs.is_none()
545 && self.interface_imports_or_exports.is_empty()
546 && self.types.is_empty()
547 && self.func_imports_or_exports.is_empty()
548 && self.stability.is_unknown()
549 && self.interface_exports.is_empty()
550 && self.func_exports.is_empty()
551 && self.interface_import_stability.is_empty()
552 && self.interface_export_stability.is_empty()
553 && self.interface_import_docs.is_empty()
554 && self.interface_export_docs.is_empty()
555 }
556
557 #[cfg(feature = "serde")]
558 fn is_compatible_with_v0(&self) -> bool {
559 self.stability.is_unknown()
560 && self
561 .interface_imports_or_exports
562 .iter()
563 .all(|(_, w)| w.is_compatible_with_v0())
564 && self
565 .func_imports_or_exports
566 .iter()
567 .all(|(_, w)| w.is_compatible_with_v0())
568 && self.types.iter().all(|(_, w)| w.is_compatible_with_v0())
569 && self.interface_exports.is_empty()
572 && self.func_exports.is_empty()
573 && self.interface_import_stability.is_empty()
574 && self.interface_export_stability.is_empty()
575 && self.interface_import_docs.is_empty()
576 && self.interface_export_docs.is_empty()
577 }
578}
579
580#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
581#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
582struct InterfaceMetadata {
583 #[cfg_attr(
584 feature = "serde",
585 serde(default, skip_serializing_if = "Option::is_none")
586 )]
587 docs: Option<String>,
588 #[cfg_attr(
589 feature = "serde",
590 serde(default, skip_serializing_if = "Stability::is_unknown")
591 )]
592 stability: Stability,
593 #[cfg_attr(
594 feature = "serde",
595 serde(default, skip_serializing_if = "StringMap::is_empty")
596 )]
597 funcs: StringMap<FunctionMetadata>,
598 #[cfg_attr(
599 feature = "serde",
600 serde(default, skip_serializing_if = "StringMap::is_empty")
601 )]
602 types: StringMap<TypeMetadata>,
603}
604
605impl InterfaceMetadata {
606 fn extract(resolve: &Resolve, id: InterfaceId) -> Self {
607 let interface = &resolve.interfaces[id];
608
609 let funcs = interface
610 .functions
611 .iter()
612 .map(|(name, func)| (name.to_string(), FunctionMetadata::extract(func)))
613 .filter(|(_, item)| !item.is_empty())
614 .collect();
615 let types = interface
616 .types
617 .iter()
618 .map(|(name, id)| (name.to_string(), TypeMetadata::extract(resolve, *id)))
619 .filter(|(_, item)| !item.is_empty())
620 .collect();
621
622 Self {
623 docs: interface.docs.contents.clone(),
624 stability: interface.stability.clone(),
625 funcs,
626 types,
627 }
628 }
629
630 fn inject(&self, resolve: &mut Resolve, id: InterfaceId) -> Result<()> {
631 for (name, data) in &self.types {
632 let Some(&id) = resolve.interfaces[id].types.get(name) else {
633 bail!("missing type {name:?}");
634 };
635 data.inject(resolve, id)?;
636 }
637 let interface = &mut resolve.interfaces[id];
638 for (name, data) in &self.funcs {
639 let Some(f) = interface.functions.get_mut(name) else {
640 bail!("missing func {name:?}");
641 };
642 data.inject(f)?;
643 }
644 if let Some(docs) = &self.docs {
645 interface.docs.contents = Some(docs.to_string());
646 }
647 interface.stability = self.stability.clone();
648 Ok(())
649 }
650
651 fn is_empty(&self) -> bool {
652 self.docs.is_none()
653 && self.funcs.is_empty()
654 && self.types.is_empty()
655 && self.stability.is_unknown()
656 }
657
658 #[cfg(feature = "serde")]
659 fn is_compatible_with_v0(&self) -> bool {
660 self.stability.is_unknown()
661 && self.funcs.iter().all(|(_, w)| w.is_compatible_with_v0())
662 && self.types.iter().all(|(_, w)| w.is_compatible_with_v0())
663 }
664}
665
666#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
667#[cfg_attr(feature = "serde", serde(untagged, deny_unknown_fields))]
668enum FunctionMetadata {
669 JustDocs(Option<String>),
676
677 DocsAndStability {
680 #[cfg_attr(
681 feature = "serde",
682 serde(default, skip_serializing_if = "Option::is_none")
683 )]
684 docs: Option<String>,
685 #[cfg_attr(
686 feature = "serde",
687 serde(default, skip_serializing_if = "Stability::is_unknown")
688 )]
689 stability: Stability,
690 },
691}
692
693impl FunctionMetadata {
694 fn extract(func: &Function) -> Self {
695 if TRY_TO_EMIT_V0_BY_DEFAULT && func.stability.is_unknown() {
696 FunctionMetadata::JustDocs(func.docs.contents.clone())
697 } else {
698 FunctionMetadata::DocsAndStability {
699 docs: func.docs.contents.clone(),
700 stability: func.stability.clone(),
701 }
702 }
703 }
704
705 fn inject(&self, func: &mut Function) -> Result<()> {
706 match self {
707 FunctionMetadata::JustDocs(docs) => {
708 func.docs.contents = docs.clone();
709 }
710 FunctionMetadata::DocsAndStability { docs, stability } => {
711 func.docs.contents = docs.clone();
712 func.stability = stability.clone();
713 }
714 }
715 Ok(())
716 }
717
718 fn is_empty(&self) -> bool {
719 match self {
720 FunctionMetadata::JustDocs(docs) => docs.is_none(),
721 FunctionMetadata::DocsAndStability { docs, stability } => {
722 docs.is_none() && stability.is_unknown()
723 }
724 }
725 }
726
727 #[cfg(feature = "serde")]
728 fn is_compatible_with_v0(&self) -> bool {
729 match self {
730 FunctionMetadata::JustDocs(_) => true,
731 FunctionMetadata::DocsAndStability { .. } => false,
732 }
733 }
734}
735
736#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
737#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
738struct TypeMetadata {
739 #[cfg_attr(
740 feature = "serde",
741 serde(default, skip_serializing_if = "Option::is_none")
742 )]
743 docs: Option<String>,
744 #[cfg_attr(
745 feature = "serde",
746 serde(default, skip_serializing_if = "Stability::is_unknown")
747 )]
748 stability: Stability,
749 #[cfg_attr(
751 feature = "serde",
752 serde(default, skip_serializing_if = "StringMap::is_empty")
753 )]
754 items: StringMap<String>,
755}
756
757impl TypeMetadata {
758 fn extract(resolve: &Resolve, id: TypeId) -> Self {
759 fn extract_items<T>(items: &[T], f: impl Fn(&T) -> (&String, &Docs)) -> StringMap<String> {
760 items
761 .iter()
762 .flat_map(|item| {
763 let (name, docs) = f(item);
764 Some((name.to_string(), docs.contents.clone()?))
765 })
766 .collect()
767 }
768 let ty = &resolve.types[id];
769 let items = match &ty.kind {
770 TypeDefKind::Record(record) => {
771 extract_items(&record.fields, |item| (&item.name, &item.docs))
772 }
773 TypeDefKind::Flags(flags) => {
774 extract_items(&flags.flags, |item| (&item.name, &item.docs))
775 }
776 TypeDefKind::Variant(variant) => {
777 extract_items(&variant.cases, |item| (&item.name, &item.docs))
778 }
779 TypeDefKind::Enum(enum_) => {
780 extract_items(&enum_.cases, |item| (&item.name, &item.docs))
781 }
782 _ => IndexMap::default(),
784 };
785
786 Self {
787 docs: ty.docs.contents.clone(),
788 stability: ty.stability.clone(),
789 items,
790 }
791 }
792
793 fn inject(&self, resolve: &mut Resolve, id: TypeId) -> Result<()> {
794 let ty = &mut resolve.types[id];
795 if !self.items.is_empty() {
796 match &mut ty.kind {
797 TypeDefKind::Record(record) => {
798 self.inject_items(&mut record.fields, |item| (&item.name, &mut item.docs))?
799 }
800 TypeDefKind::Flags(flags) => {
801 self.inject_items(&mut flags.flags, |item| (&item.name, &mut item.docs))?
802 }
803 TypeDefKind::Variant(variant) => {
804 self.inject_items(&mut variant.cases, |item| (&item.name, &mut item.docs))?
805 }
806 TypeDefKind::Enum(enum_) => {
807 self.inject_items(&mut enum_.cases, |item| (&item.name, &mut item.docs))?
808 }
809 _ => {
810 bail!("got 'items' for unexpected type {ty:?}");
811 }
812 }
813 }
814 if let Some(docs) = &self.docs {
815 ty.docs.contents = Some(docs.to_string());
816 }
817 ty.stability = self.stability.clone();
818 Ok(())
819 }
820
821 fn inject_items<T: core::fmt::Debug>(
822 &self,
823 items: &mut [T],
824 f: impl Fn(&mut T) -> (&String, &mut Docs),
825 ) -> Result<()> {
826 let mut unused_docs = self.items.len();
827 for item in items.iter_mut() {
828 let (name, item_docs) = f(item);
829 if let Some(docs) = self.items.get(name.as_str()) {
830 item_docs.contents = Some(docs.to_string());
831 unused_docs -= 1;
832 }
833 }
834 if unused_docs > 0 {
835 bail!(
836 "not all 'items' match type items; {item_docs:?} vs {items:?}",
837 item_docs = self.items
838 );
839 }
840 Ok(())
841 }
842
843 fn is_empty(&self) -> bool {
844 self.docs.is_none() && self.items.is_empty() && self.stability.is_unknown()
845 }
846
847 #[cfg(feature = "serde")]
848 fn is_compatible_with_v0(&self) -> bool {
849 self.stability.is_unknown()
850 }
851}