use std::convert::identity;
use std::net::SocketAddr;
use hydroflow::hydroflow_syntax;
use hydroflow::scheduled::graph::Hydroflow;
use tokio::io::AsyncBufReadExt;
use tokio::net::UdpSocket;
use tokio_stream::wrappers::LinesStream;
use crate::helpers::{deserialize_msg, format_cycle, gen_bool, parse_edge, serialize_msg};
use crate::protocol::{Message, SimplePath};
use crate::Opts;
pub(crate) async fn run_detector(opts: Opts, peer_list: Vec<String>) {
let server_socket = UdpSocket::bind(("127.0.0.1", opts.port)).await.unwrap();
let (outbound, inbound, _) = hydroflow::util::udp_lines(server_socket);
let reader = tokio::io::BufReader::new(tokio::io::stdin());
let stdin_lines = LinesStream::new(reader.lines());
#[expect(clippy::map_identity, reason = "helps type inference?")]
let mut hf: Hydroflow = hydroflow_syntax! {
peers = source_iter(peer_list)
-> map(|s| s.parse::<SocketAddr>().unwrap())
-> tee();
outbound_chan = map(|(m,a)| (serialize_msg(m), a)) -> dest_sink(outbound);
inbound_chan = source_stream(inbound)
-> filter(|msg| {
// For some reason Windows generates connection reset errors on UDP sockets, even though UDP has no sessions.
// This code filters them out.
// `Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host."`
// https://stackoverflow.com/questions/10332630/connection-reset-on-receiving-packet-in-udp-server
// TODO(mingwei): Clean this up, figure out how to configure windows UDP sockets correctly.
if let Err(tokio_util::codec::LinesCodecError::Io(io_err)) = msg {
io_err.kind() != std::io::ErrorKind::ConnectionReset
} else {
true
}
})
-> map(deserialize_msg::<Message>);
gossip_join = cross_join::<'tick>()
-> filter(|_| gen_bool(0.8)) -> outbound_chan;
gossip = map(identity) -> persist::<'static>() -> [0]gossip_join;
peers[1] -> persist::<'static>() -> [1]gossip_join;
peers[2] -> for_each(|s| println!("Peer: {:?}", s));
source_iter([()]) -> for_each(|_s| println!("Type in an edge as a tuple of two integers (x,y): "));
new_edges = source_stream(stdin_lines)
-> filter_map(|line| {
parse_edge(line.unwrap())});
edges = union() -> tee();
edges[0] -> defer_tick() -> [1]edges;
new_edges -> [0]edges;
edges[1] -> fold::<'static>(Message::new, |m: &mut Message, edge| {
m.edges.insert(edge);
}) -> gossip;
inbound_chan[4] -> flat_map(|m| m.edges) -> [2]edges;
paths = union();
edges[2] -> map(|(from, to)| (from, to, SimplePath::new(vec![from, to]))) -> [0]paths;
new_paths = join::<'static>() -> map(|(_mid, ((from, mut path), to))| {
path.push(to);
(from, to, path)
}) -> tee();
paths -> filter_map(|(from, to, path)| {
if path.cycle() {None} // don't extend self-loops
else {Some((to, (from, path)))}
}) -> [0]new_paths;
edges[3] -> map(|(from, to)| (from, to)) -> [1]new_paths;
new_paths[0]
-> filter_map(|(from, to, path): (u32, u32, SimplePath<u32>)| if from == to {Some(path.canonical())} else {None})
-> unique::<'static>()
-> for_each(|path: Vec<u32>| {
println!("path found: {}", format_cycle(path));
});
new_paths[1] -> [1]paths;
};
#[cfg(feature = "debugging")]
if let Some(graph) = opts.graph {
let serde_graph = hf
.meta_graph()
.expect("No graph found, maybe failed to parse.");
serde_graph.open_graph(graph, opts.write_config).unwrap();
}
hf.run_async().await.unwrap();
}