Move iroh.proto to new crate

This commit is contained in:
fluo10 2025-09-05 06:22:36 +09:00
parent b461dc39a7
commit d30188e7d9
21 changed files with 150 additions and 67 deletions

View file

@ -26,7 +26,7 @@ caretta-sync-macros = { path="macros", optional = true}
caretta-sync-core = {workspace = true, features = ["test"]}
[workspace]
members = [ ".", "core", "macros", "cli", "mobile", "examples/*" , "bevy"]
members = [ ".", "core", "macros", "cli", "mobile", "examples/*" , "bevy", "iroh-proto"]
resolver = "3"
[workspace.package]
@ -46,9 +46,15 @@ futures = { version = "0.3.31", features = ["executor"] }
serde = { version = "1.0.219", features = ["derive"] }
thiserror = "2.0.12"
tokio = { version = "1.45.0", features = ["macros", "rt", "rt-multi-thread"] }
tokio-stream = "0.1.17"
tonic = "0.14.0"
url = { version = "2.5.7", features = ["serde"] }
uuid = { version = "1.17.0", features = ["v7"] }
iroh = { version = "0.91.2", features = ["discovery-local-network", "discovery-pkarr-dht"] }
prost = "0.14.1"
prost-types = "0.14.1"
tonic-prost-build = "0.14.0"
tonic-prost = "0.14.0"
[profile.dev]
opt-level = 1

View file

@ -19,9 +19,9 @@ ciborium.workspace = true
clap = {workspace = true, optional = true}
dirs = "6.0.0"
futures.workspace = true
iroh = { version = "0.91.2", features = ["discovery-local-network", "discovery-pkarr-dht"] }
prost = "0.14.1"
prost-types = "0.14.1"
iroh.workspace = true
prost.workspace = true
prost-types.workspace = true
rusqlite = { version = "0.37.0", features = ["bundled"] }
serde.workspace = true
sysinfo = "0.37.0"
@ -51,4 +51,4 @@ objc2-app-kit = "0.3.1"
tempfile = "3.20.0"
[build-dependencies]
tonic-prost-build = "0.14.0"
tonic-prost-build.workspace = true

View file

