error_message.rs

 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}