1use crate::{
18 Docs, Function, InterfaceId, PackageId, Resolve, Stability, TypeDefKind, TypeId, WorldId,
19 WorldItem, WorldKey,
20};
21use anyhow::{bail, Result};
22use indexmap::IndexMap;
23#[cfg(feature = "serde")]
24use serde_derive::{Deserialize, Serialize};
25
26type StringMap<V> = IndexMap<String, V>;
27
28#[cfg(feature = "serde")]
40const PACKAGE_DOCS_SECTION_VERSION: u8 = 1;
41
42const TRY_TO_EMIT_V0_BY_DEFAULT: bool = true;
47
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
50#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
51pub struct PackageMetadata {
52 #[cfg_attr(
53 feature = "serde",
54 serde(default, skip_serializing_if = "Option::is_none")
55 )]
56 docs: Option<String>,
57 #[cfg_attr(
58 feature = "serde",
59 serde(default, skip_serializing_if = "StringMap::is_empty")
60 )]
61 worlds: StringMap<WorldMetadata>,
62 #[cfg_attr(
63 feature = "serde",
64 serde(default, skip_serializing_if = "StringMap::is_empty")
65 )]
66 interfaces: StringMap<InterfaceMetadata>,
67}
68
69impl PackageMetadata {
70 pub const SECTION_NAME: &'static str = "package-docs";
71
72 pub fn extract(resolve: &Resolve, package: PackageId) -> Self {
74 let package = &resolve.packages[package];
75
76 let worlds = package
77 .worlds
78 .iter()
79 .map(|(name, id)| (name.to_string(), WorldMetadata::extract(resolve, *id)))
80 .filter(|(_, item)| !item.is_empty())
81 .collect();
82 let interfaces = package
83 .interfaces
84 .iter()
85 .map(|(name, id)| (name.to_string(), InterfaceMetadata::extract(resolve, *id)))
86 .filter(|(_, item)| !item.is_empty())
87 .collect();
88
89 Self {
90 docs: package.docs.contents.as_deref().map(Into::into),
91 worlds,
92 interfaces,
93 }
94 }
95
96 pub fn inject(&self, resolve: &mut Resolve, package: PackageId) -> Result<()> {
100 for (name, docs) in &self.worlds {
101 let Some(&id) = resolve.packages[package].worlds.get(name) else {
102 bail!("missing world {name:?}");
103 };
104 docs.inject(resolve, id)?;
105 }
106 for (name, docs) in &self.interfaces {
107 let Some(&id) = resolve.packages[package].interfaces.get(name) else {
108 bail!("missing interface {name:?}");
109 };
110 docs.inject(resolve, id)?;
111 }
112 if let Some(docs) = &self.docs {
113 resolve.packages[package].docs.contents = Some(docs.to_string());
114 }
115 Ok(())
116 }
117
118 #[cfg(feature = "serde")]
120 pub fn encode(&self) -> Result<Vec<u8>> {
121 let mut data = vec![
128 if TRY_TO_EMIT_V0_BY_DEFAULT && self.is_compatible_with_v0() {
129 0
130 } else {
131 PACKAGE_DOCS_SECTION_VERSION
132 },
133 ];
134 serde_json::to_writer(&mut data, self)?;
135 Ok(data)
136 }
137
138 #[cfg(feature = "serde")]
140 pub fn decode(data: &[u8]) -> Result<Self> {
141 match data.first().copied() {
142 Some(0) | Some(PACKAGE_DOCS_SECTION_VERSION) => {}
145 version => {
146 bail!(
147 "expected package-docs version {PACKAGE_DOCS_SECTION_VERSION}, got {version:?}"
148 );
149 }
150 }
151 Ok(serde_json::from_slice(&data[1..])?)
152 }
153
154 #[cfg(feature = "serde")]
155 fn is_compatible_with_v0(&self) -> bool {
156 self.worlds.iter().all(|(_, w)| w.is_compatible_with_v0())
157 && self
158 .interfaces
159 .iter()
160 .all(|(_, w)| w.is_compatible_with_v0())
161 }
162}
163
164#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
165#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
166struct WorldMetadata {
167 #[cfg_attr(
168 feature = "serde",
169 serde(default, skip_serializing_if = "Option::is_none")
170 )]
171 docs: Option<String>,
172 #[cfg_attr(
173 feature = "serde",
174 serde(default, skip_serializing_if = "Stability::is_unknown")
175 )]
176 stability: Stability,
177
178 #[cfg_attr(
200 feature = "serde",
201 serde(
202 default,
203 rename = "interfaces",
204 skip_serializing_if = "StringMap::is_empty"
205 )
206 )]
207 interface_imports_or_exports: StringMap<InterfaceMetadata>,
208
209 #[cfg_attr(
213 feature = "serde",
214 serde(default, skip_serializing_if = "StringMap::is_empty")
215 )]
216 types: StringMap<TypeMetadata>,
217
218 #[cfg_attr(
220 feature = "serde",
221 serde(default, rename = "funcs", skip_serializing_if = "StringMap::is_empty")
222 )]
223 func_imports_or_exports: StringMap<FunctionMetadata>,
224
225 #[cfg_attr(
227 feature = "serde",
228 serde(default, skip_serializing_if = "StringMap::is_empty")
229 )]
230 interface_exports: StringMap<InterfaceMetadata>,
231
232 #[cfg_attr(
234 feature = "serde",
235 serde(default, skip_serializing_if = "StringMap::is_empty")
236 )]
237 func_exports: StringMap<FunctionMetadata>,
238
239 #[cfg_attr(
249 feature = "serde",
250 serde(default, skip_serializing_if = "StringMap::is_empty")
251 )]
252 interface_import_stability: StringMap<Stability>,
253
254 #[cfg_attr(
256 feature = "serde",
257 serde(default, skip_serializing_if = "StringMap::is_empty")
258 )]
259 interface_export_stability: StringMap<Stability>,
260}
261
262impl WorldMetadata {
263 fn extract(resolve: &Resolve, id: WorldId) -> Self {
264 let world = &resolve.worlds[id];
265
266 let mut interface_imports_or_exports = StringMap::default();
267 let mut types = StringMap::default();
268 let mut func_imports_or_exports = StringMap::default();
269 let mut interface_exports = StringMap::default();
270 let mut func_exports = StringMap::default();
271 let mut interface_import_stability = StringMap::default();
272 let mut interface_export_stability = StringMap::default();
273
274 for ((key, item), import) in world
275 .imports
276 .iter()
277 .map(|p| (p, true))
278 .chain(world.exports.iter().map(|p| (p, false)))
279 {
280 match key {
281 WorldKey::Name(name) => match item {
284 WorldItem::Interface { id, .. } => {
285 let data = InterfaceMetadata::extract(resolve, *id);
286 if data.is_empty() {
287 continue;
288 }
289 let map = if import {
290 &mut interface_imports_or_exports
291 } else if !TRY_TO_EMIT_V0_BY_DEFAULT
292 || interface_imports_or_exports.contains_key(name)
293 {
294 &mut interface_exports
295 } else {
296 &mut interface_imports_or_exports
297 };
298 let prev = map.insert(name.to_string(), data);
299 assert!(prev.is_none());
300 }
301 WorldItem::Type(id) => {
302 let data = TypeMetadata::extract(resolve, *id);
303 if !data.is_empty() {
304 types.insert(name.to_string(), data);
305 }
306 }
307 WorldItem::Function(f) => {
308 let data = FunctionMetadata::extract(f);
309 if data.is_empty() {
310 continue;
311 }
312 let map = if import {
313 &mut func_imports_or_exports
314 } else if !TRY_TO_EMIT_V0_BY_DEFAULT
315 || func_imports_or_exports.contains_key(name)
316 {
317 &mut func_exports
318 } else {
319 &mut func_imports_or_exports
320 };
321 let prev = map.insert(name.to_string(), data);
322 assert!(prev.is_none());
323 }
324 },
325
326 WorldKey::Interface(_) => {
329 let stability = match item {
330 WorldItem::Interface { stability, .. } => stability,
331 _ => continue,
332 };
333 if stability.is_unknown() {
334 continue;
335 }
336
337 let map = if import {
338 &mut interface_import_stability
339 } else {
340 &mut interface_export_stability
341 };
342 let name = resolve.name_world_key(key);
343 map.insert(name, stability.clone());
344 }
345 }
346 }
347
348 Self {
349 docs: world.docs.contents.clone(),
350 stability: world.stability.clone(),
351 interface_imports_or_exports,
352 types,
353 func_imports_or_exports,
354 interface_exports,
355 func_exports,
356 interface_import_stability,
357 interface_export_stability,
358 }
359 }
360
361 fn inject(&self, resolve: &mut Resolve, id: WorldId) -> Result<()> {
362 for ((name, data), only_export) in self
365 .interface_imports_or_exports
366 .iter()
367 .map(|p| (p, false))
368 .chain(self.interface_exports.iter().map(|p| (p, true)))
369 {
370 let key = WorldKey::Name(name.to_string());
371 let world = &mut resolve.worlds[id];
372
373 let item = if only_export {
374 world.exports.get_mut(&key)
375 } else {
376 match world.imports.get_mut(&key) {
377 Some(item) => Some(item),
378 None => world.exports.get_mut(&key),
379 }
380 };
381 let Some(WorldItem::Interface { id, stability }) = item else {
382 bail!("missing interface {name:?}");
383 };
384 *stability = data.stability.clone();
385 let id = *id;
386 data.inject(resolve, id)?;
387 }
388
389 for (name, data) in &self.types {
391 let key = WorldKey::Name(name.to_string());
392 let Some(WorldItem::Type(id)) = resolve.worlds[id].imports.get(&key) else {
393 bail!("missing type {name:?}");
394 };
395 data.inject(resolve, *id)?;
396 }
397
398 let world = &resolve.worlds[id];
401 let stabilities = world
402 .imports
403 .iter()
404 .map(|i| (i, true))
405 .chain(world.exports.iter().map(|i| (i, false)))
406 .filter_map(|((key, item), import)| match item {
407 WorldItem::Interface { .. } => {
408 Some(((resolve.name_world_key(key), import), key.clone()))
409 }
410 _ => None,
411 })
412 .collect::<IndexMap<_, _>>();
413
414 let world = &mut resolve.worlds[id];
415
416 for ((name, stability), import) in self
419 .interface_import_stability
420 .iter()
421 .map(|p| (p, true))
422 .chain(self.interface_export_stability.iter().map(|p| (p, false)))
423 {
424 let key = match stabilities.get(&(name.clone(), import)) {
425 Some(key) => key.clone(),
426 None => bail!("missing interface `{name}`"),
427 };
428 let item = if import {
429 world.imports.get_mut(&key)
430 } else {
431 world.exports.get_mut(&key)
432 };
433 match item {
434 Some(WorldItem::Interface { stability: s, .. }) => *s = stability.clone(),
435 _ => bail!("item `{name}` wasn't an interface"),
436 }
437 }
438
439 for ((name, data), only_export) in self
442 .func_imports_or_exports
443 .iter()
444 .map(|p| (p, false))
445 .chain(self.func_exports.iter().map(|p| (p, true)))
446 {
447 let key = WorldKey::Name(name.to_string());
448 let item = if only_export {
449 world.exports.get_mut(&key)
450 } else {
451 match world.imports.get_mut(&key) {
452 Some(item) => Some(item),
453 None => world.exports.get_mut(&key),
454 }
455 };
456 match item {
457 Some(WorldItem::Function(f)) => data.inject(f)?,
458 _ => bail!("missing func {name:?}"),
459 }
460 }
461 if let Some(docs) = &self.docs {
462 world.docs.contents = Some(docs.to_string());
463 }
464 world.stability = self.stability.clone();
465 Ok(())
466 }
467
468 fn is_empty(&self) -> bool {
469 self.docs.is_none()
470 && self.interface_imports_or_exports.is_empty()
471 && self.types.is_empty()
472 && self.func_imports_or_exports.is_empty()
473 && self.stability.is_unknown()
474 && self.interface_exports.is_empty()
475 && self.func_exports.is_empty()
476 && self.interface_import_stability.is_empty()
477 && self.interface_export_stability.is_empty()
478 }
479
480 #[cfg(feature = "serde")]
481 fn is_compatible_with_v0(&self) -> bool {
482 self.stability.is_unknown()
483 && self
484 .interface_imports_or_exports
485 .iter()
486 .all(|(_, w)| w.is_compatible_with_v0())
487 && self
488 .func_imports_or_exports
489 .iter()
490 .all(|(_, w)| w.is_compatible_with_v0())
491 && self.types.iter().all(|(_, w)| w.is_compatible_with_v0())
492 && self.interface_exports.is_empty()
495 && self.func_exports.is_empty()
496 && self.interface_import_stability.is_empty()
497 && self.interface_export_stability.is_empty()
498 }
499}
500
501#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
502#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
503struct InterfaceMetadata {
504 #[cfg_attr(
505 feature = "serde",
506 serde(default, skip_serializing_if = "Option::is_none")
507 )]
508 docs: Option<String>,
509 #[cfg_attr(
510 feature = "serde",
511 serde(default, skip_serializing_if = "Stability::is_unknown")
512 )]
513 stability: Stability,
514 #[cfg_attr(
515 feature = "serde",
516 serde(default, skip_serializing_if = "StringMap::is_empty")
517 )]
518 funcs: StringMap<FunctionMetadata>,
519 #[cfg_attr(
520 feature = "serde",
521 serde(default, skip_serializing_if = "StringMap::is_empty")
522 )]
523 types: StringMap<TypeMetadata>,
524}
525
526impl InterfaceMetadata {
527 fn extract(resolve: &Resolve, id: InterfaceId) -> Self {
528 let interface = &resolve.interfaces[id];
529
530 let funcs = interface
531 .functions
532 .iter()
533 .map(|(name, func)| (name.to_string(), FunctionMetadata::extract(func)))
534 .filter(|(_, item)| !item.is_empty())
535 .collect();
536 let types = interface
537 .types
538 .iter()
539 .map(|(name, id)| (name.to_string(), TypeMetadata::extract(resolve, *id)))
540 .filter(|(_, item)| !item.is_empty())
541 .collect();
542
543 Self {
544 docs: interface.docs.contents.clone(),
545 stability: interface.stability.clone(),
546 funcs,
547 types,
548 }
549 }
550
551 fn inject(&self, resolve: &mut Resolve, id: InterfaceId) -> Result<()> {
552 for (name, data) in &self.types {
553 let Some(&id) = resolve.interfaces[id].types.get(name) else {
554 bail!("missing type {name:?}");
555 };
556 data.inject(resolve, id)?;
557 }
558 let interface = &mut resolve.interfaces[id];
559 for (name, data) in &self.funcs {
560 let Some(f) = interface.functions.get_mut(name) else {
561 bail!("missing func {name:?}");
562 };
563 data.inject(f)?;
564 }
565 if let Some(docs) = &self.docs {
566 interface.docs.contents = Some(docs.to_string());
567 }
568 interface.stability = self.stability.clone();
569 Ok(())
570 }
571
572 fn is_empty(&self) -> bool {
573 self.docs.is_none()
574 && self.funcs.is_empty()
575 && self.types.is_empty()
576 && self.stability.is_unknown()
577 }
578
579 #[cfg(feature = "serde")]
580 fn is_compatible_with_v0(&self) -> bool {
581 self.stability.is_unknown()
582 && self.funcs.iter().all(|(_, w)| w.is_compatible_with_v0())
583 && self.types.iter().all(|(_, w)| w.is_compatible_with_v0())
584 }
585}
586
587#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
588#[cfg_attr(feature = "serde", serde(untagged, deny_unknown_fields))]
589enum FunctionMetadata {
590 JustDocs(Option<String>),
597
598 DocsAndStabilty {
601 #[cfg_attr(
602 feature = "serde",
603 serde(default, skip_serializing_if = "Option::is_none")
604 )]
605 docs: Option<String>,
606 #[cfg_attr(
607 feature = "serde",
608 serde(default, skip_serializing_if = "Stability::is_unknown")
609 )]
610 stability: Stability,
611 },
612}
613
614impl FunctionMetadata {
615 fn extract(func: &Function) -> Self {
616 if TRY_TO_EMIT_V0_BY_DEFAULT && func.stability.is_unknown() {
617 FunctionMetadata::JustDocs(func.docs.contents.clone())
618 } else {
619 FunctionMetadata::DocsAndStabilty {
620 docs: func.docs.contents.clone(),
621 stability: func.stability.clone(),
622 }
623 }
624 }
625
626 fn inject(&self, func: &mut Function) -> Result<()> {
627 match self {
628 FunctionMetadata::JustDocs(docs) => {
629 func.docs.contents = docs.clone();
630 }
631 FunctionMetadata::DocsAndStabilty { docs, stability } => {
632 func.docs.contents = docs.clone();
633 func.stability = stability.clone();
634 }
635 }
636 Ok(())
637 }
638
639 fn is_empty(&self) -> bool {
640 match self {
641 FunctionMetadata::JustDocs(docs) => docs.is_none(),
642 FunctionMetadata::DocsAndStabilty { docs, stability } => {
643 docs.is_none() && stability.is_unknown()
644 }
645 }
646 }
647
648 #[cfg(feature = "serde")]
649 fn is_compatible_with_v0(&self) -> bool {
650 match self {
651 FunctionMetadata::JustDocs(_) => true,
652 FunctionMetadata::DocsAndStabilty { .. } => false,
653 }
654 }
655}
656
657#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
658#[cfg_attr(feature = "serde", serde(deny_unknown_fields))]
659struct TypeMetadata {
660 #[cfg_attr(
661 feature = "serde",
662 serde(default, skip_serializing_if = "Option::is_none")
663 )]
664 docs: Option<String>,
665 #[cfg_attr(
666 feature = "serde",
667 serde(default, skip_serializing_if = "Stability::is_unknown")
668 )]
669 stability: Stability,
670 #[cfg_attr(
672 feature = "serde",
673 serde(default, skip_serializing_if = "StringMap::is_empty")
674 )]
675 items: StringMap<String>,
676}
677
678impl TypeMetadata {
679 fn extract(resolve: &Resolve, id: TypeId) -> Self {
680 fn extract_items<T>(items: &[T], f: impl Fn(&T) -> (&String, &Docs)) -> StringMap<String> {
681 items
682 .iter()
683 .flat_map(|item| {
684 let (name, docs) = f(item);
685 Some((name.to_string(), docs.contents.clone()?))
686 })
687 .collect()
688 }
689 let ty = &resolve.types[id];
690 let items = match &ty.kind {
691 TypeDefKind::Record(record) => {
692 extract_items(&record.fields, |item| (&item.name, &item.docs))
693 }
694 TypeDefKind::Flags(flags) => {
695 extract_items(&flags.flags, |item| (&item.name, &item.docs))
696 }
697 TypeDefKind::Variant(variant) => {
698 extract_items(&variant.cases, |item| (&item.name, &item.docs))
699 }
700 TypeDefKind::Enum(enum_) => {
701 extract_items(&enum_.cases, |item| (&item.name, &item.docs))
702 }
703 _ => IndexMap::default(),
705 };
706
707 Self {
708 docs: ty.docs.contents.clone(),
709 stability: ty.stability.clone(),
710 items,
711 }
712 }
713
714 fn inject(&self, resolve: &mut Resolve, id: TypeId) -> Result<()> {
715 let ty = &mut resolve.types[id];
716 if !self.items.is_empty() {
717 match &mut ty.kind {
718 TypeDefKind::Record(record) => {
719 self.inject_items(&mut record.fields, |item| (&item.name, &mut item.docs))?
720 }
721 TypeDefKind::Flags(flags) => {
722 self.inject_items(&mut flags.flags, |item| (&item.name, &mut item.docs))?
723 }
724 TypeDefKind::Variant(variant) => {
725 self.inject_items(&mut variant.cases, |item| (&item.name, &mut item.docs))?
726 }
727 TypeDefKind::Enum(enum_) => {
728 self.inject_items(&mut enum_.cases, |item| (&item.name, &mut item.docs))?
729 }
730 _ => {
731 bail!("got 'items' for unexpected type {ty:?}");
732 }
733 }
734 }
735 if let Some(docs) = &self.docs {
736 ty.docs.contents = Some(docs.to_string());
737 }
738 ty.stability = self.stability.clone();
739 Ok(())
740 }
741
742 fn inject_items<T: std::fmt::Debug>(
743 &self,
744 items: &mut [T],
745 f: impl Fn(&mut T) -> (&String, &mut Docs),
746 ) -> Result<()> {
747 let mut unused_docs = self.items.len();
748 for item in items.iter_mut() {
749 let (name, item_docs) = f(item);
750 if let Some(docs) = self.items.get(name.as_str()) {
751 item_docs.contents = Some(docs.to_string());
752 unused_docs -= 1;
753 }
754 }
755 if unused_docs > 0 {
756 bail!(
757 "not all 'items' match type items; {item_docs:?} vs {items:?}",
758 item_docs = self.items
759 );
760 }
761 Ok(())
762 }
763
764 fn is_empty(&self) -> bool {
765 self.docs.is_none() && self.items.is_empty() && self.stability.is_unknown()
766 }
767
768 #[cfg(feature = "serde")]
769 fn is_compatible_with_v0(&self) -> bool {
770 self.stability.is_unknown()
771 }
772}