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
266impl WorldMetadata {
267 fn extract(resolve: &Resolve, id: WorldId) -> Self {
268 let world = &resolve.worlds[id];
269
270 let mut interface_imports_or_exports = StringMap::default();
271 let mut types = StringMap::default();
272 let mut func_imports_or_exports = StringMap::default();
273 let mut interface_exports = StringMap::default();
274 let mut func_exports = StringMap::default();
275 let mut interface_import_stability = StringMap::default();
276 let mut interface_export_stability = StringMap::default();
277
278 for ((key, item), import) in world
279 .imports
280 .iter()
281 .map(|p| (p, true))
282 .chain(world.exports.iter().map(|p| (p, false)))
283 {
284 match key {
285 WorldKey::Name(name) => match item {
288 WorldItem::Interface { id, .. } => {
289 let data = InterfaceMetadata::extract(resolve, *id);
290 if data.is_empty() {
291 continue;
292 }
293 let map = if import {
294 &mut interface_imports_or_exports
295 } else if !TRY_TO_EMIT_V0_BY_DEFAULT
296 || interface_imports_or_exports.contains_key(name)
297 {
298 &mut interface_exports
299 } else {
300 &mut interface_imports_or_exports
301 };
302 let prev = map.insert(name.to_string(), data);
303 assert!(prev.is_none());
304 }
305 WorldItem::Type { id, .. } => {
306 let data = TypeMetadata::extract(resolve, *id);
307 if !data.is_empty() {
308 types.insert(name.to_string(), data);
309 }
310 }
311 WorldItem::Function(f) => {
312 let data = FunctionMetadata::extract(f);
313 if data.is_empty() {
314 continue;
315 }
316 let map = if import {
317 &mut func_imports_or_exports
318 } else if !TRY_TO_EMIT_V0_BY_DEFAULT
319 || func_imports_or_exports.contains_key(name)
320 {
321 &mut func_exports
322 } else {
323 &mut func_imports_or_exports
324 };
325 let prev = map.insert(name.to_string(), data);
326 assert!(prev.is_none());
327 }
328 },
329
330 WorldKey::Interface(_) => {
333 let stability = match item {
334 WorldItem::Interface { stability, .. } => stability,
335 _ => continue,
336 };
337 if stability.is_unknown() {
338 continue;
339 }
340
341 let map = if import {
342 &mut interface_import_stability
343 } else {
344 &mut interface_export_stability
345 };
346 let name = resolve.name_world_key(key);
347 map.insert(name, stability.clone());
348 }
349 }
350 }
351
352 Self {
353 docs: world.docs.contents.clone(),
354 stability: world.stability.clone(),
355 interface_imports_or_exports,
356 types,
357 func_imports_or_exports,
358 interface_exports,
359 func_exports,
360 interface_import_stability,
361 interface_export_stability,
362 }
363 }
364
365 fn inject(&self, resolve: &mut Resolve, id: WorldId) -> Result<()> {
366 for ((name, data), only_export) in self
369 .interface_imports_or_exports
370 .iter()
371 .map(|p| (p, false))
372 .chain(self.interface_exports.iter().map(|p| (p, true)))
373 {
374 let key = WorldKey::Name(name.to_string());
375 let world = &mut resolve.worlds[id];
376
377 let item = if only_export {
378 world.exports.get_mut(&key)
379 } else {
380 match world.imports.get_mut(&key) {
381 Some(item) => Some(item),
382 None => world.exports.get_mut(&key),
383 }
384 };
385 let Some(WorldItem::Interface { id, stability, .. }) = item else {
386 bail!("missing interface {name:?}");
387 };
388 *stability = data.stability.clone();
389 let id = *id;
390 data.inject(resolve, id)?;
391 }
392
393 for (name, data) in &self.types {
395 let key = WorldKey::Name(name.to_string());
396 let Some(WorldItem::Type { id, .. }) = resolve.worlds[id].imports.get(&key) else {
397 bail!("missing type {name:?}");
398 };
399 data.inject(resolve, *id)?;
400 }
401
402 let world = &resolve.worlds[id];
405 let stabilities = world
406 .imports
407 .iter()
408 .map(|i| (i, true))
409 .chain(world.exports.iter().map(|i| (i, false)))
410 .filter_map(|((key, item), import)| match item {
411 WorldItem::Interface { .. } => {
412 Some(((resolve.name_world_key(key), import), key.clone()))
413 }
414 _ => None,
415 })
416 .collect::<IndexMap<_, _>>();
417
418 let world = &mut resolve.worlds[id];
419
420 for ((name, stability), import) in self
423 .interface_import_stability
424 .iter()
425 .map(|p| (p, true))
426 .chain(self.interface_export_stability.iter().map(|p| (p, false)))
427 {
428 let key = match stabilities.get(&(name.clone(), import)) {
429 Some(key) => key.clone(),
430 None => bail!("missing interface `{name}`"),
431 };
432 let item = if import {
433 world.imports.get_mut(&key)
434 } else {
435 world.exports.get_mut(&key)
436 };
437 match item {
438 Some(WorldItem::Interface { stability: s, .. }) => *s = stability.clone(),
439 _ => bail!("item `{name}` wasn't an interface"),
440 }
441 }
442
443 for ((name, data), only_export) in self
446 .func_imports_or_exports
447 .iter()
448 .map(|p| (p, false))
449 .chain(self.func_exports.iter().map(|p| (p, true)))
450 {
451 let key = WorldKey::Name(name.to_string());
452 let item = if only_export {
453 world.exports.get_mut(&key)
454 } else {
455 match world.imports.get_mut(&key) {
456 Some(item) => Some(item),
457 None => world.exports.get_mut(&key),
458 }
459 };
460 match item {
461 Some(WorldItem::Function(f)) => data.inject(f)?,
462 _ => bail!("missing func {name:?}"),
463 }
464 }
465 if let Some(docs) = &self.docs {
466 world.docs.contents = Some(docs.to_string());
467 }
468 world.stability = self.stability.clone();
469 Ok(())
470 }
471
472 fn is_empty(&self) -> bool {
473 self.docs.is_none()
474 && self.interface_imports_or_exports.is_empty()
475 && self.types.is_empty()
476 && self.func_imports_or_exports.is_empty()
477 && self.stability.is_unknown()
478 && self.interface_exports.is_empty()
479 && self.func_exports.is_empty()
480 && self.interface_import_stability.is_empty()
481 && self.interface_export_stability.is_empty()
482 }
483
484 #[cfg(feature = "serde")]
485 fn is_compatible_with_v0(&self) -> bool {
486 self.stability.is_unknown()
487 && self
488 .interface_imports_or_exports
489 .iter()
490 .all(|(_, w)| w.is_compatible_with_v0())
491 && self
492 .func_imports_or_exports
493 .iter()
494 .all(|(_, w)| w.is_compatible_with_v0())
495 && self.types.iter().all(|(_, w)| w.is_compatible_with_v0())
496 && self.interface_exports.is_empty()
499 && self.func_exports.is_empty()
500 && self.interface_import_stability.is_empty()
501 && self.interface_export_stability.is_empty()
502 }
503}
504
505#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
506#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
507struct InterfaceMetadata {
508 #[cfg_attr(
509 feature = "serde",
510 serde(default, skip_serializing_if = "Option::is_none")
511 )]
512 docs: Option<String>,
513 #[cfg_attr(
514 feature = "serde",
515 serde(default, skip_serializing_if = "Stability::is_unknown")
516 )]
517 stability: Stability,
518 #[cfg_attr(
519 feature = "serde",
520 serde(default, skip_serializing_if = "StringMap::is_empty")
521 )]
522 funcs: StringMap<FunctionMetadata>,
523 #[cfg_attr(
524 feature = "serde",
525 serde(default, skip_serializing_if = "StringMap::is_empty")
526 )]
527 types: StringMap<TypeMetadata>,
528}
529
530impl InterfaceMetadata {
531 fn extract(resolve: &Resolve, id: InterfaceId) -> Self {
532 let interface = &resolve.interfaces[id];
533
534 let funcs = interface
535 .functions
536 .iter()
537 .map(|(name, func)| (name.to_string(), FunctionMetadata::extract(func)))
538 .filter(|(_, item)| !item.is_empty())
539 .collect();
540 let types = interface
541 .types
542 .iter()
543 .map(|(name, id)| (name.to_string(), TypeMetadata::extract(resolve, *id)))
544 .filter(|(_, item)| !item.is_empty())
545 .collect();
546
547 Self {
548 docs: interface.docs.contents.clone(),
549 stability: interface.stability.clone(),
550 funcs,
551 types,
552 }
553 }
554
555 fn inject(&self, resolve: &mut Resolve, id: InterfaceId) -> Result<()> {
556 for (name, data) in &self.types {
557 let Some(&id) = resolve.interfaces[id].types.get(name) else {
558 bail!("missing type {name:?}");
559 };
560 data.inject(resolve, id)?;
561 }
562 let interface = &mut resolve.interfaces[id];
563 for (name, data) in &self.funcs {
564 let Some(f) = interface.functions.get_mut(name) else {
565 bail!("missing func {name:?}");
566 };
567 data.inject(f)?;
568 }
569 if let Some(docs) = &self.docs {
570 interface.docs.contents = Some(docs.to_string());
571 }
572 interface.stability = self.stability.clone();
573 Ok(())
574 }
575
576 fn is_empty(&self) -> bool {
577 self.docs.is_none()
578 && self.funcs.is_empty()
579 && self.types.is_empty()
580 && self.stability.is_unknown()
581 }
582
583 #[cfg(feature = "serde")]
584 fn is_compatible_with_v0(&self) -> bool {
585 self.stability.is_unknown()
586 && self.funcs.iter().all(|(_, w)| w.is_compatible_with_v0())
587 && self.types.iter().all(|(_, w)| w.is_compatible_with_v0())
588 }
589}
590
591#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
592#[cfg_attr(feature = "serde", serde(untagged, deny_unknown_fields))]
593enum FunctionMetadata {
594 JustDocs(Option<String>),
601
602 DocsAndStabilty {
605 #[cfg_attr(
606 feature = "serde",
607 serde(default, skip_serializing_if = "Option::is_none")
608 )]
609 docs: Option<String>,
610 #[cfg_attr(
611 feature = "serde",
612 serde(default, skip_serializing_if = "Stability::is_unknown")
613 )]
614 stability: Stability,
615 },
616}
617
618impl FunctionMetadata {
619 fn extract(func: &Function) -> Self {
620 if TRY_TO_EMIT_V0_BY_DEFAULT && func.stability.is_unknown() {
621 FunctionMetadata::JustDocs(func.docs.contents.clone())
622 } else {
623 FunctionMetadata::DocsAndStabilty {
624 docs: func.docs.contents.clone(),
625 stability: func.stability.clone(),
626 }
627 }
628 }
629
630 fn inject(&self, func: &mut Function) -> Result<()> {
631 match self {
632 FunctionMetadata::JustDocs(docs) => {
633 func.docs.contents = docs.clone();
634 }
635 FunctionMetadata::DocsAndStabilty { docs, stability } => {
636 func.docs.contents = docs.clone();
637 func.stability = stability.clone();
638 }
639 }
640 Ok(())
641 }
642
643 fn is_empty(&self) -> bool {
644 match self {
645 FunctionMetadata::JustDocs(docs) => docs.is_none(),
646 FunctionMetadata::DocsAndStabilty { docs, stability } => {
647 docs.is_none() && stability.is_unknown()
648 }
649 }
650 }
651
652 #[cfg(feature = "serde")]
653 fn is_compatible_with_v0(&self) -> bool {
654 match self {
655 FunctionMetadata::JustDocs(_) => true,
656 FunctionMetadata::DocsAndStabilty { .. } => false,
657 }
658 }
659}
660
661#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
662#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
663struct TypeMetadata {
664 #[cfg_attr(
665 feature = "serde",
666 serde(default, skip_serializing_if = "Option::is_none")
667 )]
668 docs: Option<String>,
669 #[cfg_attr(
670 feature = "serde",
671 serde(default, skip_serializing_if = "Stability::is_unknown")
672 )]
673 stability: Stability,
674 #[cfg_attr(
676 feature = "serde",
677 serde(default, skip_serializing_if = "StringMap::is_empty")
678 )]
679 items: StringMap<String>,
680}
681
682impl TypeMetadata {
683 fn extract(resolve: &Resolve, id: TypeId) -> Self {
684 fn extract_items<T>(items: &[T], f: impl Fn(&T) -> (&String, &Docs)) -> StringMap<String> {
685 items
686 .iter()
687 .flat_map(|item| {
688 let (name, docs) = f(item);
689 Some((name.to_string(), docs.contents.clone()?))
690 })
691 .collect()
692 }
693 let ty = &resolve.types[id];
694 let items = match &ty.kind {
695 TypeDefKind::Record(record) => {
696 extract_items(&record.fields, |item| (&item.name, &item.docs))
697 }
698 TypeDefKind::Flags(flags) => {
699 extract_items(&flags.flags, |item| (&item.name, &item.docs))
700 }
701 TypeDefKind::Variant(variant) => {
702 extract_items(&variant.cases, |item| (&item.name, &item.docs))
703 }
704 TypeDefKind::Enum(enum_) => {
705 extract_items(&enum_.cases, |item| (&item.name, &item.docs))
706 }
707 _ => IndexMap::default(),
709 };
710
711 Self {
712 docs: ty.docs.contents.clone(),
713 stability: ty.stability.clone(),
714 items,
715 }
716 }
717
718 fn inject(&self, resolve: &mut Resolve, id: TypeId) -> Result<()> {
719 let ty = &mut resolve.types[id];
720 if !self.items.is_empty() {
721 match &mut ty.kind {
722 TypeDefKind::Record(record) => {
723 self.inject_items(&mut record.fields, |item| (&item.name, &mut item.docs))?
724 }
725 TypeDefKind::Flags(flags) => {
726 self.inject_items(&mut flags.flags, |item| (&item.name, &mut item.docs))?
727 }
728 TypeDefKind::Variant(variant) => {
729 self.inject_items(&mut variant.cases, |item| (&item.name, &mut item.docs))?
730 }
731 TypeDefKind::Enum(enum_) => {
732 self.inject_items(&mut enum_.cases, |item| (&item.name, &mut item.docs))?
733 }
734 _ => {
735 bail!("got 'items' for unexpected type {ty:?}");
736 }
737 }
738 }
739 if let Some(docs) = &self.docs {
740 ty.docs.contents = Some(docs.to_string());
741 }
742 ty.stability = self.stability.clone();
743 Ok(())
744 }
745
746 fn inject_items<T: core::fmt::Debug>(
747 &self,
748 items: &mut [T],
749 f: impl Fn(&mut T) -> (&String, &mut Docs),
750 ) -> Result<()> {
751 let mut unused_docs = self.items.len();
752 for item in items.iter_mut() {
753 let (name, item_docs) = f(item);
754 if let Some(docs) = self.items.get(name.as_str()) {
755 item_docs.contents = Some(docs.to_string());
756 unused_docs -= 1;
757 }
758 }
759 if unused_docs > 0 {
760 bail!(
761 "not all 'items' match type items; {item_docs:?} vs {items:?}",
762 item_docs = self.items
763 );
764 }
765 Ok(())
766 }
767
768 fn is_empty(&self) -> bool {
769 self.docs.is_none() && self.items.is_empty() && self.stability.is_unknown()
770 }
771
772 #[cfg(feature = "serde")]
773 fn is_compatible_with_v0(&self) -> bool {
774 self.stability.is_unknown()
775 }
776}