rust-sdk/examples/wasi/src/lib.rs
4t145 1f7f4d3055
refactor: refactor tool macros and router implementation (#261)
* refactor: refactor tool macros and router implementation

- Updated the `#[tool(tool_box)]` macro to `#[tool_router]` across various modules for consistency.
- Enhanced the `Calculator`, `Counter`, and `GenericService` structs to utilize `ToolRouter` for handling tool calls.
- Introduced `Parameters` struct for better parameter handling in tool functions.
- Added new methods for listing tools and calling tools in server handlers.
- Improved test cases to reflect changes in tool routing and parameter handling.
- Updated documentation and examples to align with the new router structure.

* fix: fix fmt and build error

* fix: fix test failure

* docs: documents for macros, fix ci

* fix: fix ci

* fix: fix wrongly replaced documents

* fix: remove useless file

* fix: change the parameter format for tool_router

* fix: update extract_doc_line to handle existing documentation and clean up unused code in server handler

* doc: update document for macro and examples

* doc: update readme and add contribute guide

* fix: fix type
2025-06-24 04:01:22 +08:00

124 lines
3.5 KiB
Rust

pub mod calculator;
use std::task::{Poll, Waker};
use rmcp::ServiceExt;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing_subscriber::EnvFilter;
use wasi::{
cli::{
stdin::{InputStream, get_stdin},
stdout::{OutputStream, get_stdout},
},
io::streams::Pollable,
};
pub fn wasi_io() -> (AsyncInputStream, AsyncOutputStream) {
let input = AsyncInputStream { inner: get_stdin() };
let output = AsyncOutputStream {
inner: get_stdout(),
};
(input, output)
}
pub struct AsyncInputStream {
inner: InputStream,
}
impl AsyncRead for AsyncInputStream {
fn poll_read(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> std::task::Poll<std::io::Result<()>> {
let bytes = self
.inner
.read(buf.remaining() as u64)
.map_err(std::io::Error::other)?;
if bytes.is_empty() {
let pollable = self.inner.subscribe();
let waker = cx.waker().clone();
runtime_poll(waker, pollable);
return Poll::Pending;
}
buf.put_slice(&bytes);
std::task::Poll::Ready(Ok(()))
}
}
pub struct AsyncOutputStream {
inner: OutputStream,
}
fn runtime_poll(waker: Waker, pollable: Pollable) {
tokio::task::spawn(async move {
loop {
if pollable.ready() {
waker.wake();
break;
} else {
tokio::task::yield_now().await;
}
}
});
}
impl AsyncWrite for AsyncOutputStream {
fn poll_write(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, std::io::Error>> {
let writable_len = self.inner.check_write().map_err(std::io::Error::other)?;
if writable_len == 0 {
let pollable = self.inner.subscribe();
let waker = cx.waker().clone();
runtime_poll(waker, pollable);
return Poll::Pending;
}
let bytes_to_write = buf.len().min(writable_len as usize);
self.inner
.write(&buf[0..bytes_to_write])
.map_err(std::io::Error::other)?;
Poll::Ready(Ok(bytes_to_write))
}
fn poll_flush(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
self.inner.flush().map_err(std::io::Error::other)?;
Poll::Ready(Ok(()))
}
fn poll_shutdown(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Result<(), std::io::Error>> {
self.poll_flush(cx)
}
}
struct TokioCliRunner;
impl wasi::exports::cli::run::Guest for TokioCliRunner {
fn run() -> Result<(), ()> {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
rt.block_on(async move {
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::from_default_env().add_directive(tracing::Level::DEBUG.into()),
)
.with_writer(std::io::stderr)
.with_ansi(false)
.init();
let server = calculator::Calculator::new()
.serve(wasi_io())
.await
.unwrap();
server.waiting().await.unwrap();
});
Ok(())
}
}
wasi::cli::command::export!(TokioCliRunner);