Skip to main content

substrate_data/schema/refs/
read_ref.rs

1//! References read: of the kinds a field takes, their ids as the kinds' id type.
2
3use super::{Kinds, RefId};
4use crate::diagnostic::Problem;
5use crate::model::borrowed::ValueRef;
6use crate::model::value::{KindName, Ref, RefKey};
7use crate::schema::{Expected, Reader, Schema, Shape};
8use crate::typed;
9
10/// `v` as a reference to one of `kinds` (any kind if empty), written
11/// `@kind:id` — a bare id is an error that says how to write it. The id
12/// is read untyped ([`read_id`] reads it as a kind's id type).
13pub fn read_ref(v: &ValueRef<'_>, cx: &mut Reader<'_>, kinds: &[&str]) -> Option<Ref> {
14    let r = match v {
15        ValueRef::Ref { kind, id } => Ref::new(*kind, RefKey::spelled(id)),
16        ValueRef::Str(s) if s.starts_with(crate::names::TYPED) => spelled_ref(cx, s, kinds)?,
17        ValueRef::Str(s) => {
18            let kind = kinds.first().copied().unwrap_or("kind");
19            cx.problem_of(
20                Problem::NotAReference,
21                format!("`{s}`: name it by a reference — `@{kind}:{s}`"),
22            );
23            return None;
24        }
25        other => {
26            cx.expected(Expected::Reference, other);
27            return None;
28        }
29    };
30    if !kinds.is_empty() && !kinds.contains(&r.kind.as_str()) {
31        cx.problem_of(
32            Problem::WrongKind,
33            format!(
34                "`{r}` is a reference to {} — this takes {}",
35                r.kind,
36                wanted(kinds)
37            ),
38        );
39        return None;
40    }
41    cx.note_ref(r.clone());
42    Some(r)
43}
44
45/// The kinds a reference may be of, as they're written: "`@user:` or `@group:`".
46fn wanted(kinds: &[&str]) -> String {
47    if kinds.is_empty() {
48        return "`@kind:id`".into();
49    }
50    kinds
51        .iter()
52        .map(|k| format!("`@{k}:`"))
53        .collect::<Vec<_>>()
54        .join(" or ")
55}
56
57/// A reference spelled in a string (`"@kind:id"`), or a problem.
58fn spelled_ref(cx: &mut Reader<'_>, s: &str, kinds: &[&str]) -> Option<Ref> {
59    match typed::classify(s) {
60        Ok(typed::Spelled::Ref(r)) => Some(r),
61        Ok(other) => {
62            cx.problem_of(
63                Problem::Expected,
64                format!(
65                    "expected a reference ({}), found {}",
66                    wanted(kinds),
67                    other.kind()
68                ),
69            );
70            None
71        }
72        Err(e) => {
73            cx.problem_of(Problem::BadTyped, e.to_string());
74            None
75        }
76    }
77}
78
79/// `id` (of a reference of `kind`) as an `I`, or a problem.
80fn parse_id<I: RefId>(cx: &mut Reader<'_>, kind: KindName, id: &str) -> Option<I> {
81    I::parse(id).or_else(|| {
82        cx.problem_of(
83            Problem::BadId,
84            format!(
85                "`@{kind}:{id}`: `{id}` isn't a {kind} id (expected {})",
86                I::EXPECTED
87            ),
88        );
89        None
90    })
91}
92
93/// `v` as a reference to one of `K`'s kinds, its id read as `K::Id` — as
94/// written, or as the [`crate::Resolve`] rewrote it.
95pub fn read_id<K: Kinds>(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<K::Id> {
96    // The usual case, a reference of a kind it takes, nothing kept or resolved.
97    if let ValueRef::Ref { kind, id } = v
98        && K::NAMES.contains(&kind.as_str())
99        && !cx.keeps_found()
100        && !cx.resolves()
101    {
102        return parse_id(cx, *kind, id);
103    }
104    let r = read_ref(v, cx, K::NAMES)?;
105    let id = parse_id::<K::Id>(cx, r.kind, r.id.as_str())?;
106    if !cx.resolves() {
107        return Some(id);
108    }
109    let kind = r.kind;
110    let key = cx.take_ref(r)?;
111    parse_id(cx, kind, key.as_str())
112}
113
114/// The shape of a reference to `K`.
115#[must_use]
116pub fn ref_shape<K: Kinds>() -> Shape {
117    Shape::Ref {
118        kinds: K::NAMES,
119        id: <K::Id as Schema>::shape,
120    }
121}