1use crate::{
7 card::card_kind_predicate,
8 claim::{Claim, ClaimPattern},
9 env::Cx,
10 error::{Error, Result},
11 event::{Event, EventKind, Tick},
12 id::Symbol,
13 ref_id::Ref,
14};
15
16pub fn stream_kind() -> Symbol {
18 Symbol::qualified("core", "stream")
19}
20
21pub fn stream_clock_predicate() -> Symbol {
23 Symbol::qualified("stream", "clock")
24}
25
26pub fn stream_codec_predicate() -> Symbol {
28 Symbol::qualified("stream", "codec")
29}
30
31pub fn stream_transport_predicate() -> Symbol {
33 Symbol::qualified("stream", "transport")
34}
35
36pub fn publish_stream_metadata_claims(
38 cx: &mut Cx,
39 stream: Ref,
40 metadata: impl IntoIterator<Item = (Symbol, Ref)>,
41) -> Result<()> {
42 insert_once(
43 cx,
44 stream.clone(),
45 card_kind_predicate(),
46 Ref::Symbol(stream_kind()),
47 )?;
48 for (predicate, object) in metadata {
49 insert_once(cx, stream.clone(), predicate, object)?;
50 }
51 Ok(())
52}
53
54pub fn stream_packet_event(run: Ref, seq: u64, ticks: Vec<Tick>, payload: Ref) -> Result<Event> {
56 match payload {
57 Ref::Content(_) | Ref::Handle(_) => {
58 Event::new(run, seq, ticks, EventKind::Chunk { payload })
59 }
60 Ref::Symbol(_) | Ref::Coord(_) => Err(Error::Eval(
61 "stream packet payload must be a content or handle ref".to_owned(),
62 )),
63 }
64}
65
66pub fn remote_stream_frame_event(
68 run: Ref,
69 seq: u64,
70 ticks: Vec<Tick>,
71 frame: Ref,
72) -> Result<Event> {
73 stream_packet_event(run, seq, ticks, frame)
74}
75
76fn insert_once(cx: &mut Cx, subject: Ref, predicate: Symbol, object: Ref) -> Result<()> {
77 let exists = !cx
78 .query_facts(ClaimPattern::exact(
79 subject.clone(),
80 predicate.clone(),
81 object.clone(),
82 ))?
83 .is_empty();
84 if !exists {
85 cx.insert_fact(Claim::public(subject, predicate, object))?;
86 }
87 Ok(())
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::{ContentId, Coordinate, DefaultFactory, Expr, NoopEvalPolicy, card::card_for_ref};
94 use std::sync::Arc;
95
96 #[test]
97 fn stream_metadata_and_packet_events_use_claims_and_refs() {
98 let mut cx = Cx::new(
99 Arc::new(NoopEvalPolicy),
100 Arc::new(DefaultFactory),
101 crate::HandleSeed::new(7),
102 );
103 let stream = Ref::Handle(cx.fresh_handle());
104 publish_stream_metadata_claims(
105 &mut cx,
106 stream.clone(),
107 [(
108 stream_clock_predicate(),
109 Ref::Symbol(Symbol::qualified("clock", "sample")),
110 )],
111 )
112 .unwrap();
113
114 let claims = cx
115 .query_facts(ClaimPattern::exact(
116 stream,
117 stream_clock_predicate(),
118 Ref::Symbol(Symbol::qualified("clock", "sample")),
119 ))
120 .unwrap();
121 assert_eq!(claims.len(), 1);
122
123 let payload = Ref::Content(ContentId::from_bytes(
124 Symbol::qualified("core", "sha256"),
125 [7; 32],
126 ));
127 let event = stream_packet_event(
128 Ref::Symbol(Symbol::qualified("run", "one")),
129 0,
130 Vec::new(),
131 payload.clone(),
132 )
133 .unwrap();
134 assert!(matches!(event.kind, EventKind::Chunk { payload: actual } if actual == payload));
135 }
136
137 #[test]
138 fn stream_metadata_and_payload_rules_project_to_cards() {
139 let mut cx = Cx::new(
140 Arc::new(NoopEvalPolicy),
141 Arc::new(DefaultFactory),
142 crate::HandleSeed::new(7),
143 );
144 let stream = Ref::Handle(cx.fresh_handle());
145 publish_stream_metadata_claims(
146 &mut cx,
147 stream.clone(),
148 [
149 (
150 stream_clock_predicate(),
151 Ref::Symbol(Symbol::qualified("clock", "sample")),
152 ),
153 (
154 stream_codec_predicate(),
155 Ref::Symbol(Symbol::qualified("codec", "binary-base64")),
156 ),
157 (
158 stream_transport_predicate(),
159 Ref::Symbol(Symbol::qualified("stream", "memory")),
160 ),
161 ],
162 )
163 .unwrap();
164
165 let card = card_expr(&mut cx, stream.clone());
166 assert_eq!(
167 table_value(&card, "kind"),
168 Some(&Expr::Symbol(stream_kind()))
169 );
170
171 let run = Ref::Symbol(Symbol::qualified("run", "one"));
172 let ordinal = ContentId::from_bytes(Symbol::qualified("core", "sha256"), [3; 32]);
173 let coordinate = Ref::Coord(Coordinate {
174 space: Symbol::qualified("rank", "space"),
175 ordinal,
176 });
177 assert!(
178 stream_packet_event(run.clone(), 0, Vec::new(), Ref::Symbol(Symbol::new("bad")))
179 .is_err()
180 );
181 assert!(stream_packet_event(run, 0, Vec::new(), coordinate).is_err());
182 }
183
184 fn card_expr(cx: &mut Cx, subject: Ref) -> Expr {
185 card_for_ref(cx, subject)
186 .unwrap()
187 .object()
188 .as_expr(cx)
189 .unwrap()
190 }
191
192 fn table_value<'a>(expr: &'a Expr, key: &str) -> Option<&'a Expr> {
193 let Expr::Map(entries) = expr else {
194 return None;
195 };
196 entries.iter().find_map(|(entry_key, entry_value)| {
197 let Expr::Symbol(entry_key) = entry_key else {
198 return None;
199 };
200 (entry_key == &Symbol::new(key)).then_some(entry_value)
201 })
202 }
203}