# Webwire command-line Interface [![Crates.io](https://img.shields.io/crates/v/webwire-cli)](https://crates.io/crates/webwire-cli) [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/webwire/webwire-cli/Rust)](https://github.com/webwire/webwire-cli/actions) [![Discord Chat](https://img.shields.io/discord/726922033039933472?label=Discord+Chat&color=%23677bc4&logo=discord&logoColor=white&style=for-the-badge)](https://discord.gg/jjD6aWG) ![webwire logo](https://webwire.dev/logo.svg) Webwire is a **contract-first API system** which features an interface description language a network protocol and code generator for both servers and clients. This repository contains the the **command-line interface** used to validate Webwire IDL files and generate code and documentation. To learn more about webwire in general please visit the documentation repository [webwire/webwire-docs](https://github.com/webwire/webwire-docs). # Example The following example assumes a Rust server and a TypeScript client. Webwire is by no means limited to those two but those languages show the potential of webwire best. Given the following IDL file: ```webwire struct HelloRequest { name: String, } struct HelloResponse { message: String, } service Hello { hello: HelloRequest -> HelloResponse } ``` The server and client files can be generated using the code generator: ```bash $ webwire gen rust < api/chat.ww > server/src/api.rs $ webwire gen ts < api/chat.ww > client/src/api.ts ``` A Rust server implementation for the given code would look like this: ```rust,ignore use std::net::SocketAddr; use std::sync::{Arc}; use async_trait::async_trait; use ::api::chat; use ::webwire::server::hyper::MakeHyperService; use ::webwire::server::session::{Auth, AuthError}; use ::webwire::{Response, Router, Server, ConsumerError}; struct ChatService { #[allow(dead_code)] session: Arc, server: Arc>, } #[async_trait] impl chat::Server for ChatService { async fn send(&self, message: &chat::Message) -> Response> { let client = chat::ClientConsumer(&*self.server); assert!(matches!(client.on_message(message).await, Err(ConsumerError::Broadcast))); Ok(Ok(())) } } #[derive(Default)] struct Session {} struct Sessions {} impl Sessions { pub fn new() -> Self { Self {} } } #[async_trait] impl webwire::SessionHandler for Sessions { async fn auth(&self, _auth: Option) -> Result { Ok(Session::default()) } async fn connect(&self, _session: &Session) {} async fn disconnect(&self, _session: &Session) {} } #[tokio::main] async fn main() { // Create session handler let session_handler = Sessions::new(); // Create service router let router = Arc::new(Router::::new()); // Create webwire server let server = Arc::new(webwire::server::Server::new( session_handler, router.clone(), )); // Register services router.service(chat::ServerProvider({ let server = server.clone(); move |session| ChatService { session, server: server.clone(), } })); // Start hyper service let addr = SocketAddr::from(([0, 0, 0, 0], 2323)); let make_service = MakeHyperService { server }; let server = hyper::Server::bind(&addr).serve(make_service); if let Err(e) = server.await { eprintln!("server error: {}", e); } } ``` A TypeScript client using the generated code would look like that: ```typescript import { Client } from 'webwire' import api from 'api' // this is the generated code let client = new Client('http://localhost:8000/', [ api.chat.ClientProvider({ async on_message(message) { console.log("Message received:", message) } }) ]) assert(await client.connect()) let chat = api.chat.ServerConsumer(client) let response = await chat.message({ text: "Hello world!" }) assert(response.Ok === null) ``` ## License Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or - MIT license ([LICENSE-MIT](LICENSE-MIT) or at your option.