Skip to main content

eventgraph/compositions/
market.rs

1//! Layer 2 (Exchange) composition operations.
2//!
3//! 14 operations + 7 named functions for trust-based marketplaces.
4
5use crate::errors::Result;
6use crate::event::{Event, Signer};
7use crate::grammar::Grammar;
8use crate::store::InMemoryStore;
9use crate::types::{ActorId, ConversationId, DomainScope, EventId, Weight};
10
11/// MarketGrammar provides Layer 2 (Exchange) composition operations.
12pub struct MarketGrammar<'a>(Grammar<'a>);
13
14impl<'a> MarketGrammar<'a> {
15    pub fn new(store: &'a mut InMemoryStore) -> Self {
16        Self(Grammar::new(store))
17    }
18
19    // --- Operations (14) ---
20
21    /// List publishes an offering to the market.
22    pub fn list(&mut self, source: ActorId, offering: &str, causes: Vec<EventId>, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
23        self.0.emit(source, &format!("list: {offering}"), conv_id, causes, signer)
24    }
25
26    /// Bid makes a counter-offer on a listing.
27    pub fn bid(&mut self, source: ActorId, offer: &str, listing: EventId, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
28        self.0.respond(source, &format!("bid: {offer}"), listing, conv_id, signer)
29    }
30
31    /// Inquire asks for clarification about an offering.
32    pub fn inquire(&mut self, source: ActorId, question: &str, listing: EventId, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
33        self.0.respond(source, &format!("inquire: {question}"), listing, conv_id, signer)
34    }
35
36    /// Negotiate opens a channel for refining terms.
37    pub fn negotiate(&mut self, source: ActorId, counterparty: ActorId, scope: Option<&DomainScope>, cause: EventId, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
38        self.0.channel(source, counterparty, scope, cause, conv_id, signer)
39    }
40
41    /// Accept accepts terms, creating mutual obligation.
42    pub fn accept(&mut self, buyer: ActorId, seller: ActorId, terms: &str, scope: &DomainScope, cause: EventId, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
43        self.0.consent(buyer, seller, &format!("accept: {terms}"), scope, cause, conv_id, signer)
44    }
45
46    /// Decline rejects an offer.
47    pub fn decline(&mut self, source: ActorId, reason: &str, causes: Vec<EventId>, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
48        self.0.emit(source, &format!("decline: {reason}"), conv_id, causes, signer)
49    }
50
51    /// Invoice formalizes a payment obligation.
52    pub fn invoice(&mut self, source: ActorId, description: &str, causes: Vec<EventId>, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
53        self.0.emit(source, &format!("invoice: {description}"), conv_id, causes, signer)
54    }
55
56    /// Pay satisfies a financial obligation.
57    pub fn pay(&mut self, source: ActorId, description: &str, causes: Vec<EventId>, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
58        self.0.emit(source, &format!("pay: {description}"), conv_id, causes, signer)
59    }
60
61    /// Deliver satisfies a service/goods obligation.
62    pub fn deliver(&mut self, source: ActorId, description: &str, causes: Vec<EventId>, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
63        self.0.emit(source, &format!("deliver: {description}"), conv_id, causes, signer)
64    }
65
66    /// Confirm acknowledges receipt and satisfaction.
67    pub fn confirm(&mut self, source: ActorId, confirmation: &str, causes: Vec<EventId>, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
68        self.0.emit(source, &format!("confirm: {confirmation}"), conv_id, causes, signer)
69    }
70
71    /// Rate provides structured feedback on an exchange.
72    pub fn rate(&mut self, source: ActorId, target: EventId, target_actor: ActorId, weight: Weight, scope: Option<&DomainScope>, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
73        self.0.endorse(source, target, target_actor, weight, scope, conv_id, signer)
74    }
75
76    /// Dispute flags a failed obligation.
77    pub fn dispute(&mut self, source: ActorId, complaint: &str, target: EventId, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
78        let (_, flag) = self.0.challenge(source, &format!("dispute: {complaint}"), target, conv_id, signer)?;
79        Ok(flag)
80    }
81
82    /// Escrow holds value pending conditions.
83    pub fn escrow(&mut self, source: ActorId, escrow_actor: ActorId, scope: &DomainScope, weight: Weight, cause: EventId, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
84        self.0.delegate(source, escrow_actor, scope, weight, cause, conv_id, signer)
85    }
86
87    /// Release releases escrowed value on condition.
88    pub fn release(&mut self, party_a: ActorId, party_b: ActorId, terms: &str, scope: &DomainScope, cause: EventId, conv_id: ConversationId, signer: &dyn Signer) -> Result<Event> {
89        self.0.consent(party_a, party_b, &format!("release: {terms}"), scope, cause, conv_id, signer)
90    }
91
92    // --- Named Functions (7) ---
93
94    /// Auction runs competitive bidding: List + Bid (multiple) + Accept (highest).
95    pub fn auction(
96        &mut self, seller: ActorId, offering: &str,
97        bidders: &[ActorId], bids: &[&str], winner_idx: usize,
98        scope: &DomainScope, causes: Vec<EventId>,
99        conv_id: ConversationId, signer: &dyn Signer,
100    ) -> Result<AuctionResult> {
101        if bidders.len() != bids.len() {
102            return Err(crate::errors::EventGraphError::GrammarViolation { detail: "auction: bidders and bids must have equal length".to_string() });
103        }
104        if winner_idx >= bidders.len() {
105            return Err(crate::errors::EventGraphError::GrammarViolation { detail: "auction: winner_idx out of range".to_string() });
106        }
107        let listing = self.list(seller.clone(), offering, causes, conv_id.clone(), signer)?;
108        let mut bid_events = Vec::new();
109        for (i, bidder) in bidders.iter().enumerate() {
110            let b = self.bid(bidder.clone(), bids[i], listing.id.clone(), conv_id.clone(), signer)?;
111            bid_events.push(b);
112        }
113        let acceptance = self.accept(bidders[winner_idx].clone(), seller, &format!("auction won: {}", bids[winner_idx]), scope, bid_events[winner_idx].id.clone(), conv_id, signer)?;
114        Ok(AuctionResult { listing, bids: bid_events, acceptance })
115    }
116
117    /// Milestone is staged delivery and payment: Accept + Deliver (partial) + Pay (partial).
118    pub fn milestone(
119        &mut self, buyer: ActorId, seller: ActorId,
120        terms: &str, milestones: &[&str], payments: &[&str],
121        scope: &DomainScope, cause: EventId,
122        conv_id: ConversationId, signer: &dyn Signer,
123    ) -> Result<MilestoneResult> {
124        if milestones.len() != payments.len() {
125            return Err(crate::errors::EventGraphError::GrammarViolation { detail: "milestone: milestones and payments must have equal length".to_string() });
126        }
127        let acceptance = self.accept(buyer.clone(), seller.clone(), terms, scope, cause, conv_id.clone(), signer)?;
128        let mut deliveries = Vec::new();
129        let mut payment_events = Vec::new();
130        let mut prev = acceptance.id.clone();
131        for i in 0..milestones.len() {
132            let d = self.deliver(seller.clone(), milestones[i], vec![prev], conv_id.clone(), signer)?;
133            let p = self.pay(buyer.clone(), payments[i], vec![d.id.clone()], conv_id.clone(), signer)?;
134            prev = p.id.clone();
135            deliveries.push(d);
136            payment_events.push(p);
137        }
138        Ok(MilestoneResult { acceptance, deliveries, payments: payment_events })
139    }
140
141    /// Barter exchanges goods for goods: List + Bid (goods) + Accept.
142    pub fn barter(
143        &mut self, party_a: ActorId, party_b: ActorId,
144        offer_a: &str, offer_b: &str, scope: &DomainScope,
145        causes: Vec<EventId>, conv_id: ConversationId, signer: &dyn Signer,
146    ) -> Result<BarterResult> {
147        let listing = self.list(party_a.clone(), offer_a, causes, conv_id.clone(), signer)?;
148        let counter = self.bid(party_b.clone(), offer_b, listing.id.clone(), conv_id.clone(), signer)?;
149        let acceptance = self.accept(party_a, party_b, &format!("barter: {offer_a} for {offer_b}"), scope, counter.id.clone(), conv_id, signer)?;
150        Ok(BarterResult { listing, counter_offer: counter, acceptance })
151    }
152
153    /// Subscription creates recurring delivery and payment.
154    pub fn subscription(
155        &mut self, subscriber: ActorId, provider: ActorId,
156        terms: &str, periods: &[&str], deliveries: &[&str],
157        scope: &DomainScope, cause: EventId,
158        conv_id: ConversationId, signer: &dyn Signer,
159    ) -> Result<SubscriptionResult> {
160        if periods.len() != deliveries.len() {
161            return Err(crate::errors::EventGraphError::GrammarViolation { detail: "subscription: periods and deliveries must have equal length".to_string() });
162        }
163        let acceptance = self.accept(subscriber.clone(), provider.clone(), terms, scope, cause, conv_id.clone(), signer)?;
164        let mut payment_events = Vec::new();
165        let mut delivery_events = Vec::new();
166        let mut prev = acceptance.id.clone();
167        for i in 0..periods.len() {
168            let p = self.pay(subscriber.clone(), periods[i], vec![prev], conv_id.clone(), signer)?;
169            let d = self.deliver(provider.clone(), deliveries[i], vec![p.id.clone()], conv_id.clone(), signer)?;
170            prev = d.id.clone();
171            payment_events.push(p);
172            delivery_events.push(d);
173        }
174        Ok(SubscriptionResult { acceptance, payments: payment_events, deliveries: delivery_events })
175    }
176
177    /// Refund processes a return: Dispute + resolution + Pay (reversed).
178    pub fn refund(
179        &mut self, buyer: ActorId, seller: ActorId,
180        complaint: &str, resolution: &str, refund_amount: &str,
181        target: EventId, conv_id: ConversationId, signer: &dyn Signer,
182    ) -> Result<RefundResult> {
183        let dispute_ev = self.dispute(buyer.clone(), complaint, target, conv_id.clone(), signer)?;
184        let resolution_ev = self.0.emit(seller.clone(), &format!("resolution: {resolution}"), conv_id.clone(), vec![dispute_ev.id.clone()], signer)?;
185        let reversal = self.pay(seller, &format!("refund: {refund_amount}"), vec![resolution_ev.id.clone()], conv_id, signer)?;
186        Ok(RefundResult { dispute: dispute_ev, resolution: resolution_ev, reversal })
187    }
188
189    /// ReputationTransfer collects ratings from multiple parties.
190    pub fn reputation_transfer(
191        &mut self, raters: &[ActorId], targets: &[EventId], target_actor: ActorId,
192        weights: &[Weight], scope: Option<&DomainScope>,
193        conv_id: ConversationId, signer: &dyn Signer,
194    ) -> Result<ReputationTransferResult> {
195        if raters.len() != targets.len() || raters.len() != weights.len() {
196            return Err(crate::errors::EventGraphError::GrammarViolation { detail: "reputation-transfer: raters, targets, and weights must have equal length".to_string() });
197        }
198        let mut ratings = Vec::new();
199        for (i, rater) in raters.iter().enumerate() {
200            let r = self.rate(rater.clone(), targets[i].clone(), target_actor.clone(), weights[i], scope, conv_id.clone(), signer)?;
201            ratings.push(r);
202        }
203        Ok(ReputationTransferResult { ratings })
204    }
205
206    /// Arbitration resolves a dispute with escrow: Dispute + Escrow + Release.
207    pub fn arbitration(
208        &mut self, plaintiff: ActorId, defendant: ActorId, arbiter: ActorId,
209        complaint: &str, scope: &DomainScope, weight: Weight,
210        target: EventId, conv_id: ConversationId, signer: &dyn Signer,
211    ) -> Result<ArbitrationResult> {
212        let dispute_ev = self.dispute(plaintiff.clone(), complaint, target, conv_id.clone(), signer)?;
213        let escrow_ev = self.escrow(defendant, arbiter.clone(), scope, weight, dispute_ev.id.clone(), conv_id.clone(), signer)?;
214        let release_ev = self.release(arbiter, plaintiff, "arbitration resolved", scope, escrow_ev.id.clone(), conv_id, signer)?;
215        Ok(ArbitrationResult { dispute: dispute_ev, escrow: escrow_ev, release: release_ev })
216    }
217}
218
219pub struct AuctionResult { pub listing: Event, pub bids: Vec<Event>, pub acceptance: Event }
220pub struct MilestoneResult { pub acceptance: Event, pub deliveries: Vec<Event>, pub payments: Vec<Event> }
221pub struct BarterResult { pub listing: Event, pub counter_offer: Event, pub acceptance: Event }
222pub struct SubscriptionResult { pub acceptance: Event, pub payments: Vec<Event>, pub deliveries: Vec<Event> }
223pub struct RefundResult { pub dispute: Event, pub resolution: Event, pub reversal: Event }
224pub struct ReputationTransferResult { pub ratings: Vec<Event> }
225pub struct ArbitrationResult { pub dispute: Event, pub escrow: Event, pub release: Event }