#contract #service

macro comfund_macros

A WCF-like attribute macro for defining service contracts

3 releases

0.1.2 Jul 17, 2025
0.1.1 Jul 13, 2025
0.1.0 Jul 13, 2025

#304 in #contract

Download history 9/week @ 2025-10-16

93 downloads per month
Used in comfund

MIT/Apache

60KB
1.5K SLoC

comfund: WCF-like Service Contracts in Rust

proc-macro crate for comfund contract attribute.

Contract is a rust trait representing possible requests to a service, parameters of each request and its return type.

For client side, either a stateful client or static implementation will be generated.

For server side, a service trait will be generated. Implementation of this trait can then be passed to a generated configure function to create configuration/router with corresponding handlers and middleware mounted on specified paths.

Additional attributes and options

Every fn of contract trait should be annotated with #[endpoint] attribute with two required arguments:

  • Method [get, post, put, delete]
  • Endpoint path ("/"-prefixed string literal)
use comfund::contract;

#[contract]
pub trait Service {
    #[endpoint(get, "/")]
    fn endpoint();
}

Endpoints can accept parameters. Each parameter should be annotated with #[param] attribute with one required arg - type of transport:

  • through endpoint URL path (path),
  • URL query param (query)
  • Request body (plain text or json)
use comfund::contract;

#[contract]
pub trait Service {
    #[endpoint(get, "/path/{a}")]
    fn path(#[param(path)] a: String);
    
    #[endpoint(get, "/query")]
    fn query(#[param(query)] a: String);
    
    #[endpoint(post, "/body")]
    fn body(#[param(body)]) a: String);
    
    #[endpoint(post, "/body/json")]
    fn json(#[param(json)]) a: Vec<String>);
}

Endpoints can also have return types. If you want to be able to return/read error info as well, you can set Result as return type.

use comfund::contract;

#[contract]
pub trait Service {
    #[endpoint(get, "/")]
    fn infallible() -> String;

    #[endpoint(get, "/may_fail")]
    fn may_fail() -> Result<String, Error>;
}

Endpoints can also specify content-type for returned value. Generated server and client code will handle the conversion accordingly.

use comfund::contract;
use comfund::serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
#[serde(crate = comfund::serde)]
struct Return {
    status: u16,
    string: String
}

#[contract]
pub trait Service {
    #[endpoint(get, "/may_fail", content_type = "application/json")]
    fn may_fail() -> Return;
}

../README.md

Dependencies

~4MB
~95K SLoC