1// Copyright (c) 2024 Jonas Schäfer <jonas@zombofant.net>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7//! Infrastructure for contextual error messages
8
9use std::fmt;
10
11use syn::*;
12
13/// Reference to a compound field's parent
14///
15/// This reference can be converted to a hopefully-useful human-readable
16/// string via [`std::fmt::Display`].
17#[derive(Clone, Debug)]
18pub(super) enum ParentRef {
19 /// The parent is addressable by a path, e.g. a struct type or enum
20 /// variant.
21 Named(Path),
22}
23
24impl From<Path> for ParentRef {
25 fn from(other: Path) -> Self {
26 Self::Named(other)
27 }
28}
29
30impl From<&Path> for ParentRef {
31 fn from(other: &Path) -> Self {
32 Self::Named(other.clone())
33 }
34}
35
36impl fmt::Display for ParentRef {
37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 match self {
39 Self::Named(name) => {
40 let mut first = true;
41 for segment in name.segments.iter() {
42 if !first || name.leading_colon.is_some() {
43 write!(f, "::")?;
44 }
45 first = false;
46 write!(f, "{}", segment.ident)?;
47 }
48 write!(f, " element")
49 }
50 }
51 }
52}
53
54/// Ephemeral struct to create a nice human-readable representation of
55/// [`syn::Member`].
56///
57/// It implements [`std::fmt::Display`] for that purpose and is otherwise of
58/// little use.
59#[repr(transparent)]
60struct FieldName<'x>(&'x Member);
61
62impl fmt::Display for FieldName<'_> {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 match self.0 {
65 Member::Named(v) => write!(f, "field '{}'", v),
66 Member::Unnamed(v) => write!(f, "unnamed field {}", v.index),
67 }
68 }
69}
70
71/// Create a string error message for a missing attribute.
72///
73/// `parent_name` should point at the compound which is being parsed and
74/// `field` should be the field to which the attribute belongs.
75pub(super) fn on_missing_attribute(parent_name: &ParentRef, field: &Member) -> String {
76 format!(
77 "Required attribute {} on {} missing.",
78 FieldName(field),
79 parent_name
80 )
81}
82
83/// Create a string error message for a missing child element.
84///
85/// `parent_name` should point at the compound which is being parsed and
86/// `field` should be the field to which the child belongs.
87pub(super) fn on_missing_child(parent_name: &ParentRef, field: &Member) -> String {
88 format!("Missing child {} in {}.", FieldName(&field), parent_name)
89}