working-with-schemas.md
This commit is contained in:
parent
174fd891ef
commit
e2974fef7d
|
@ -77,7 +77,7 @@
|
|||
|
||||
- [Overview](./guide/index.md)
|
||||
- [Preserves](./guide/preserves.md)
|
||||
- [Working with schemas]()
|
||||
- [Working with schemas](./guide/working-with-schemas.md)
|
||||
- [Language-neutral protocols]()
|
||||
- [Rust]()
|
||||
- [Python]()
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,4 @@
|
|||
[ -d .venv ] || python -m venv .venv
|
||||
. .venv/bin/activate
|
||||
pip install -U preserves
|
||||
PATH=$PATH:~/src/preserves/implementations/rust/target/debug
|
|
@ -0,0 +1 @@
|
|||
.venv
|
|
@ -0,0 +1,15 @@
|
|||
all: simpleChatProtocol.prb rs ts
|
||||
|
||||
simpleChatProtocol.prb: simpleChatProtocol.prs
|
||||
preserves-schemac .:$< > $@
|
||||
|
||||
clean:
|
||||
rm -f simpleChatProtocol.prb
|
||||
rm -rf rs
|
||||
rm -rf ts
|
||||
|
||||
rs: simpleChatProtocol.prb
|
||||
preserves-schema-rs --output-dir rs/chat --prefix chat simpleChatProtocol.prb
|
||||
|
||||
ts: simpleChatProtocol.prs
|
||||
preserves-schema-ts --output ./ts/gen .:simpleChatProtocol.prs
|
|
@ -0,0 +1,38 @@
|
|||
from preserves import stringify, schema, parse, Symbol
|
||||
S = schema.load_schema_file('./simpleChatProtocol.prb')
|
||||
P = S.simpleChatProtocol
|
||||
|
||||
def hook_for_doctests():
|
||||
'''
|
||||
>>> P.Present('me')
|
||||
Present {'username': 'me'}
|
||||
|
||||
>>> stringify(P.Present('me'))
|
||||
'<Present "me">'
|
||||
|
||||
>>> P.Present.decode(parse('<Present "me">'))
|
||||
Present {'username': 'me'}
|
||||
|
||||
>>> P.Present.try_decode(parse('<Present "me">'))
|
||||
Present {'username': 'me'}
|
||||
|
||||
>>> P.Present.try_decode(parse('<NotPresent "me">')) is None
|
||||
True
|
||||
|
||||
>>> stringify(P.UserStatus('me', P.Status.here()))
|
||||
'<Status "me" here>'
|
||||
|
||||
>>> stringify(P.UserStatus('me', P.Status.away('2022-03-08')))
|
||||
'<Status "me" <away "2022-03-08">>'
|
||||
|
||||
>>> x = P.UserStatus.decode(parse('<Status "me" <away "2022-03-08">>'))
|
||||
>>> x.status.VARIANT
|
||||
#away
|
||||
>>> x.status.VARIANT == Symbol('away')
|
||||
True
|
||||
'''
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
|
@ -0,0 +1,25 @@
|
|||
pub mod simple_chat_protocol;
|
||||
|
||||
use preserves_schema::support as _support;
|
||||
use _support::preserves;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub struct Language<N: preserves::value::NestedValue> {
|
||||
pub LIT_0_PRESENT: N /* Present */,
|
||||
pub LIT_1_SAYS: N /* Says */,
|
||||
pub LIT_4_STATUS: N /* Status */,
|
||||
pub LIT_3_AWAY: N /* away */,
|
||||
pub LIT_2_HERE: N /* here */
|
||||
}
|
||||
|
||||
impl<N: preserves::value::NestedValue> Default for Language<N> {
|
||||
fn default() -> Self {
|
||||
Language {
|
||||
LIT_0_PRESENT: /* Present */ _support::decode_lit(&vec![179, 7, 80, 114, 101, 115, 101, 110, 116]).unwrap(),
|
||||
LIT_1_SAYS: /* Says */ _support::decode_lit(&vec![179, 4, 83, 97, 121, 115]).unwrap(),
|
||||
LIT_4_STATUS: /* Status */ _support::decode_lit(&vec![179, 6, 83, 116, 97, 116, 117, 115]).unwrap(),
|
||||
LIT_3_AWAY: /* away */ _support::decode_lit(&vec![179, 4, 97, 119, 97, 121]).unwrap(),
|
||||
LIT_2_HERE: /* here */ _support::decode_lit(&vec![179, 4, 104, 101, 114, 101]).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use preserves_schema::support as _support;
|
||||
use _support::Deserialize;
|
||||
use _support::Parse;
|
||||
use _support::Unparse;
|
||||
use _support::preserves;
|
||||
use preserves::value::Domain;
|
||||
use preserves::value::NestedValue;
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct Present {
|
||||
pub username: std::string::String
|
||||
}
|
||||
|
||||
impl preserves::value::Domain for Present {}
|
||||
|
||||
impl<'de, _Value: preserves::value::NestedValue, R: _support::Reader<'de, _Value>> _support::Deserialize<'de, _Value, R> for Present {
|
||||
fn deserialize(r: &mut R) -> std::result::Result<Self, _support::ParseError> {
|
||||
r.open_record(None)?;
|
||||
let mut _tmp0 = _support::B::Type::default();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordLabel));
|
||||
r.boundary(&_tmp0)?;
|
||||
match r.next_token(true)? {
|
||||
preserves::value::Token::Atom(v) => match v.value() {
|
||||
preserves::value::Value::Symbol(w) if w == "Present" => {}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.Present"))?,
|
||||
}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.Present"))?,
|
||||
}
|
||||
let _tmp1 = ();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordField));
|
||||
r.boundary(&_tmp0)?;
|
||||
let _tmp2 = r.next_str()?.into_owned();
|
||||
r.ensure_complete(_tmp0, &_support::B::Item::RecordField)?;
|
||||
Ok(Present {username: _tmp2})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Parse<_L, _Value> for Present {
|
||||
fn parse(_ctxt: _L, value: &_Value) -> std::result::Result<Self, _support::ParseError> {
|
||||
let _tmp0 = value.value().to_record(None)?;
|
||||
if _tmp0.label() != &<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_0_PRESENT { return Err(_support::ParseError::conformance_error("simpleChatProtocol.Present")); }
|
||||
let _tmp1 = ();
|
||||
if _tmp0.fields().len() < 1 { return Err(_support::ParseError::conformance_error("simpleChatProtocol.Present")); }
|
||||
let _tmp2 = (&_tmp0.fields()[0]).value().to_string()?;
|
||||
Ok(Present {username: _tmp2.clone()})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Unparse<_L, _Value> for Present {
|
||||
fn unparse(&self, _ctxt: _L) -> _Value {
|
||||
let Present {username: _tmp0} = self;
|
||||
{
|
||||
let mut _tmp1 = preserves::value::Record(vec![(&<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_0_PRESENT).clone()]);
|
||||
_tmp1.fields_vec_mut().push(preserves::value::Value::from(_tmp0).wrap());
|
||||
_tmp1.finish().wrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct Says {
|
||||
pub who: std::string::String,
|
||||
pub what: std::string::String
|
||||
}
|
||||
|
||||
impl preserves::value::Domain for Says {}
|
||||
|
||||
impl<'de, _Value: preserves::value::NestedValue, R: _support::Reader<'de, _Value>> _support::Deserialize<'de, _Value, R> for Says {
|
||||
fn deserialize(r: &mut R) -> std::result::Result<Self, _support::ParseError> {
|
||||
r.open_record(None)?;
|
||||
let mut _tmp0 = _support::B::Type::default();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordLabel));
|
||||
r.boundary(&_tmp0)?;
|
||||
match r.next_token(true)? {
|
||||
preserves::value::Token::Atom(v) => match v.value() {
|
||||
preserves::value::Value::Symbol(w) if w == "Says" => {}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.Says"))?,
|
||||
}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.Says"))?,
|
||||
}
|
||||
let _tmp1 = ();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordField));
|
||||
r.boundary(&_tmp0)?;
|
||||
let _tmp2 = r.next_str()?.into_owned();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordField));
|
||||
r.boundary(&_tmp0)?;
|
||||
let _tmp3 = r.next_str()?.into_owned();
|
||||
r.ensure_complete(_tmp0, &_support::B::Item::RecordField)?;
|
||||
Ok(Says {who: _tmp2, what: _tmp3})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Parse<_L, _Value> for Says {
|
||||
fn parse(_ctxt: _L, value: &_Value) -> std::result::Result<Self, _support::ParseError> {
|
||||
let _tmp0 = value.value().to_record(None)?;
|
||||
if _tmp0.label() != &<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_1_SAYS { return Err(_support::ParseError::conformance_error("simpleChatProtocol.Says")); }
|
||||
let _tmp1 = ();
|
||||
if _tmp0.fields().len() < 2 { return Err(_support::ParseError::conformance_error("simpleChatProtocol.Says")); }
|
||||
let _tmp2 = (&_tmp0.fields()[0]).value().to_string()?;
|
||||
let _tmp3 = (&_tmp0.fields()[1]).value().to_string()?;
|
||||
Ok(Says {who: _tmp2.clone(), what: _tmp3.clone()})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Unparse<_L, _Value> for Says {
|
||||
fn unparse(&self, _ctxt: _L) -> _Value {
|
||||
let Says {who: _tmp0, what: _tmp1} = self;
|
||||
{
|
||||
let mut _tmp2 = preserves::value::Record(vec![(&<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_1_SAYS).clone()]);
|
||||
_tmp2.fields_vec_mut().push(preserves::value::Value::from(_tmp0).wrap());
|
||||
_tmp2.fields_vec_mut().push(preserves::value::Value::from(_tmp1).wrap());
|
||||
_tmp2.finish().wrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Hash)]
|
||||
pub enum Status {
|
||||
Here,
|
||||
Away {
|
||||
since: std::boxed::Box<TimeStamp>
|
||||
}
|
||||
}
|
||||
|
||||
impl preserves::value::Domain for Status {}
|
||||
|
||||
fn read_status_here<'de, _Value: preserves::value::NestedValue, R: _support::Reader<'de, _Value>>(r: &mut R) -> std::result::Result<Status, _support::ParseError> {
|
||||
match r.next_token(true)? {
|
||||
preserves::value::Token::Atom(v) => match v.value() {
|
||||
preserves::value::Value::Symbol(w) if w == "here" => {}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.Status::here"))?,
|
||||
}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.Status::here"))?,
|
||||
}
|
||||
let _tmp0 = ();
|
||||
Ok(Status::Here)
|
||||
}
|
||||
|
||||
fn read_status_away<'de, _Value: preserves::value::NestedValue, R: _support::Reader<'de, _Value>>(r: &mut R) -> std::result::Result<Status, _support::ParseError> {
|
||||
r.open_record(None)?;
|
||||
let mut _tmp0 = _support::B::Type::default();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordLabel));
|
||||
r.boundary(&_tmp0)?;
|
||||
match r.next_token(true)? {
|
||||
preserves::value::Token::Atom(v) => match v.value() {
|
||||
preserves::value::Value::Symbol(w) if w == "away" => {}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.Status::away"))?,
|
||||
}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.Status::away"))?,
|
||||
}
|
||||
let _tmp1 = ();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordField));
|
||||
r.boundary(&_tmp0)?;
|
||||
let _tmp2 = TimeStamp::deserialize(r)?;
|
||||
r.ensure_complete(_tmp0, &_support::B::Item::RecordField)?;
|
||||
Ok(Status::Away {since: std::boxed::Box::new(_tmp2)})
|
||||
}
|
||||
|
||||
impl<'de, _Value: preserves::value::NestedValue, R: _support::Reader<'de, _Value>> _support::Deserialize<'de, _Value, R> for Status {
|
||||
fn deserialize(r: &mut R) -> std::result::Result<Self, _support::ParseError> {
|
||||
let _mark = r.mark()?;
|
||||
match read_status_here(r) { Err(e) if e.is_conformance_error() => r.restore(&_mark)?, result => return result }
|
||||
match read_status_away(r) { Err(e) if e.is_conformance_error() => r.restore(&_mark)?, result => return result }
|
||||
Err(_support::ParseError::conformance_error("simpleChatProtocol.Status"))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_status_here<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
>(_ctxt: _L, value: &_Value) -> std::result::Result<Status, _support::ParseError> {
|
||||
if value != &<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_2_HERE { return Err(_support::ParseError::conformance_error("simpleChatProtocol.Status::here")); }
|
||||
let _tmp0 = ();
|
||||
Ok(Status::Here)
|
||||
}
|
||||
|
||||
fn parse_status_away<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
>(_ctxt: _L, value: &_Value) -> std::result::Result<Status, _support::ParseError> {
|
||||
let _tmp0 = value.value().to_record(None)?;
|
||||
if _tmp0.label() != &<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_3_AWAY { return Err(_support::ParseError::conformance_error("simpleChatProtocol.Status::away")); }
|
||||
let _tmp1 = ();
|
||||
if _tmp0.fields().len() < 1 { return Err(_support::ParseError::conformance_error("simpleChatProtocol.Status::away")); }
|
||||
let _tmp2 = TimeStamp::parse(_ctxt, (&_tmp0.fields()[0]))?;
|
||||
Ok(Status::Away {since: std::boxed::Box::new(_tmp2)})
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Parse<_L, _Value> for Status {
|
||||
fn parse(_ctxt: _L, value: &_Value) -> std::result::Result<Self, _support::ParseError> {
|
||||
if let Ok(r) = parse_status_here(_ctxt, value) { return Ok(r); }
|
||||
if let Ok(r) = parse_status_away(_ctxt, value) { return Ok(r); }
|
||||
Err(_support::ParseError::conformance_error("simpleChatProtocol.Status"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Unparse<_L, _Value> for Status {
|
||||
fn unparse(&self, _ctxt: _L) -> _Value {
|
||||
match self {
|
||||
Status::Here => (&<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_2_HERE).clone(),
|
||||
Status::Away {since: _tmp0} => {
|
||||
let mut _tmp1 = preserves::value::Record(vec![(&<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_3_AWAY).clone()]);
|
||||
_tmp1.fields_vec_mut().push(_tmp0.as_ref().unparse(_ctxt));
|
||||
_tmp1.finish().wrap()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct TimeStamp(pub std::string::String);
|
||||
|
||||
impl preserves::value::Domain for TimeStamp {}
|
||||
|
||||
impl<'de, _Value: preserves::value::NestedValue, R: _support::Reader<'de, _Value>> _support::Deserialize<'de, _Value, R> for TimeStamp {
|
||||
fn deserialize(r: &mut R) -> std::result::Result<Self, _support::ParseError> {
|
||||
let _tmp0 = r.next_str()?.into_owned();
|
||||
Ok(TimeStamp(_tmp0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Parse<_L, _Value> for TimeStamp {
|
||||
fn parse(_ctxt: _L, value: &_Value) -> std::result::Result<Self, _support::ParseError> {
|
||||
let _tmp0 = value.value().to_string()?;
|
||||
Ok(TimeStamp(_tmp0.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Unparse<_L, _Value> for TimeStamp {
|
||||
fn unparse(&self, _ctxt: _L) -> _Value {
|
||||
let TimeStamp(_tmp0) = self;
|
||||
preserves::value::Value::from(_tmp0).wrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct UserStatus {
|
||||
pub username: std::string::String,
|
||||
pub status: Status
|
||||
}
|
||||
|
||||
impl preserves::value::Domain for UserStatus {}
|
||||
|
||||
impl<'de, _Value: preserves::value::NestedValue, R: _support::Reader<'de, _Value>> _support::Deserialize<'de, _Value, R> for UserStatus {
|
||||
fn deserialize(r: &mut R) -> std::result::Result<Self, _support::ParseError> {
|
||||
r.open_record(None)?;
|
||||
let mut _tmp0 = _support::B::Type::default();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordLabel));
|
||||
r.boundary(&_tmp0)?;
|
||||
match r.next_token(true)? {
|
||||
preserves::value::Token::Atom(v) => match v.value() {
|
||||
preserves::value::Value::Symbol(w) if w == "Status" => {}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.UserStatus"))?,
|
||||
}
|
||||
_ => return Err(_support::ParseError::conformance_error("simpleChatProtocol.UserStatus"))?,
|
||||
}
|
||||
let _tmp1 = ();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordField));
|
||||
r.boundary(&_tmp0)?;
|
||||
let _tmp2 = r.next_str()?.into_owned();
|
||||
_tmp0.shift(Some(_support::B::Item::RecordField));
|
||||
r.boundary(&_tmp0)?;
|
||||
let _tmp3 = Status::deserialize(r)?;
|
||||
r.ensure_complete(_tmp0, &_support::B::Item::RecordField)?;
|
||||
Ok(UserStatus {username: _tmp2, status: _tmp3})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Parse<_L, _Value> for UserStatus {
|
||||
fn parse(_ctxt: _L, value: &_Value) -> std::result::Result<Self, _support::ParseError> {
|
||||
let _tmp0 = value.value().to_record(None)?;
|
||||
if _tmp0.label() != &<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_4_STATUS { return Err(_support::ParseError::conformance_error("simpleChatProtocol.UserStatus")); }
|
||||
let _tmp1 = ();
|
||||
if _tmp0.fields().len() < 2 { return Err(_support::ParseError::conformance_error("simpleChatProtocol.UserStatus")); }
|
||||
let _tmp2 = (&_tmp0.fields()[0]).value().to_string()?;
|
||||
let _tmp3 = Status::parse(_ctxt, (&_tmp0.fields()[1]))?;
|
||||
Ok(UserStatus {username: _tmp2.clone(), status: _tmp3})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
'a,
|
||||
_L: Copy + Into<&'a chat::Language<_Value>>,
|
||||
_Value: preserves::value::NestedValue + 'a
|
||||
> _support::Unparse<_L, _Value> for UserStatus {
|
||||
fn unparse(&self, _ctxt: _L) -> _Value {
|
||||
let UserStatus {username: _tmp0, status: _tmp1} = self;
|
||||
{
|
||||
let mut _tmp2 = preserves::value::Record(vec![(&<_L as Into<&'a chat::Language<_Value>>>::into(_ctxt).LIT_4_STATUS).clone()]);
|
||||
_tmp2.fields_vec_mut().push(preserves::value::Value::from(_tmp0).wrap());
|
||||
_tmp2.fields_vec_mut().push(_tmp1.unparse(_ctxt));
|
||||
_tmp2.finish().wrap()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<schema {
|
||||
definitions: {
|
||||
Present: <rec <lit Present> <tuple [
|
||||
<named username <atom String>>
|
||||
]>>
|
||||
Says: <rec <lit Says> <tuple [
|
||||
<named who <atom String>>
|
||||
<named what <atom String>>
|
||||
]>>
|
||||
}
|
||||
embeddedType: #f
|
||||
version: 1
|
||||
}>
|
|
@ -0,0 +1,2 @@
|
|||
´³bundle·µ³simpleChatProtocol„´³schema·³version‘³definitions·³Says´³rec´³lit³Says„´³tupleµ´³named³who´³atom³String„„´³named³what´³atom³String„„„„„³Status´³orµµ±here´³lit³here„„µ±away´³rec´³lit³away„´³tupleµ´³named³since´³refµ„³ TimeStamp„„„„„„„„³Present´³rec´³lit³Present„´³tupleµ´³named³username´³atom³String„„„„„³ TimeStamp´³atom³String„³
|
||||
UserStatus´³rec´³lit³Status„´³tupleµ´³named³username´³atom³String„„´³named³status´³refµ„³Status„„„„„„³embeddedType€„„„„
|
|
@ -0,0 +1,6 @@
|
|||
version 1 .
|
||||
Present = <Present @username string> .
|
||||
Says = <Says @who string @what string> .
|
||||
UserStatus = <Status @username string @status Status> .
|
||||
Status = =here / <away @since TimeStamp> .
|
||||
TimeStamp = string .
|
|
@ -0,0 +1,283 @@
|
|||
import * as _ from "@preserves/core";
|
||||
|
||||
export const $Present = _.Symbol.for("Present");
|
||||
export const $Says = _.Symbol.for("Says");
|
||||
export const $Status = _.Symbol.for("Status");
|
||||
export const $away = _.Symbol.for("away");
|
||||
export const $here = _.Symbol.for("here");
|
||||
|
||||
let __schema: _.Value | null = null;
|
||||
|
||||
export function _schema() {
|
||||
if (__schema === null) {
|
||||
__schema = _.decode<_.GenericEmbedded>(_.Bytes.fromHex("b4b306736368656d61b7b30776657273696f6e91b30b646566696e6974696f6e73b7b30453617973b4b303726563b4b3036c6974b3045361797384b4b3057475706c65b5b4b3056e616d6564b30377686fb4b30461746f6db306537472696e678484b4b3056e616d6564b30477686174b4b30461746f6db306537472696e678484848484b306537461747573b4b3026f72b5b5b10468657265b4b3036c6974b304686572658484b5b10461776179b4b303726563b4b3036c6974b3046177617984b4b3057475706c65b5b4b3056e616d6564b30573696e6365b4b303726566b584b30954696d655374616d708484848484848484b30750726573656e74b4b303726563b4b3036c6974b30750726573656e7484b4b3057475706c65b5b4b3056e616d6564b308757365726e616d65b4b30461746f6db306537472696e678484848484b30954696d655374616d70b4b30461746f6db306537472696e6784b30a55736572537461747573b4b303726563b4b3036c6974b30653746174757384b4b3057475706c65b5b4b3056e616d6564b308757365726e616d65b4b30461746f6db306537472696e678484b4b3056e616d6564b306737461747573b4b303726566b584b306537461747573848484848484b30c656d62656464656454797065808484"));
|
||||
};
|
||||
return __schema;
|
||||
}
|
||||
|
||||
export const _imports = {}
|
||||
|
||||
|
||||
export type Present = {"username": string};
|
||||
|
||||
export type Says = {"who": string, "what": string};
|
||||
|
||||
export type UserStatus = {"username": string, "status": Status};
|
||||
|
||||
export type Status = ({"_variant": "here"} | {"_variant": "away", "since": TimeStamp});
|
||||
|
||||
export type TimeStamp = string;
|
||||
|
||||
|
||||
export function Present(username: string): (
|
||||
Present &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {return {"username": username, __as_preserve__() {return fromPresent(this);}};}
|
||||
|
||||
Present.schema = function () {
|
||||
return {schema: _schema(), imports: _imports, definitionName: _.Symbol.for("Present")};
|
||||
}
|
||||
|
||||
export function Says({who, what}: {who: string, what: string}): (Says & {__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}) {return {"who": who, "what": what, __as_preserve__() {return fromSays(this);}};}
|
||||
|
||||
Says.schema = function () {
|
||||
return {schema: _schema(), imports: _imports, definitionName: _.Symbol.for("Says")};
|
||||
}
|
||||
|
||||
export function UserStatus({username, status}: {username: string, status: Status}): (
|
||||
UserStatus &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {
|
||||
return {
|
||||
"username": username,
|
||||
"status": status,
|
||||
__as_preserve__() {return fromUserStatus(this);}
|
||||
};
|
||||
}
|
||||
|
||||
UserStatus.schema = function () {
|
||||
return {
|
||||
schema: _schema(),
|
||||
imports: _imports,
|
||||
definitionName: _.Symbol.for("UserStatus")
|
||||
};
|
||||
}
|
||||
|
||||
export namespace Status {
|
||||
export function here(): (
|
||||
Status &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {return {"_variant": "here", __as_preserve__() {return fromStatus(this);}};};
|
||||
here.schema = function () {
|
||||
return {
|
||||
schema: _schema(),
|
||||
imports: _imports,
|
||||
definitionName: _.Symbol.for("Status"),
|
||||
variant: _.Symbol.for("here")
|
||||
};
|
||||
};
|
||||
export function away(since: TimeStamp): (
|
||||
Status &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {
|
||||
return {
|
||||
"_variant": "away",
|
||||
"since": since,
|
||||
__as_preserve__() {return fromStatus(this);}
|
||||
};
|
||||
};
|
||||
away.schema = function () {
|
||||
return {
|
||||
schema: _schema(),
|
||||
imports: _imports,
|
||||
definitionName: _.Symbol.for("Status"),
|
||||
variant: _.Symbol.for("away")
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function TimeStamp(value: string): TimeStamp {return value;}
|
||||
|
||||
TimeStamp.schema = function () {
|
||||
return {
|
||||
schema: _schema(),
|
||||
imports: _imports,
|
||||
definitionName: _.Symbol.for("TimeStamp")
|
||||
};
|
||||
}
|
||||
|
||||
export function asPresent<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): (
|
||||
Present &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {
|
||||
let result = toPresent(v);
|
||||
if (result === void 0) throw new TypeError(`Invalid Present: ${_.stringify(v)}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toPresent<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | (
|
||||
Present &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {
|
||||
let result: undefined | (
|
||||
Present &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
);
|
||||
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
|
||||
let _tmp0: ({}) | undefined;
|
||||
_tmp0 = _.is(v.label, $Present) ? {} : void 0;
|
||||
if (_tmp0 !== void 0) {
|
||||
let _tmp1: (string) | undefined;
|
||||
_tmp1 = typeof v[0] === 'string' ? v[0] : void 0;
|
||||
if (_tmp1 !== void 0) {result = {"username": _tmp1, __as_preserve__() {return fromPresent(this);}};};
|
||||
};
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
Present.__from_preserve__ = toPresent;
|
||||
|
||||
export function fromPresent<_embedded = _.GenericEmbedded>(_v: Present): _.Value<_embedded> {return _.Record($Present, [_v["username"]]);}
|
||||
|
||||
export function asSays<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): (Says & {__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}) {
|
||||
let result = toSays(v);
|
||||
if (result === void 0) throw new TypeError(`Invalid Says: ${_.stringify(v)}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toSays<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | (Says & {__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}) {
|
||||
let result: undefined | (Says & {__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>});
|
||||
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
|
||||
let _tmp0: ({}) | undefined;
|
||||
_tmp0 = _.is(v.label, $Says) ? {} : void 0;
|
||||
if (_tmp0 !== void 0) {
|
||||
let _tmp1: (string) | undefined;
|
||||
_tmp1 = typeof v[0] === 'string' ? v[0] : void 0;
|
||||
if (_tmp1 !== void 0) {
|
||||
let _tmp2: (string) | undefined;
|
||||
_tmp2 = typeof v[1] === 'string' ? v[1] : void 0;
|
||||
if (_tmp2 !== void 0) {
|
||||
result = {"who": _tmp1, "what": _tmp2, __as_preserve__() {return fromSays(this);}};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
Says.__from_preserve__ = toSays;
|
||||
|
||||
export function fromSays<_embedded = _.GenericEmbedded>(_v: Says): _.Value<_embedded> {return _.Record($Says, [_v["who"], _v["what"]]);}
|
||||
|
||||
export function asUserStatus<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): (
|
||||
UserStatus &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {
|
||||
let result = toUserStatus(v);
|
||||
if (result === void 0) throw new TypeError(`Invalid UserStatus: ${_.stringify(v)}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toUserStatus<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | (
|
||||
UserStatus &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {
|
||||
let result: undefined | (
|
||||
UserStatus &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
);
|
||||
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
|
||||
let _tmp0: ({}) | undefined;
|
||||
_tmp0 = _.is(v.label, $Status) ? {} : void 0;
|
||||
if (_tmp0 !== void 0) {
|
||||
let _tmp1: (string) | undefined;
|
||||
_tmp1 = typeof v[0] === 'string' ? v[0] : void 0;
|
||||
if (_tmp1 !== void 0) {
|
||||
let _tmp2: (Status) | undefined;
|
||||
_tmp2 = toStatus(v[1]);
|
||||
if (_tmp2 !== void 0) {
|
||||
result = {
|
||||
"username": _tmp1,
|
||||
"status": _tmp2,
|
||||
__as_preserve__() {return fromUserStatus(this);}
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
UserStatus.__from_preserve__ = toUserStatus;
|
||||
|
||||
export function fromUserStatus<_embedded = _.GenericEmbedded>(_v: UserStatus): _.Value<_embedded> {
|
||||
return _.Record($Status, [_v["username"], fromStatus<_embedded>(_v["status"])]);
|
||||
}
|
||||
|
||||
export function asStatus<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): (
|
||||
Status &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {
|
||||
let result = toStatus(v);
|
||||
if (result === void 0) throw new TypeError(`Invalid Status: ${_.stringify(v)}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toStatus<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | (
|
||||
Status &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
) {
|
||||
let _tmp0: ({}) | undefined;
|
||||
let result: undefined | (
|
||||
Status &
|
||||
{__as_preserve__<_embedded = _.GenericEmbedded>(): _.Value<_embedded>}
|
||||
);
|
||||
_tmp0 = _.is(v, $here) ? {} : void 0;
|
||||
if (_tmp0 !== void 0) {result = {"_variant": "here", __as_preserve__() {return fromStatus(this);}};};
|
||||
if (result === void 0) {
|
||||
if (_.Record.isRecord<_.Value<_embedded>, _.Tuple<_.Value<_embedded>>, _embedded>(v)) {
|
||||
let _tmp1: ({}) | undefined;
|
||||
_tmp1 = _.is(v.label, $away) ? {} : void 0;
|
||||
if (_tmp1 !== void 0) {
|
||||
let _tmp2: (TimeStamp) | undefined;
|
||||
_tmp2 = toTimeStamp(v[0]);
|
||||
if (_tmp2 !== void 0) {
|
||||
result = {
|
||||
"_variant": "away",
|
||||
"since": _tmp2,
|
||||
__as_preserve__() {return fromStatus(this);}
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
export namespace Status {export const __from_preserve__ = toStatus;}
|
||||
|
||||
export function fromStatus<_embedded = _.GenericEmbedded>(_v: Status): _.Value<_embedded> {
|
||||
switch (_v._variant) {
|
||||
case "here": {return $here;};
|
||||
case "away": {return _.Record($away, [fromTimeStamp<_embedded>(_v["since"])]);};
|
||||
};
|
||||
}
|
||||
|
||||
export function asTimeStamp<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): TimeStamp {
|
||||
let result = toTimeStamp(v);
|
||||
if (result === void 0) throw new TypeError(`Invalid TimeStamp: ${_.stringify(v)}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toTimeStamp<_embedded = _.GenericEmbedded>(v: _.Value<_embedded>): undefined | TimeStamp {
|
||||
let _tmp0: (string) | undefined;
|
||||
let result: undefined | TimeStamp;
|
||||
_tmp0 = typeof v === 'string' ? v : void 0;
|
||||
if (_tmp0 !== void 0) {result = _tmp0;};
|
||||
return result;
|
||||
}
|
||||
|
||||
TimeStamp.__from_preserve__ = toTimeStamp;
|
||||
|
||||
export function fromTimeStamp<_embedded = _.GenericEmbedded>(_v: TimeStamp): _.Value<_embedded> {return _v;}
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
# Working with schemas
|
||||
|
||||
- [Preserves schema specification](https://preserves.gitlab.io/preserves/preserves-schema.html)
|
||||
- [Index of Preserves schema tools](https://preserves.gitlab.io/preserves/doc/schema-tools.html)
|
||||
|
||||
[preserves-schemac]: https://preserves.gitlab.io/preserves/doc/preserves-schemac.html
|
||||
|
||||
## Schema source code: *.prs files
|
||||
|
||||
Preserves schemas are written in a syntax that (ab)uses Preserves text syntax as a kind of
|
||||
S-expression. Schema source code looks like this:
|
||||
|
||||
```preserves-schema
|
||||
version 1 .
|
||||
Present = <Present @username string> .
|
||||
Says = <Says @who string @what string> .
|
||||
UserStatus = <Status @username string @status Status> .
|
||||
Status = =here / <away @since TimeStamp> .
|
||||
TimeStamp = string .
|
||||
```
|
||||
|
||||
Conventionally, schema source code is stored in `*.prs` files. In this example, the source code
|
||||
above is placed in `simpleChatProtocol.prs`.
|
||||
|
||||
## Compiling source code to metaschema instances: *.prb files
|
||||
|
||||
Many of the code generator tools for Preserves schemas require not source code, but instances
|
||||
of the [Preserves
|
||||
metaschema](https://preserves.gitlab.io/preserves/preserves-schema.html#appendix-metaschema).
|
||||
To compile schema source code to metaschema instances, use
|
||||
[preserves-schemac][]:
|
||||
|
||||
```shell
|
||||
yarn global add @preserves/schema
|
||||
preserves-schemac .:simpleChatProtocol.prs > simpleChatProtocol.prb
|
||||
```
|
||||
|
||||
Binary-syntax metaschema instances are conventionally stored in `*.prb` files.
|
||||
|
||||
If you have a whole directory tree of `*.prs` files, you can supply just "`.`" without the
|
||||
"`:`"-prefixed fileglob part. See the [preserves-schemac documentation][preserves-schemac].
|
||||
|
||||
Converting the `simpleChatProtocol.prb` file to Preserves text syntax lets us read the
|
||||
metaschema instance corresponding to the source code:
|
||||
|
||||
```shell
|
||||
cat simpleChatProtocol.prb | preserves-tool convert
|
||||
```
|
||||
|
||||
The result:
|
||||
|
||||
```preserves
|
||||
<bundle {
|
||||
[
|
||||
simpleChatProtocol
|
||||
]: <schema {
|
||||
definitions: {
|
||||
Present: <rec <lit Present> <tuple [
|
||||
<named username <atom String>>
|
||||
]>>
|
||||
Says: <rec <lit Says> <tuple [
|
||||
<named who <atom String>>
|
||||
<named what <atom String>>
|
||||
]>>
|
||||
Status: <or [
|
||||
[
|
||||
"here"
|
||||
<lit here>
|
||||
]
|
||||
[
|
||||
"away"
|
||||
<rec <lit away> <tuple [
|
||||
<named since <ref [] TimeStamp>>
|
||||
]>>
|
||||
]
|
||||
]>
|
||||
TimeStamp: <atom String>
|
||||
UserStatus: <rec <lit Status> <tuple [
|
||||
<named username <atom String>>
|
||||
<named status <ref [] Status>>
|
||||
]>>
|
||||
}
|
||||
embeddedType: #f
|
||||
version: 1
|
||||
}>
|
||||
}>
|
||||
```
|
||||
|
||||
## Generating support code from metaschema instances
|
||||
|
||||
Support exists for working with schemas in many languages, including Python, Rust, TypeScript,
|
||||
Racket, and Squeak Smalltalk.
|
||||
|
||||
### Python
|
||||
|
||||
Python doesn't have a separate compilation step: it loads binary metaschema instances at
|
||||
runtime, generating classes on the fly.
|
||||
|
||||
After `pip install preserves`, load metaschemas with `preserves.schema.load_schema_file`:
|
||||
|
||||
```python
|
||||
from preserves import stringify, schema, parse
|
||||
S = schema.load_schema_file('./simpleChatProtocol.prb')
|
||||
P = S.simpleChatProtocol
|
||||
```
|
||||
|
||||
Then, members of `P` are the definitions from `simpleChatProtocol.prs`:
|
||||
|
||||
```python
|
||||
>>> P.Present('me')
|
||||
Present {'username': 'me'}
|
||||
|
||||
>>> stringify(P.Present('me'))
|
||||
'<Present "me">'
|
||||
|
||||
>>> P.Present.decode(parse('<Present "me">'))
|
||||
Present {'username': 'me'}
|
||||
|
||||
>>> P.Present.try_decode(parse('<Present "me">'))
|
||||
Present {'username': 'me'}
|
||||
|
||||
>>> P.Present.try_decode(parse('<NotPresent "me">')) is None
|
||||
True
|
||||
|
||||
>>> stringify(P.UserStatus('me', P.Status.here()))
|
||||
'<Status "me" here>'
|
||||
|
||||
>>> stringify(P.UserStatus('me', P.Status.away('2022-03-08')))
|
||||
'<Status "me" <away "2022-03-08">>'
|
||||
|
||||
>>> x = P.UserStatus.decode(parse('<Status "me" <away "2022-03-08">>'))
|
||||
>>> x.status.VARIANT
|
||||
#away
|
||||
>>> x.status.VARIANT == Symbol('away')
|
||||
True
|
||||
```
|
||||
|
||||
### Rust
|
||||
|
||||
Generate Rust definitions corresponding to a metaschema instance with [preserves-schema-rs][].
|
||||
The best way to use it is to integrate it into your `build.rs` (see [the
|
||||
docs][preserves-schema-rs]), but you can also use it as a standalone command-line tool.
|
||||
|
||||
[preserves-schema-rs]: https://preserves.gitlab.io/preserves/doc/preserves-schema-rs.html
|
||||
|
||||
The following command generates a directory `./rs/chat` containing rust sources for a module
|
||||
that expects to be called `chat` in Rust code:
|
||||
|
||||
```shell
|
||||
preserves-schema-rs --output-dir rs/chat --prefix chat simpleChatProtocol.prb
|
||||
```
|
||||
|
||||
Representative excerpts from one of the generated files, `./rs/chat/simple_chat_protocol.rs`:
|
||||
|
||||
```rust,noplayground
|
||||
pub struct Present {
|
||||
pub username: std::string::String
|
||||
}
|
||||
pub struct Says {
|
||||
pub who: std::string::String,
|
||||
pub what: std::string::String
|
||||
}
|
||||
pub struct UserStatus {
|
||||
pub username: std::string::String,
|
||||
pub status: Status
|
||||
}
|
||||
pub enum Status {
|
||||
Here,
|
||||
Away {
|
||||
since: std::boxed::Box<TimeStamp>
|
||||
}
|
||||
}
|
||||
pub struct TimeStamp(pub std::string::String);
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
Generate TypeScript definitions from schema **sources** (not metaschema instances) using
|
||||
[preserves-schema-ts][]. Unlike other code generators, this one understands schema source code
|
||||
directly.
|
||||
|
||||
[preserves-schema-ts]: https://preserves.gitlab.io/preserves/doc/preserves-schema-ts.html
|
||||
|
||||
The following command generates a directory `./ts/gen` containing TypeScript sources:
|
||||
|
||||
```shell
|
||||
preserves-schema-ts --output ./ts/gen .:simpleChatProtocol.prs
|
||||
```
|
||||
|
||||
Representative excerpts from one of the generated files, `./ts/gen/simpleChatProtocol.ts`:
|
||||
|
||||
```typescript
|
||||
export type Present = {"username": string};
|
||||
export type Says = {"who": string, "what": string};
|
||||
export type UserStatus = {"username": string, "status": Status};
|
||||
export type Status = ({"_variant": "here"} | {"_variant": "away", "since": TimeStamp});
|
||||
export type TimeStamp = string;
|
||||
```
|
||||
|
||||
### Squeak Smalltalk
|
||||
|
||||
After loading the `Preserves` package from the [Preserves project SqueakSource
|
||||
page](https://squeaksource.com/Preserves.html), perhaps via
|
||||
|
||||
```smalltalk
|
||||
Installer squeaksource project: 'Preserves'; install: 'Preserves'.
|
||||
```
|
||||
|
||||
you can load and compile the bundle using something like
|
||||
|
||||
```smalltalk
|
||||
(PreservesSchemaEnvironment fromBundleFile: 'simpleChatProtocol.prb')
|
||||
category: 'Example-Preserves-Schema-SimpleChat';
|
||||
prefix: 'SimpleChat';
|
||||
cleanCategoryOnCompile: true;
|
||||
compileBundle.
|
||||
```
|
||||
|
||||
which results in classes whose names are prefixed with `SimpleChat` being created in package
|
||||
`Example-Preserves-Schema-SimpleChat`. Here's a screenshot of a browser showing the generated
|
||||
classes:
|
||||
|
||||
![Screenshot of Squeak Browser on class SimpleChatSimpleChatProtocol](<../figures/System Browser: SimpleChatSimpleChatProtocol.png>)
|
||||
|
||||
Exploring the result of evaluating the following expression, which generates a Smalltalk object
|
||||
in the specified schema, yields the following screenshot:
|
||||
|
||||
```smalltalk
|
||||
SimpleChatSimpleChatProtocolStatus away
|
||||
since: (SimpleChatSimpleChatProtocolTimeStamp new value: '2022-03-08')
|
||||
```
|
||||
|
||||
![Exploration of a SimpleChatSimpleChatProtocolStatus object](<../figures/a SimpleChatSimpleChatProtocolStatus_away.png>)
|
||||
|
||||
Exploring the result of evaluating the following expression, which generates a Smalltalk object
|
||||
representing the Preserves value corresponding to the value produced in the previous
|
||||
expression, yields the following screenshot:
|
||||
|
||||
```smalltalk
|
||||
(SimpleChatSimpleChatProtocolStatus away
|
||||
since: (SimpleChatSimpleChatProtocolTimeStamp new value: '2022-03-08'))
|
||||
asPreserves
|
||||
```
|
||||
|
||||
![Exploration of a SimpleChatSimpleChatProtocolStatus preserves value object](<../figures/away-2022-03-08.png>)
|
||||
|
||||
Finally, the following expression parses a valid `Status` string input:
|
||||
|
||||
```smalltalk
|
||||
SimpleChatSimpleChatProtocolStatus
|
||||
from: '<away "2022-03-08">' parsePreserves
|
||||
orTry: []
|
||||
```
|
||||
|
||||
If it had been invalid, the answer would have been `nil` (because `[] value` is `nil`).
|
Loading…
Reference in New Issue