1use std::fmt;
4
5use substrate_data::{Id, Reader, Schema, Shape, Value, ValueRef};
6
7use crate::kinds;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct Res {
13 pub set: String,
14 pub key: String,
15}
16
17impl Res {
18 pub fn name(&self) -> String {
20 format!("{}{}{}", self.set, yog_ids::sep::DOT, self.key)
21 }
22
23 pub fn parse(spelled: &str) -> Option<Res> {
25 let (set, key) = spelled.split_once(yog_ids::sep::DOT)?;
26 let set_ok = set.starts_with(|c: char| c.is_ascii_uppercase())
27 && set.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_');
28 (set_ok && is_key(key)).then(|| Res {
29 set: set.to_owned(),
30 key: key.to_owned(),
31 })
32 }
33}
34
35pub fn is_key(key: &str) -> bool {
37 key.starts_with(|c: char| c.is_ascii_alphabetic() || c == '_')
38 && key.split(yog_ids::sep::DOT).all(|part| {
39 !part.is_empty() && part.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_')
40 })
41}
42
43impl fmt::Display for Res {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "{}{}", yog_ids::reference::RES, self.name())
46 }
47}
48
49impl Schema for Res {
50 fn shape() -> Shape {
51 substrate_data::schema::ref_shape::<kinds::Res>()
52 }
53 fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
54 let id = Id::<kinds::Res>::read(v, cx)?;
55 Res::parse(&id.id).or_else(|| {
56 cx.problem(format!(
57 "`{}`: an entry is `@res:Set.key` with an upper-case set (`@res:Strings.title`)",
58 id.id
59 ));
60 None
61 })
62 }
63 fn write(&self) -> Value {
64 Id::<kinds::Res>::new(self.name()).write()
65 }
66}