Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions crates/cow-venue/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
//! The typed CoW intent client.
//!
//! [`CowClient`] binds the strategy-facing [`IntentClient`] to the CoW
//! [`CowClient`] binds the keeper-facing [`IntentClient`] to the CoW
//! venue id and speaks the venue's own [`CowIntentBody`] over it, so
//! strategy code submits a typed CoW body without naming the venue on
//! keeper code submits a typed CoW body without naming the venue on
//! every call or handling wire bytes. The classification API
//! ([`classify`](crate::classification::classify)) travels in the same
//! slice so the client that submits an order and the table that
//! classifies its rejection version together.

use nexum_venue_sdk::client::{ClientError, IntentClient, IntentPool};
use nexum_venue_sdk::client::{ClientError, IntentClient, VenueClient};
use nexum_venue_sdk::{IntentStatus, SubmitOutcome};

use crate::body::CowIntentBody;

/// The venue id the CoW adapter registers under and the router resolves.
/// The venue id the CoW adapter registers under and the registry resolves.
/// Every [`CowClient`] call routes here.
pub const VENUE: &str = "cow";

Expand All @@ -25,11 +25,11 @@ pub struct CowClient<P> {
inner: IntentClient<P>,
}

impl<P: IntentPool> CowClient<P> {
/// Bind a pool handle to the CoW venue.
pub fn new(pool: P) -> Self {
impl<P: VenueClient> CowClient<P> {
/// Bind a client handle to the CoW venue.
pub fn new(venues: P) -> Self {
Self {
inner: IntentClient::new(pool, VENUE),
inner: IntentClient::new(venues, VENUE),
}
}

Expand Down Expand Up @@ -66,13 +66,13 @@ mod tests {

/// Records the venue every call routed to and the bytes submitted.
/// Cloneable over a shared log so the test can inspect it after the
/// pool moves into the client.
/// handle moves into the client.
#[derive(Clone, Default)]
struct SpyPool {
struct SpyClient {
submitted: SubmitLog,
}

impl IntentPool for SpyPool {
impl VenueClient for SpyClient {
fn submit(&self, venue: &str, body: Vec<u8>) -> Result<SubmitOutcome, VenueError> {
self.submitted
.borrow_mut()
Expand Down Expand Up @@ -112,15 +112,15 @@ mod tests {
fn submit_routes_to_the_cow_venue_with_encoded_body() {
use nexum_venue_sdk::IntentBody;

let pool = SpyPool::default();
let spy = SpyClient::default();
let body = sample_body();
let expected = body.to_bytes().expect("body encodes");

let client = CowClient::new(pool.clone());
let client = CowClient::new(spy.clone());
assert_eq!(client.venue(), VENUE);
client.submit(&body).expect("submit succeeds");

let calls = pool.submitted.borrow();
let calls = spy.submitted.borrow();
assert_eq!(calls.len(), 1);
assert_eq!(calls[0].0, VENUE);
assert_eq!(calls[0].1, expected);
Expand Down
24 changes: 12 additions & 12 deletions crates/nexum-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//!
//! [`venue`] is the adapter counterpart: it emits the same per-cdylib
//! wit-bindgen and `export!`, but for a per-component venue-adapter
//! world exporting the `nexum:intent/adapter` face and importing only
//! world exporting the `videre:venue/adapter` face and importing only
//! the manifest's declared scoped transport.
//!
//! [`derive@IntentBody`] implements the venue SDK's versioned body codec
Expand Down Expand Up @@ -271,7 +271,7 @@ pub fn module(attr: TokenStream, item: TokenStream) -> TokenStream {
.into()
}

/// The associated functions the `nexum:intent/adapter` face mandates. A
/// The associated functions the `videre:venue/adapter` face mandates. A
/// venue adapter must define all four; `init` is separate (a no-op when
/// absent, exactly as in a module).
const VENUE_EXPORTS: [&str; 4] = ["derive_header", "submit", "status", "cancel"];
Expand All @@ -280,7 +280,7 @@ const VENUE_EXPORTS: [&str; 4] = ["derive_header", "submit", "status", "cancel"]
///
/// Apply to an inherent `impl` block whose associated functions are the
/// adapter face: `derive_header`, `submit`, `status`, `cancel` (all
/// required, from `nexum:intent/adapter`), plus an optional `init`
/// required, from `videre:venue/adapter`), plus an optional `init`
/// (absent means a no-op). Each takes and returns the per-cdylib
/// wit-bindgen payloads for its signature. The macro reads the crate's
/// `module.toml`, synthesizes a per-component world exporting the
Expand All @@ -298,7 +298,7 @@ const VENUE_EXPORTS: [&str; 4] = ["derive_header", "submit", "status", "cancel"]
///
/// The same crate-root resolution invariants as [`macro@module`] apply:
/// the wit-bindgen output lands at the module crate root (so the emitted
/// glue resolves `Guest`, `Fault`, and the `nexum::*` type modules
/// glue resolves `Guest`, `Fault`, and the `nexum::*`/`videre::*` type modules
/// there), the consuming crate must declare `wit-bindgen` as a direct
/// dependency, and the crate root must not shadow std prelude names.
#[proc_macro_attribute]
Expand Down Expand Up @@ -423,37 +423,37 @@ pub fn venue(attr: TokenStream, item: TokenStream) -> TokenStream {
#init_impl
}

impl exports::nexum::intent::adapter::Guest for __NexumVenueAdapterExport {
impl exports::videre::venue::adapter::Guest for __NexumVenueAdapterExport {
fn derive_header(
body: ::std::vec::Vec<u8>,
) -> ::core::result::Result<
nexum::intent::types::IntentHeader,
nexum::intent::types::VenueError,
videre::types::types::IntentHeader,
videre::types::types::VenueError,
> {
<#self_ty>::derive_header(body)
}

fn submit(
body: ::std::vec::Vec<u8>,
) -> ::core::result::Result<
nexum::intent::types::SubmitOutcome,
nexum::intent::types::VenueError,
videre::types::types::SubmitOutcome,
videre::types::types::VenueError,
> {
<#self_ty>::submit(body)
}

fn status(
receipt: ::std::vec::Vec<u8>,
) -> ::core::result::Result<
nexum::intent::types::IntentStatus,
nexum::intent::types::VenueError,
videre::types::types::IntentStatus,
videre::types::types::VenueError,
> {
<#self_ty>::status(receipt)
}

fn cancel(
receipt: ::std::vec::Vec<u8>,
) -> ::core::result::Result<(), nexum::intent::types::VenueError> {
) -> ::core::result::Result<(), videre::types::types::VenueError> {
<#self_ty>::cancel(receipt)
}
}
Expand Down
57 changes: 39 additions & 18 deletions crates/nexum-macros/src/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct Capability {

/// Every capability the macro recognises, in emission order. Mirrors
/// the runtime's core registry plus the extension namespaces the
/// workspace ships (`nexum:intent/pool`, `shepherd:cow/cow-api`).
/// workspace ships (`videre:venue/client`, `shepherd:cow/cow-api`).
const KNOWN: &[Capability] = &[
Capability {
name: "chain",
Expand Down Expand Up @@ -71,9 +71,9 @@ const KNOWN: &[Capability] = &[
adapter: Some("logging"),
},
Capability {
name: "pool",
import: Some("nexum:intent/pool@0.1.0"),
packages: &["nexum-value-flow", "nexum-intent"],
name: "client",
import: Some("videre:venue/client@0.1.0"),
packages: &["videre-value-flow", "videre-types", "videre-venue"],
adapter: None,
},
Capability {
Expand Down Expand Up @@ -149,7 +149,7 @@ const VENUE_CAPABILITIES: &[&str] = &["chain", "messaging", "http"];

/// Build the per-component venue-adapter world from the declared
/// capability names. The world exports `init` and the
/// `nexum:intent/adapter` face and imports exactly the declared scoped
/// `videre:venue/adapter` face and imports exactly the declared scoped
/// transport, so a macro-built adapter's imports equal its declarations
/// by construction. A capability outside the venue-permitted set is a
/// compile error: an adapter that reaches for host key material or
Expand All @@ -167,11 +167,16 @@ pub fn synthesize_venue(declared: &[String]) -> Result<ModuleWorld, String> {
}

let mut imports = String::new();
// The export face (`nexum:intent/adapter`, its types, and the
// value-flow vocabulary they are expressed in) needs the intent
// The export face (`videre:venue/adapter`, its types, and the
// value-flow vocabulary they are expressed in) needs the videre
// packages on the resolve path beyond the leaf host package, in
// dependency order: a package precedes its dependants.
let mut packages = vec!["nexum-value-flow", "nexum-intent", "nexum-host"];
let mut packages = vec![
"videre-value-flow",
"videre-types",
"nexum-host",
"videre-venue",
];
for cap in KNOWN {
if !declared.iter().any(|d| d == cap.name) {
continue;
Expand All @@ -198,7 +203,7 @@ pub fn synthesize_venue(declared: &[String]) -> Result<ModuleWorld, String> {
wit.push_str(&imports);
wit.push_str(
"\n export init: func(config: config) -> result<_, fault>;\n \
export nexum:intent/adapter@0.1.0;\n}\n",
export videre:venue/adapter@0.1.0;\n}\n",
);

Ok(ModuleWorld {
Expand Down Expand Up @@ -278,8 +283,13 @@ mod tests {
const MODULE_PACKAGES: [&str; 1] = ["nexum-host"];

/// The package set every venue world resolves against: the exported
/// adapter face pulls the intent vocabulary, in dependency order.
const VENUE_PACKAGES: [&str; 3] = ["nexum-value-flow", "nexum-intent", "nexum-host"];
/// adapter face pulls the videre vocabulary, in dependency order.
const VENUE_PACKAGES: [&str; 4] = [
"videre-value-flow",
"videre-types",
"nexum-host",
"videre-venue",
];

#[test]
fn logging_only_world_imports_logging_alone() {
Expand All @@ -299,12 +309,17 @@ mod tests {
}

#[test]
fn pool_pulls_the_intent_packages() {
let world = synthesize(&["pool".to_string()]).unwrap();
assert!(world.wit.contains("import nexum:intent/pool@0.1.0;"));
fn client_pulls_the_videre_packages() {
let world = synthesize(&["client".to_string()]).unwrap();
assert!(world.wit.contains("import videre:venue/client@0.1.0;"));
assert_eq!(
world.packages,
vec!["nexum-host", "nexum-value-flow", "nexum-intent"]
vec![
"nexum-host",
"videre-value-flow",
"videre-types",
"videre-venue"
]
);
assert!(world.adapters.is_empty());
}
Expand Down Expand Up @@ -340,7 +355,7 @@ mod tests {
.wit
.contains("export init: func(config: config) -> result<_, fault>;")
);
assert!(world.wit.contains("export nexum:intent/adapter@0.1.0;"));
assert!(world.wit.contains("export videre:venue/adapter@0.1.0;"));
assert_eq!(world.packages, VENUE_PACKAGES);
assert!(world.adapters.is_empty());
}
Expand Down Expand Up @@ -368,12 +383,18 @@ mod tests {
fn venue_world_with_no_capabilities_imports_nothing() {
let world = synthesize_venue(&[]).unwrap();
assert!(!world.wit.contains("import"));
assert!(world.wit.contains("export nexum:intent/adapter@0.1.0;"));
assert!(world.wit.contains("export videre:venue/adapter@0.1.0;"));
}

#[test]
fn venue_world_refuses_non_transport_capabilities() {
for cap in ["local-store", "remote-store", "identity", "logging", "pool"] {
for cap in [
"local-store",
"remote-store",
"identity",
"logging",
"client",
] {
let err = synthesize_venue(&[cap.to_string()]).unwrap_err();
assert!(err.contains(cap), "message was: {err}");
assert!(err.contains("venue adapter"), "message was: {err}");
Expand Down
Loading
Loading