Hooked up test call from frontend!

This commit is contained in:
Gregory Schier
2024-01-29 20:50:43 -08:00
parent 373915671e
commit c51d5c5377
9 changed files with 821 additions and 9 deletions

324
src-tauri/Cargo.lock generated
View File

@@ -77,9 +77,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.75"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "async-compression"
@@ -95,6 +95,39 @@ dependencies = [
"tokio",
]
[[package]]
name = "async-stream"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
dependencies = [
"async-stream-impl",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-stream-impl"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "async-trait"
version = "0.1.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "atk"
version = "0.15.1"
@@ -134,6 +167,51 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
dependencies = [
"async-trait",
"axum-core",
"bitflags 1.3.2",
"bytes",
"futures-util",
"http",
"http-body",
"hyper",
"itoa 1.0.9",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"sync_wrapper",
"tower",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http-body",
"mime",
"rustversion",
"tower-layer",
"tower-service",
]
[[package]]
name = "backtrace"
version = "0.3.69"
@@ -1596,6 +1674,24 @@ dependencies = [
"system-deps 6.2.0",
]
[[package]]
name = "grpc"
version = "0.1.0"
dependencies = [
"anyhow",
"log",
"once_cell",
"prost",
"prost-reflect",
"prost-types",
"serde",
"serde_json",
"tokio",
"tokio-stream",
"tonic",
"tonic-reflection",
]
[[package]]
name = "gtk"
version = "0.15.5"
@@ -1853,6 +1949,18 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-timeout"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
dependencies = [
"hyper",
"pin-project-lite",
"tokio",
"tokio-io-timeout",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
@@ -2423,6 +2531,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "matchit"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "md-5"
version = "0.10.6"
@@ -2839,6 +2953,15 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]]
name = "os_info"
version = "3.7.0"
@@ -3074,6 +3197,26 @@ dependencies = [
"siphasher",
]
[[package]]
name = "pin-project"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "pin-project-lite"
version = "0.2.13"
@@ -3230,6 +3373,64 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "prost"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-derive"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "prost-reflect"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057237efdb71cf4b3f9396302a3d6599a92fa94063ba537b66130980ea9909f3"
dependencies = [
"base64 0.21.5",
"once_cell",
"prost",
"prost-reflect-derive",
"prost-types",
"serde",
"serde-value",
]
[[package]]
name = "prost-reflect-derive"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "172da1212c02be2c94901440cb27183cd92bff00ebacca5c323bf7520b8f9c04"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "prost-types"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e"
dependencies = [
"prost",
]
[[package]]
name = "psl-types"
version = "2.0.11"
@@ -3736,18 +3937,28 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.195"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.195"
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
@@ -3756,9 +3967,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.111"
version = "1.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
dependencies = [
"itoa 1.0.9",
"ryu",
@@ -4314,6 +4525,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "synstructure"
version = "0.13.0"
@@ -4829,9 +5046,31 @@ dependencies = [
"num_cpus",
"pin-project-lite",
"socket2 0.5.5",
"tokio-macros",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-io-timeout"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
dependencies = [
"pin-project-lite",
"tokio",
]
[[package]]
name = "tokio-macros"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
@@ -4935,6 +5174,72 @@ dependencies = [
"winnow",
]
[[package]]
name = "tonic"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e"
dependencies = [
"async-stream",
"async-trait",
"axum",
"base64 0.21.5",
"bytes",
"h2",
"http",
"http-body",
"hyper",
"hyper-timeout",
"percent-encoding",
"pin-project",
"prost",
"tokio",
"tokio-stream",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tonic-reflection"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fa37c513df1339d197f4ba21d28c918b9ef1ac1768265f11ecb6b7f1cba1b76"
dependencies = [
"prost",
"prost-types",
"tokio",
"tokio-stream",
"tonic",
]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"indexmap 1.9.3",
"pin-project",
"pin-project-lite",
"rand 0.8.5",
"slab",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
[[package]]
name = "tower-service"
version = "0.3.2"
@@ -5925,6 +6230,7 @@ dependencies = [
"cookie 0.18.0",
"datetime",
"futures",
"grpc",
"http",
"log",
"objc",

View File

@@ -1,3 +1,4 @@
workspace = { members = ["grpc"] }
[package]
name = "yaak-app"
version = "0.0.0"
@@ -59,6 +60,7 @@ log = "0.4.20"
datetime = "0.5.2"
window-shadows = "0.2.2"
reqwest_cookie_store = "0.6.0"
grpc = { path = "./grpc" }
[features]
# by default Tauri runs in production mode

18
src-tauri/grpc/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "grpc"
version = "0.1.0"
edition = "2021"
[dependencies]
tonic = "0.10.2"
prost = "0.12"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
tonic-reflection = "0.10.2"
tokio-stream = "0.1.14"
prost-types = "0.12.3"
serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113"
prost-reflect = { version = "0.12.0", features = ["serde", "derive"] }
log = "0.4.20"
once_cell = { version = "1.19.0", features = [] }
anyhow = "1.0.79"

View File

@@ -0,0 +1,52 @@
use prost_reflect::prost::Message;
use prost_reflect::{DynamicMessage, MethodDescriptor};
use tonic::codec::{Codec, DecodeBuf, Decoder, EncodeBuf, Encoder};
use tonic::Status;
#[derive(Clone)]
pub struct DynamicCodec(MethodDescriptor);
impl DynamicCodec {
#[allow(dead_code)]
pub fn new(md: MethodDescriptor) -> Self {
Self(md)
}
}
impl Codec for DynamicCodec {
type Encode = DynamicMessage;
type Decode = DynamicMessage;
type Encoder = Self;
type Decoder = Self;
fn encoder(&mut self) -> Self::Encoder {
self.clone()
}
fn decoder(&mut self) -> Self::Decoder {
self.clone()
}
}
impl Encoder for DynamicCodec {
type Item = DynamicMessage;
type Error = Status;
fn encode(&mut self, item: Self::Item, dst: &mut EncodeBuf<'_>) -> Result<(), Self::Error> {
item.encode(dst)
.expect("buffer is too small to decode this message");
Ok(())
}
}
impl Decoder for DynamicCodec {
type Item = DynamicMessage;
type Error = Status;
fn decode(&mut self, src: &mut DecodeBuf<'_>) -> Result<Option<Self::Item>, Self::Error> {
let mut msg = DynamicMessage::new(self.0.output());
msg.merge(src)
.map_err(|err| Status::internal(err.to_string()))?;
Ok(Some(msg))
}
}

View File

@@ -0,0 +1,175 @@
use std::collections::HashMap;
use prost_reflect::{DescriptorPool, MessageDescriptor};
use prost_types::field_descriptor_proto;
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize)]
#[serde(default, rename_all = "camelCase")]
pub struct JsonSchemaEntry {
#[serde(skip_serializing_if = "Option::is_none")]
title: Option<String>,
#[serde(rename = "type")]
type_: JsonType,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
properties: Option<HashMap<String, JsonSchemaEntry>>,
#[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
enum_: Option<Vec<String>>,
/// Don't allow any other properties in the object
#[serde(skip_serializing_if = "Option::is_none")]
additional_properties: Option<bool>,
/// Set all properties to required
#[serde(skip_serializing_if = "Option::is_none")]
required: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
items: Option<Box<JsonSchemaEntry>>,
}
enum JsonType {
String,
Number,
Object,
Array,
Boolean,
Null,
_UNKNOWN,
}
impl Default for JsonType {
fn default() -> Self {
JsonType::_UNKNOWN
}
}
impl serde::Serialize for JsonType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
JsonType::String => serializer.serialize_str("string"),
JsonType::Number => serializer.serialize_str("number"),
JsonType::Object => serializer.serialize_str("object"),
JsonType::Array => serializer.serialize_str("array"),
JsonType::Boolean => serializer.serialize_str("boolean"),
JsonType::Null => serializer.serialize_str("null"),
JsonType::_UNKNOWN => serializer.serialize_str("unknown"),
}
}
}
impl<'de> serde::Deserialize<'de> for JsonType {
fn deserialize<D>(deserializer: D) -> Result<JsonType, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"string" => Ok(JsonType::String),
"number" => Ok(JsonType::Number),
"object" => Ok(JsonType::Object),
"array" => Ok(JsonType::Array),
"boolean" => Ok(JsonType::Boolean),
"null" => Ok(JsonType::Null),
_ => Ok(JsonType::_UNKNOWN),
}
}
}
pub fn message_to_json_schema(pool: &DescriptorPool, message: MessageDescriptor) -> JsonSchemaEntry {
let mut schema = JsonSchemaEntry {
title: Some(message.name().to_string()),
type_: JsonType::Object, // Messages are objects
..Default::default()
};
let mut properties = HashMap::new();
message.fields().for_each(|f| match f.kind() {
prost_reflect::Kind::Message(m) => {
properties.insert(f.name().to_string(), message_to_json_schema(pool, m));
}
prost_reflect::Kind::Enum(e) => {
properties.insert(
f.name().to_string(),
JsonSchemaEntry {
type_: map_proto_type_to_json_type(f.field_descriptor_proto().r#type()),
enum_: Some(e.values().map(|v| v.name().to_string()).collect::<Vec<_>>()),
..Default::default()
},
);
}
_ => {
// TODO: Handle repeated label
match f.field_descriptor_proto().label() {
field_descriptor_proto::Label::Repeated => {
// TODO: Handle more complex repeated types. This just handles primitives for now
properties.insert(
f.name().to_string(),
JsonSchemaEntry {
type_: JsonType::Array,
items: Some(Box::new(JsonSchemaEntry {
type_: map_proto_type_to_json_type(
f.field_descriptor_proto().r#type(),
),
..Default::default()
})),
..Default::default()
},
);
}
_ => {
// Regular JSON field
properties.insert(
f.name().to_string(),
JsonSchemaEntry {
type_: map_proto_type_to_json_type(f.field_descriptor_proto().r#type()),
..Default::default()
},
);
}
};
}
});
schema.properties = Some(properties);
schema.required = Some(
message
.fields()
.map(|f| f.name().to_string())
.collect::<Vec<_>>(),
);
schema
}
fn map_proto_type_to_json_type(proto_type: field_descriptor_proto::Type) -> JsonType {
match proto_type {
field_descriptor_proto::Type::Double => JsonType::Number,
field_descriptor_proto::Type::Float => JsonType::Number,
field_descriptor_proto::Type::Int64 => JsonType::Number,
field_descriptor_proto::Type::Uint64 => JsonType::Number,
field_descriptor_proto::Type::Int32 => JsonType::Number,
field_descriptor_proto::Type::Fixed64 => JsonType::Number,
field_descriptor_proto::Type::Fixed32 => JsonType::Number,
field_descriptor_proto::Type::Bool => JsonType::Boolean,
field_descriptor_proto::Type::String => JsonType::String,
field_descriptor_proto::Type::Group => JsonType::_UNKNOWN,
field_descriptor_proto::Type::Message => JsonType::Object,
field_descriptor_proto::Type::Bytes => JsonType::String,
field_descriptor_proto::Type::Uint32 => JsonType::Number,
field_descriptor_proto::Type::Enum => JsonType::String,
field_descriptor_proto::Type::Sfixed32 => JsonType::Number,
field_descriptor_proto::Type::Sfixed64 => JsonType::Number,
field_descriptor_proto::Type::Sint32 => JsonType::Number,
field_descriptor_proto::Type::Sint64 => JsonType::Number,
}
}

79
src-tauri/grpc/src/lib.rs Normal file
View File

@@ -0,0 +1,79 @@
use prost_reflect::DynamicMessage;
use serde::{Deserialize, Serialize};
use serde_json::Deserializer;
use tonic::IntoRequest;
use tonic::transport::Uri;
use crate::codec::DynamicCodec;
use crate::proto::{fill_pool, method_desc_to_path};
mod codec;
mod json_schema;
mod proto;
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct ServiceDefinition {
pub name: String,
pub methods: Vec<MethodDefinition>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct MethodDefinition {
pub name: String,
pub schema: String,
}
pub async fn call(uri: &Uri, service: &str, method: &str, message_json: &str) -> String {
let (pool, conn) = fill_pool(uri).await;
let service = pool.get_service_by_name(service).unwrap();
let method = &service.methods().find(|m| m.name() == method).unwrap();
let input_message = method.input();
let mut deserializer = Deserializer::from_str(message_json);
let req_message = DynamicMessage::deserialize(input_message, &mut deserializer).unwrap();
deserializer.end().unwrap();
let mut client = tonic::client::Grpc::new(conn);
println!(
"\n---------- SENDING -----------------\n{}",
serde_json::to_string_pretty(&req_message).expect("json")
);
let req = req_message.into_request();
let path = method_desc_to_path(method);
let codec = DynamicCodec::new(method.clone());
client.ready().await.unwrap();
let resp = client.unary(req, path, codec).await.unwrap();
let response_json = serde_json::to_string_pretty(&resp.into_inner()).expect("json to string");
println!("\n---------- RECEIVING ---------------\n{}", response_json,);
response_json
}
pub async fn callable(uri: &Uri) -> Vec<ServiceDefinition> {
let (pool, _) = fill_pool(uri).await;
pool.services()
.map(|s| {
let mut def = ServiceDefinition {
name: s.full_name().to_string(),
..Default::default()
};
for method in s.methods() {
let input_message = method.input();
def.methods.push(MethodDefinition {
name: method.name().to_string(),
schema: serde_json::to_string_pretty(&json_schema::message_to_json_schema(
&pool,
input_message,
))
.unwrap(),
})
}
def
})
.collect::<Vec<_>>()
}

137
src-tauri/grpc/src/proto.rs Normal file
View File

@@ -0,0 +1,137 @@
use std::ops::Deref;
use std::str::FromStr;
use anyhow::anyhow;
use prost::Message;
use prost_reflect::{DescriptorPool, MethodDescriptor};
use prost_types::FileDescriptorProto;
use tokio_stream::StreamExt;
use tonic::codegen::http::uri::PathAndQuery;
use tonic::Request;
use tonic::transport::{Channel, Uri};
use tonic_reflection::pb::server_reflection_client::ServerReflectionClient;
use tonic_reflection::pb::server_reflection_request::MessageRequest;
use tonic_reflection::pb::server_reflection_response::MessageResponse;
use tonic_reflection::pb::ServerReflectionRequest;
pub async fn fill_pool(uri: &Uri) -> (DescriptorPool, Channel) {
let mut pool = DescriptorPool::new();
let conn = tonic::transport::Endpoint::new(uri.clone())
.unwrap()
.connect()
.await
.unwrap();
let mut client = ServerReflectionClient::new(conn.clone());
let services = list_services(&mut client).await;
for service in services {
if service == "grpc.reflection.v1alpha.ServerReflection" {
continue;
}
file_descriptor_set_from_service_name(&service, &mut pool, &mut client).await;
}
(pool, conn)
}
async fn list_services(reflect_client: &mut ServerReflectionClient<Channel>) -> Vec<String> {
let response =
send_reflection_request(reflect_client, MessageRequest::ListServices("".into())).await;
let list_services_response = match response {
MessageResponse::ListServicesResponse(resp) => resp,
_ => panic!("Expected a ListServicesResponse variant"),
};
list_services_response
.service
.iter()
.map(|s| s.name.clone())
.collect::<Vec<_>>()
}
async fn file_descriptor_set_from_service_name(
service_name: &str,
pool: &mut DescriptorPool,
client: &mut ServerReflectionClient<Channel>,
) {
let response = send_reflection_request(
client,
MessageRequest::FileContainingSymbol(service_name.into()),
)
.await;
let file_descriptor_response = match response {
MessageResponse::FileDescriptorResponse(resp) => resp,
_ => panic!("Expected a FileDescriptorResponse variant"),
};
for fd in file_descriptor_response.file_descriptor_proto {
let fdp = FileDescriptorProto::decode(fd.deref()).unwrap();
// Add deps first or else we'll get an error
for dep_name in fdp.clone().dependency {
file_descriptor_set_by_filename(&dep_name, pool, client).await;
}
pool.add_file_descriptor_proto(fdp)
.expect("add file descriptor proto");
}
}
async fn file_descriptor_set_by_filename(
filename: &str,
pool: &mut DescriptorPool,
client: &mut ServerReflectionClient<Channel>,
) {
// We already fetched this file
if let Some(_) = pool.get_file_by_name(filename) {
return;
}
let response =
send_reflection_request(client, MessageRequest::FileByFilename(filename.into())).await;
let file_descriptor_response = match response {
MessageResponse::FileDescriptorResponse(resp) => resp,
_ => panic!("Expected a FileDescriptorResponse variant"),
};
for fd in file_descriptor_response.file_descriptor_proto {
let fdp = FileDescriptorProto::decode(fd.deref()).unwrap();
pool.add_file_descriptor_proto(fdp)
.expect("add file descriptor proto");
}
}
async fn send_reflection_request(
client: &mut ServerReflectionClient<Channel>,
message: MessageRequest,
) -> MessageResponse {
let reflection_request = ServerReflectionRequest {
host: "".into(), // Doesn't matter
message_request: Some(message),
};
let request = Request::new(tokio_stream::once(reflection_request));
client
.server_reflection_info(request)
.await
.expect("server reflection failed")
.into_inner()
.next()
.await
.expect("steamed response")
.expect("successful response")
.message_response
.expect("some MessageResponse")
}
pub fn method_desc_to_path(md: &MethodDescriptor) -> PathAndQuery {
let full_name = md.full_name();
let (namespace, method_name) = full_name
.rsplit_once('.')
.ok_or_else(|| anyhow!("invalid method path"))
.expect("invalid method path");
PathAndQuery::from_str(&format!("/{}/{}", namespace, method_name)).expect("invalid method path")
}

View File

@@ -8,12 +8,15 @@ extern crate core;
#[macro_use]
extern crate objc;
use ::http::Uri;
use std::collections::HashMap;
use std::env::current_dir;
use std::fs::{create_dir_all, read_to_string, File};
use std::process::exit;
use std::str::FromStr;
use fern::colors::ColoredLevelConfig;
use grpc::ServiceDefinition;
use log::{debug, error, info, warn};
use rand::random;
use serde::Serialize;
@@ -75,6 +78,29 @@ async fn migrate_db(
Ok(())
}
#[tauri::command]
async fn grpc_reflect(
endpoint: &str,
// app_handle: AppHandle<Wry>,
// db_instance: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<Vec<ServiceDefinition>, String> {
let uri = Uri::from_str(endpoint).map_err(|e| e.to_string())?;
Ok(grpc::callable(&uri).await)
}
#[tauri::command]
async fn grpc_call_unary(
endpoint: &str,
service: &str,
method: &str,
message: &str,
// app_handle: AppHandle<Wry>,
// db_instance: State<'_, Mutex<Pool<Sqlite>>>,
) -> Result<String, String> {
let uri = Uri::from_str(endpoint).map_err(|e| e.to_string())?;
Ok(grpc::call(&uri, service, method, message).await)
}
#[tauri::command]
async fn send_ephemeral_request(
mut request: models::HttpRequest,
@@ -977,6 +1003,8 @@ fn main() {
get_request,
get_settings,
get_workspace,
grpc_call_unary,
grpc_reflect,
import_data,
list_cookie_jars,
list_environments,

View File

@@ -1,3 +1,4 @@
import { invoke } from '@tauri-apps/api';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { attachConsole } from 'tauri-plugin-log-api';
@@ -10,6 +11,20 @@ import { setAppearanceOnDocument } from './lib/theme/window';
import { appWindow } from '@tauri-apps/api/window';
import { type } from '@tauri-apps/api/os';
try {
const services: any = await invoke('grpc_reflect', { endpoint: 'http://localhost:50051' });
console.log('SERVICES', services);
const response = await invoke('grpc_call_unary', {
endpoint: 'http://localhost:50051',
service: services[0].name,
method: services[0].methods[0].name,
message: '{"name": "Greg"}',
});
console.log('RESPONSE', response);
} catch (err) {
console.log('ERROR', err);
}
// Hide decorations here because it doesn't work in Rust for some reason (bug?)
const osType = await type();
if (osType !== 'Darwin') {