m4a_matrix_core/
builder.rs1use std::collections::HashMap;
2
3use ruma_common::{CanonicalJsonObject, OwnedEventId, OwnedRoomId, UserId};
4use ruma_events::{StateEventType, TimelineEventType};
5use ruma_signatures::{hash_and_sign_event, reference_hash};
6use ruma_state_res::{auth_types_for_event, check_state_dependent_auth_rules, check_state_independent_auth_rules, StateMap};
7use serde_json::{json, value::RawValue, Value};
8
9use crate::{Pdu, Signer, ROOM_VERSION};
10
11#[derive(Debug, thiserror::Error)]
12pub enum BuildError {
13 #[error("unsupported room version")]
14 Version,
15 #[error("bad event field: {0}")]
16 Field(String),
17 #[error("signing failed: {0}")]
18 Sign(String),
19 #[error("auth rules refuse the event: {0}")]
20 Auth(String),
21}
22
23#[derive(Clone, Debug)]
25pub struct SignedEvent {
26 pub event_id: OwnedEventId,
27 pub json: CanonicalJsonObject,
28}
29
30pub struct RoomBuilder<'a> {
34 room_id: OwnedRoomId,
35 signer: &'a Signer,
36 rules: ruma_common::room_version_rules::RoomVersionRules,
37 ts: u64,
38 depth: u64,
39 last: Option<OwnedEventId>,
40 state: StateMap<OwnedEventId>,
41 pdus: HashMap<OwnedEventId, Pdu>,
42 events: Vec<SignedEvent>,
43}
44
45impl<'a> RoomBuilder<'a> {
46 pub fn new(room_id: OwnedRoomId, signer: &'a Signer, start_ts_ms: u64) -> Result<Self, BuildError> {
47 let rules = ROOM_VERSION.rules().ok_or(BuildError::Version)?;
48 Ok(Self { room_id, signer, rules, ts: start_ts_ms, depth: 0, last: None, state: StateMap::new(), pdus: HashMap::new(), events: Vec::new() })
49 }
50
51 pub fn push(&mut self, sender: &str, kind: &str, state_key: Option<&str>, content: Value) -> Result<&SignedEvent, BuildError> {
53 let sender_id = UserId::parse(sender).map_err(|e| BuildError::Field(e.to_string()))?;
54 let raw = RawValue::from_string(content.to_string()).map_err(|e| BuildError::Field(e.to_string()))?;
55 let tl_kind = TimelineEventType::from(kind.to_owned());
56 let auth_events: Vec<OwnedEventId> = auth_types_for_event(&tl_kind, &sender_id, state_key, &raw, &self.rules.authorization)
57 .map_err(BuildError::Auth)?
58 .iter()
59 .filter_map(|k| self.state.get(k).cloned())
60 .collect();
61 self.depth += 1;
62 self.ts += 1;
63 let mut obj = json!({
64 "room_id": self.room_id,
65 "sender": sender,
66 "type": kind,
67 "content": content,
68 "origin_server_ts": self.ts,
69 "prev_events": self.last.iter().collect::<Vec<_>>(),
70 "auth_events": auth_events,
71 "depth": self.depth,
72 });
73 if let Some(sk) = state_key {
74 obj["state_key"] = json!(sk);
75 }
76 let mut obj: CanonicalJsonObject = serde_json::from_value(obj).map_err(|e| BuildError::Field(e.to_string()))?;
77 hash_and_sign_event(self.signer.server(), self.signer, &mut obj, &self.rules.redaction).map_err(|e| BuildError::Sign(e.to_string()))?;
78 let id = format!("${}", reference_hash(&obj, &self.rules).map_err(|e| BuildError::Sign(e.to_string()))?);
79 let event_id = OwnedEventId::try_from(id).map_err(|e| BuildError::Field(e.to_string()))?;
80 let pdu = Pdu::from_wire(event_id.clone(), &obj).map_err(BuildError::Field)?;
81
82 let by_id = |id: &ruma_common::EventId| self.pdus.get(id).cloned();
85 check_state_independent_auth_rules(&self.rules.authorization, &pdu, by_id).map_err(BuildError::Auth)?;
86 let fetch = |t: &StateEventType, k: &str| self.state.get(&(t.clone(), k.to_owned())).and_then(|id| self.pdus.get(id).cloned());
87 check_state_dependent_auth_rules(&self.rules.authorization, &pdu, fetch).map_err(BuildError::Auth)?;
88
89 if let Some(sk) = &pdu.state_key {
90 self.state.insert((StateEventType::from(pdu.kind.to_string()), sk.clone()), event_id.clone());
91 }
92 self.last = Some(event_id.clone());
93 self.pdus.insert(event_id.clone(), pdu);
94 self.events.push(SignedEvent { event_id, json: obj });
95 Ok(self.events.last().expect("just pushed"))
96 }
97
98 pub fn events(&self) -> &[SignedEvent] {
99 &self.events
100 }
101 pub fn state(&self) -> &StateMap<OwnedEventId> {
102 &self.state
103 }
104 pub fn pdus(&self) -> &HashMap<OwnedEventId, Pdu> {
105 &self.pdus
106 }
107 pub fn rules(&self) -> &ruma_common::room_version_rules::RoomVersionRules {
108 &self.rules
109 }
110}