1use std::fmt;
5use std::marker::PhantomData;
6
7use substrate_data::{Id, Kind, Reader, Ref, Schema, Shape, Value, ValueRef};
8
9use crate::kinds;
10
11fn split(spelled: &str) -> Option<(&str, &str)> {
13 spelled.split_once(yog_ids::sep::NAMESPACE)
14}
15
16pub struct TagId<K> {
18 pub id: String,
19 kind: PhantomData<K>,
20}
21
22impl<K> TagId<K> {
23 pub fn new(id: impl Into<String>) -> Self {
24 Self {
25 id: id.into(),
26 kind: PhantomData,
27 }
28 }
29}
30
31impl<K> Clone for TagId<K> {
32 fn clone(&self) -> Self {
33 Self::new(self.id.clone())
34 }
35}
36
37impl<K> PartialEq for TagId<K> {
38 fn eq(&self, o: &Self) -> bool {
39 self.id == o.id
40 }
41}
42
43impl<K: Kind> fmt::Debug for TagId<K> {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "@{}:{}:{}", kinds::Tag::NAME, K::NAME, self.id)
46 }
47}
48
49impl<K: Kind> Schema for TagId<K> {
50 fn shape() -> Shape {
51 substrate_data::schema::ref_shape::<kinds::Tag>()
52 }
53 fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
54 let tag = Id::<kinds::Tag>::read(v, cx)?;
55 match split(&tag.id) {
56 Some((kind, id)) if kind == K::NAME => Some(TagId::new(id)),
57 _ => {
58 cx.problem(format!(
59 "`@{}:{}`: this takes a tag of {} — `@{}:{}:<id>`",
60 kinds::Tag::NAME,
61 tag.id,
62 K::NAME,
63 kinds::Tag::NAME,
64 K::NAME
65 ));
66 None
67 }
68 }
69 }
70 fn write(&self) -> Value {
71 let spelled = format!("{}{}{}", K::NAME, yog_ids::sep::NAMESPACE, self.id);
72 Id::<kinds::Tag>::new(spelled).write()
73 }
74}
75
76fn is_tag(v: &ValueRef<'_>) -> bool {
78 let tag = kinds::Tag::NAME;
79 match v {
80 ValueRef::Ref { kind, .. } => kind.as_str() == tag,
81 ValueRef::Str(s) => s
82 .strip_prefix(yog_ids::sep::TYPED)
83 .and_then(|rest| rest.strip_prefix(tag))
84 .is_some_and(|rest| rest.starts_with(yog_ids::sep::NAMESPACE)),
85 _ => false,
86 }
87}
88
89#[derive(Debug, Clone, PartialEq)]
91pub enum OneOrTag<K: Kind<Id = String>> {
92 One(Id<K>),
93 Tag(TagId<K>),
94}
95
96pub type ItemIdOrTag = OneOrTag<kinds::Item>;
98
99impl<K: Kind<Id = String>> Schema for OneOrTag<K> {
100 fn shape() -> Shape {
101 Shape::Ref {
102 kinds: &[K::NAME, kinds::Tag::NAME],
103 id: String::shape,
104 }
105 }
106 fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
107 if let ValueRef::Str(s) = v
108 && let Some(tag) = s.strip_prefix(yog_ids::sep::TAG)
109 {
110 cx.problem(format!(
111 "`{s}`: write a tag as `@{}:{}:{tag}`",
112 kinds::Tag::NAME,
113 K::NAME
114 ));
115 return None;
116 }
117 if is_tag(v) {
118 TagId::read(v, cx).map(OneOrTag::Tag)
119 } else {
120 Id::read(v, cx).map(OneOrTag::One)
121 }
122 }
123 fn write(&self) -> Value {
124 match self {
125 OneOrTag::One(i) => i.write(),
126 OneOrTag::Tag(t) => t.write(),
127 }
128 }
129}
130
131#[derive(Debug, Clone, PartialEq)]
134pub enum AnyRefOrTag {
135 One(Ref),
136 Tag(String, String),
138}
139
140impl AnyRefOrTag {
141 pub fn kind(&self) -> &str {
143 match self {
144 AnyRefOrTag::One(r) => r.kind.as_str(),
145 AnyRefOrTag::Tag(kind, _) => kind,
146 }
147 }
148}
149
150impl fmt::Display for AnyRefOrTag {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 match self {
153 AnyRefOrTag::One(r) => r.fmt(f),
154 AnyRefOrTag::Tag(kind, id) => write!(f, "@{}:{kind}:{id}", kinds::Tag::NAME),
155 }
156 }
157}
158
159impl Schema for AnyRefOrTag {
160 fn shape() -> Shape {
161 Shape::Ref {
162 kinds: &[],
163 id: String::shape,
164 }
165 }
166 fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
167 let r = substrate_data::schema::read_ref(v, cx, &[])?;
168 if r.kind.as_str() != kinds::Tag::NAME {
169 return Some(AnyRefOrTag::One(r));
170 }
171 let Some((kind, id)) = split(r.id.as_str()) else {
172 cx.problem(format!("`{r}`: a tag is `@tag:<kind>:<id>`"));
173 return None;
174 };
175 Some(AnyRefOrTag::Tag(kind.to_owned(), id.to_owned()))
176 }
177 fn write(&self) -> Value {
178 match self {
179 AnyRefOrTag::One(r) => Value::Ref(r.clone()),
180 AnyRefOrTag::Tag(kind, id) => {
181 Id::<kinds::Tag>::new(format!("{kind}{}{id}", yog_ids::sep::NAMESPACE)).write()
182 }
183 }
184 }
185}