@ -1,4 +1,4 @@
use std::ffi::OsString;
use std::{array::TryFromSliceError, ffi::OsString};
#[derive(thiserror::Error, Debug)]
pub enum Error {
@ -23,6 +23,10 @@ pub enum Error {
#[cfg(feature="cli")]
#[error("Parse args error: {0}")]
ParseCommand(#[from] clap::Error),
#[error("Signature error: {0}")]
Signature(#[from] ed25519_dalek::SignatureError),
#[error("slice parse error: {0}")]
SliceTryFrom(#[from] TryFromSliceError),
#[error("toml deserialization error: {0}")]
TomlDe(#[from] toml::de::Error),
#[error("toml serialization error: {0}")]

View file

@ -1,11 +0,0 @@
use iroh::NodeId;
use crate::proto::iroh::RemoteInfoRequest;
impl From<NodeId> for RemoteInfoRequest {
fn from(value: NodeId) -> Self {
Self {
node_id : value.to_string()
}
}
}

View file

@ -1,3 +1 @@
pub mod iroh;
tonic::include_proto!("caretta_sync");

View file

@ -1,29 +0,0 @@
use crate::{global::{DATABASE_CONNECTION}, proto::CachedAddressMessage};
use futures::future::join_all;
use tonic::{Request, Response, Status};
use crate::proto::{cached_peer_service_server::{CachedPeerServiceServer}, CachedPeerListRequest, CachedPeerListResponse, CachedPeerMessage};
#[derive(Debug, Default)]
pub struct CachedPeerService {}
#[tonic::async_trait]
impl crate::proto::cached_peer_service_server::CachedPeerService for CachedPeerService {
async fn list(&self, request: Request<CachedPeerListRequest>) -> Result<Response<CachedPeerListResponse>, Status> {
println!("Got a request: {:?}", request);
let reply = CachedPeerListResponse {
peers: join_all( CachedPeerEntity::find().all(DATABASE_CONNECTIONS.get_cache_unchecked()).await.or_else(|e| Err(Status::from_error(Box::new(e))))?.iter().map(|x| async move {
let addresses = CachedAddressEntity::find()
.all(DATABASE_CONNECTIONS.get_cache_unchecked())
.await
.or_else(|e| Err(Status::from_error(Box::new(e))))?;
Ok::<CachedPeerMessage, Status>(CachedPeerMessage::from((x, &addresses)))
})).await.into_iter().collect::<Result<Vec<_>,_>>()?,
};
Ok(Response::new(reply))
}
}

View file

@ -1 +1 @@
pub mod cached_peer;
pub mod iroh;

21
iroh-proto/Cargo.toml Normal file
View file

@ -0,0 +1,21 @@
[package]
name = "iroh-proto"
edition.workspace = true
version = "0.1.0-alpha"
description = "protobuf definition and tonic server implemention for fetch iroh server infomation"
license.workspace = true
repository.workspace = true
[dependencies]
ed25519-dalek = { version = "2.2.0", features = ["signature"] }
futures.workspace = true
iroh.workspace = true
prost.workspace = true
prost-types.workspace = true
tokio-stream.workspace = true
tonic.workspace = true
tonic-prost.workspace = true
thiserror.workspace = true
[build-dependencies]
tonic-prost-build.workspace = true

4
iroh-proto/build.rs Normal file
View file

@ -0,0 +1,4 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_prost_build::compile_protos("proto/iroh.proto")?;
Ok(())
}

View file

@ -4,18 +4,22 @@ import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
service Iroh {
rpc RemoteInfo(RemoteInfoRequest) returns (RemoteInfoMessage);
rpc RemoteInfoIter(RemoteInfoIterRequest) returns (stream RemoteInfoMessage);
rpc RemoteInfo(RemoteInfoRequest) returns (RemoteInfoResponse);
rpc RemoteInfoIter(RemoteInfoIterRequest) returns (stream RemoteInfoResponse);
}
message RemoteInfoRequest {
string node_id = 1;
bytes node_id = 1;
}
message RemoteInfoIterRequest {}
message RemoteInfoResponse {
RemoteInfoMessage remote_info = 1;
}
message RemoteInfoMessage {
string node_id = 1;
bytes node_id = 1;
string relay_url = 2;
repeated DirectAddrInfoMessage addrs = 3;
string conn_type = 4;

9
iroh-proto/src/error.rs Normal file
View file

@ -0,0 +1,9 @@
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Duration parse error: {0}")]
Duration(#[from] prost_types::DurationError),
#[error("Signature error: {0}")]
Signature(#[from] ed25519_dalek::SignatureError),
#[error("slice parse error: {0}")]
SliceTryFrom(#[from] std::array::TryFromSliceError)
}

3
iroh-proto/src/lib.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod error;
pub mod proto;
pub mod server;

View file

@ -1,18 +1,17 @@
use iroh::endpoint::DirectAddrInfo;
use prost_types::DurationError;
use crate::proto::iroh::{DirectAddrInfoMessage, SourceMessage};
use crate::{error::Error, proto::{DirectAddrInfoMessage, LastControlMessage, SourceMessage}};
impl TryFrom<DirectAddrInfo> for DirectAddrInfoMessage {
type Error = DurationError;
type Error = Error;
fn try_from(value: DirectAddrInfo) -> Result<Self, Self::Error> {
Ok(DirectAddrInfoMessage {
addr: value.addr.to_string(),
latency: value.latency.map(|x| x.try_into()).transpose()?,
last_control: value.last_control.map(|x| super::LastControlMessage::try_from(x)).transpose()?,
last_control: value.last_control.map(|x| LastControlMessage::try_from(x)).transpose()?,
last_payload: value.last_payload.map(|x| x.try_into()).transpose()?,
last_alive: value.last_alive.map(|x| x.try_into()).transpose()?,
sources: value.sources.into_iter().map(|x| SourceMessage::try_from(x)).collect::<Result<Vec<SourceMessage>, DurationError>>()?
sources: value.sources.into_iter().map(|x| SourceMessage::try_from(x)).collect::<Result<Vec<SourceMessage>, Error>>()?
})
}
}

View file

@ -3,7 +3,7 @@ use std::time::Duration;
use iroh::endpoint::ControlMsg;
use prost_types::DurationError;
use crate::proto::iroh::LastControlMessage;
use crate::proto::LastControlMessage;
impl TryFrom<(Duration, ControlMsg)> for LastControlMessage {
type Error = DurationError;

View file

@ -3,6 +3,7 @@ mod last_control_message;
mod remote_info_iter_request;
mod remote_info_message;
mod remote_info_request;
mod remote_info_response;
mod source_message;
tonic::include_proto!("iroh");

View file

@ -1,4 +1,4 @@
use crate::proto::iroh::RemoteInfoIterRequest;
use crate::proto::RemoteInfoIterRequest;
impl RemoteInfoIterRequest {
pub fn new() -> Self {

View file

@ -1,17 +1,16 @@
use iroh::endpoint::RemoteInfo;
use prost_types::DurationError;
use crate::proto::iroh::{DirectAddrInfoMessage, RemoteInfoMessage};
use crate::{error::Error, proto::{DirectAddrInfoMessage, RemoteInfoMessage}};
impl TryFrom<RemoteInfo> for RemoteInfoMessage {
type Error = DurationError;
type Error = Error;
fn try_from(value: RemoteInfo) -> Result<Self, Self::Error> {
Ok(Self {
node_id: value.node_id.to_string(),
node_id: Vec::from(value.node_id.as_bytes()),
relay_url: value.relay_url.map_or(String::from(""), |x| x.relay_url.to_string()),
addrs: value.addrs.into_iter()
.map(|x| DirectAddrInfoMessage::try_from(x))
.collect::<Result<Vec<DirectAddrInfoMessage>,DurationError>>()?,
.collect::<Result<Vec<DirectAddrInfoMessage>,Error>>()?,
conn_type: value.conn_type.to_string(),
latency: value.latency.map(|x| x.try_into()).transpose()?,
last_used: value.last_used.map(|x| x.try_into()).transpose()?,

View file

@ -0,0 +1,19 @@
use iroh::NodeId;
use crate::proto::RemoteInfoRequest;
impl From<NodeId> for RemoteInfoRequest {
fn from(value: NodeId) -> Self {
Self {
node_id : Vec::from(value.as_bytes())
}
}
}
impl TryFrom<RemoteInfoRequest> for NodeId {
type Error = crate::error::Error;
fn try_from(value: RemoteInfoRequest) -> Result<Self, Self::Error> {
let slice: [u8; 32] = value.node_id[0..32].try_into()?;
Ok(NodeId::from_bytes(&slice)?)
}
}

View file

@ -0,0 +1,16 @@
use crate::{ proto::{RemoteInfoMessage, RemoteInfoResponse}};
impl From<RemoteInfoMessage> for RemoteInfoResponse {
fn from(value: RemoteInfoMessage) -> Self {
Self {
remote_info: Some(value)
}
}
}
impl From<Option<RemoteInfoMessage>> for RemoteInfoResponse {
fn from(value: Option<RemoteInfoMessage>) -> Self {
Self{
remote_info: value,
}
}
}

View file

@ -2,10 +2,10 @@ use std::time::Duration;
use iroh::endpoint::Source;
use crate::proto::iroh::SourceMessage;
use crate::{error::Error, proto::SourceMessage};
impl TryFrom<(Source, Duration)> for SourceMessage {
type Error = prost_types::DurationError;
type Error = Error;
fn try_from(src: (Source, Duration)) -> Result<Self, Self::Error> {
let (source, duration )= src;
Ok(Self {

40
iroh-proto/src/server.rs Normal file
View file

@ -0,0 +1,40 @@
use std::pin::Pin;
use iroh::{Endpoint, NodeId};
use tonic::{Response, Request, Status};
use tokio_stream::Stream;
use crate::proto::{RemoteInfoIterRequest, RemoteInfoMessage, RemoteInfoRequest, RemoteInfoResponse};
#[tonic::async_trait]
pub trait AsEndpoint: Send + Sync + 'static {
async fn as_endpoint(&self) -> &Endpoint;
}
pub struct IrohServer<T>
where
T: AsEndpoint
{
endpoint: T
}
#[tonic::async_trait]
impl<T> crate::proto::iroh_server::Iroh for IrohServer<T>
where
T: AsEndpoint
{
type RemoteInfoIterStream = Pin<Box<dyn Stream<Item = Result<RemoteInfoResponse, Status>> + Send>>;
async fn remote_info(&self, request: Request<RemoteInfoRequest>) -> Result<Response<RemoteInfoResponse>, Status> {
let node_id = NodeId::try_from(request.into_inner()).or_else(|e| {
Err(Status::from_error(Box::new(e)))
})?;
let remote_info: Option<RemoteInfoMessage> = self.endpoint.as_endpoint().await.remote_info(node_id).map(|x| x.try_into()).transpose().or_else(|e| {
Err(Status::from_error(Box::new(e)))
})?;
Ok(Response::new(RemoteInfoResponse::from(remote_info)))
}
async fn remote_info_iter(&self, _: Request<RemoteInfoIterRequest>) -> Result<Response<Self::RemoteInfoIterStream>, Status> {
let iter = self.endpoint.as_endpoint().await.remote_info_iter();
let stream = futures::stream::iter(iter);
todo!()
}
}