Skip to content

Commit

Permalink
Draft: Add resolving feature
Browse files Browse the repository at this point in the history
Closes #12
  • Loading branch information
hrzlgnm committed Mar 5, 2024
1 parent e8b61ef commit f30bd85
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 8 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ js-sys = "0.3"
serde = { version = "1", features = ["derive"] }
serde-wasm-bindgen = "0.6"
log = "^0.4.21"
console_error_panic_hook = "0.1.7"
console_log = "1.0.0"

[workspace]
members = ["src-tauri"]
55 changes: 52 additions & 3 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use std::sync::{Arc, Mutex};

use log::LevelFilter;
use mdns_sd::{ServiceDaemon, ServiceEvent};
use serde::{Deserialize, Serialize};
use std::{
collections::HashSet,
net::IpAddr,
sync::{Arc, Mutex},
};
use tauri::State;
use tauri_plugin_log::LogTarget;

Expand All @@ -19,6 +23,48 @@ fn get_shared_daemon() -> SharedServiceDaemon {
Arc::new(Mutex::new(daemon))
}

#[derive(Serialize, Deserialize, Clone, Debug)]
struct ResolvedService {
instance_name: String,
hostname: String,
port: u16,
addresses: HashSet<IpAddr>,
}

#[tauri::command]
fn resolve_service(service_type: String, state: State<Daemon>) -> Vec<ResolvedService> {
log::info!("Resolving {}", service_type);
let mdns = state.shared.lock().unwrap();
let receiver = mdns
.browse(service_type.as_str())
.expect("Failed to browse");
let mut result = vec![];
let mut done = false;
while let Ok(event) = receiver.recv() {
match event {
ServiceEvent::ServiceResolved(info) => {
result.push(ResolvedService {
instance_name: info.get_fullname().into(),
hostname: info.get_hostname().into(),
port: info.get_port(),
addresses: info.get_addresses().clone(),
});
}
ServiceEvent::SearchStarted(_) => {
if done {
let _ = mdns.stop_browse(service_type.as_str());
}
done = true;
}
ServiceEvent::SearchStopped(_) => {
break;
}
_ => {}
}
}
result
}

#[tauri::command]
fn enum_service_types(state: State<Daemon>) -> Vec<String> {
let mut found = vec![];
Expand Down Expand Up @@ -87,7 +133,10 @@ fn main() {
.level(LevelFilter::Info)
.build(),
)
.invoke_handler(tauri::generate_handler![enum_service_types])
.invoke_handler(tauri::generate_handler![
enum_service_types,
resolve_service
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
94 changes: 89 additions & 5 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
use leptos::*;
use serde_wasm_bindgen::from_value;
use std::{collections::HashSet, net::IpAddr};

use leptos::{html::Input, *};
use serde::{Deserialize, Serialize};
use serde_wasm_bindgen::{from_value, to_value};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "tauri"])]
async fn invoke(cmd: &str) -> JsValue;
async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}

type ServiceTypes = Vec<String>;

async fn enum_service_types() -> ServiceTypes {
let service_types: ServiceTypes = from_value(invoke("enum_service_types").await).unwrap();
log::info!("Received {:#?}", service_types);
let service_types: ServiceTypes =
from_value(invoke("enum_service_types", JsValue::UNDEFINED).await).unwrap();
service_types
}

#[derive(Serialize, Deserialize, Debug, Clone)]
struct ResolvedService {
instance_name: String,
hostname: String,
port: u16,
addresses: HashSet<IpAddr>,
}
type ResolvedServices = Vec<ResolvedService>;

#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
struct ResolveServiceArgs<'a> {
serviceType: &'a str,
}

async fn resolve_service(service_type: String) -> ResolvedServices {
log::debug!("resolve service: {}", service_type);

let args = to_value(&ResolveServiceArgs {
serviceType: &service_type,
})
.unwrap();
let resolved_services: ResolvedServices =
from_value(invoke("resolve_service", args).await).unwrap();
log::debug!("Resolved: {:#?}", resolved_services);
resolved_services
}

#[component]
fn ShowServices(services: ServiceTypes) -> impl IntoView {
view! {
Expand Down Expand Up @@ -52,10 +83,63 @@ fn ServiceTypeList() -> impl IntoView {
}
}

#[component]
fn ShowResolvedServices(services: ResolvedServices) -> impl IntoView {
view! {
<ul>
{services.into_iter()
.map(|n|
view! {
<div>Instance name: {n.instance_name}</div>
<div>Hostname: {n.hostname}</div>
<div>Port: {n.port}</div>
<div>IPs: {n.addresses.iter().map(|n|n.to_string()).collect::<Vec<String>>().join(", ")}</div>
})
.collect::<Vec<_>>()}
</ul>
}
}

#[component]
fn ResolveService() -> impl IntoView {
let (service, _) = create_signal("".to_string());
let resolve_action = create_action(|input: &String| {
let input = input.clone();
async move { resolve_service(input.clone()).await }
});
let input_element: NodeRef<Input> = create_node_ref();
let on_submit = move |ev: ev::SubmitEvent| {
ev.prevent_default();
let value = input_element.get().expect("<input> to exist").value();
resolve_action.dispatch(value);
};

let value = resolve_action.value();

view! {
<form on:submit=on_submit>
<input type="text"
value=service
node_ref=input_element
/>
<button type="submit">"Resolve"</button>
</form>
{move || match value.get() {
None => view! { <p>Click on Resolve</p> }.into_view(),
Some(services) => {
view! {
<ShowResolvedServices services />
}.into_view()
}
}}
}
}

#[component]
pub fn App() -> impl IntoView {
view! {
<main class="container">
<ResolveService />
<ServiceTypeList />
</main>
}
Expand Down

0 comments on commit f30bd85

Please sign in to comment.