substrate_data/schema/refs/
read_ref.rs1use 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
10pub 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
45fn 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
57fn 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
79fn 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
93pub fn read_id<K: Kinds>(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<K::Id> {
96 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#[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}