use std::collections::BTreeMap;
use std::ops::ControlFlow;
use std::time::Duration;
use clap::Args;
use crate::component::Stream;
use crate::input::InputArgs;
use crate::json;
use crate::print::{or_unknown, print_event, print_service, print_stream};
use crate::si::{self, Event, Network, Service, Si};
#[derive(Clone, Debug, Args)]
pub struct Options {
#[command(flatten)]
input: InputArgs,
#[arg(long, default_value_t = 10)]
timeout: u64,
#[arg(long)]
json: bool,
}
#[derive(Default)]
struct Status {
network: Option<Network>,
services: Option<(u16, Vec<Service>)>,
present_events: BTreeMap<u16, Event>,
streams: BTreeMap<u16, Vec<Stream>>,
}
impl Status {
fn read(&mut self, si: Si) -> ControlFlow<()> {
match si {
Si::Network(network) => self.network = Some(network),
Si::Services(services) if services.actual => {
self.services = Some((services.stream_id, services.services));
}
Si::Streams(streams) => {
self.streams.insert(streams.service_id, streams.streams);
}
Si::Events(events) if events.present_following && events.section_number == 0 => {
if let Some(event) = events.events.into_iter().next() {
self.present_events.insert(events.service_id, event);
}
}
_ => {}
}
if self.is_complete() {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
fn is_complete(&self) -> bool {
let Some((_, services)) = &self.services else {
return false;
};
self.network.is_some()
&& services.iter().all(|service| {
self.present_events.contains_key(&service.id)
&& self.streams.contains_key(&service.id)
})
}
fn to_json(&self) -> serde_json::Value {
let (stream_id, services) = self
.services
.as_ref()
.map_or((None, &[][..]), |(stream_id, services)| {
(Some(stream_id), &services[..])
});
let services: Vec<_> = services
.iter()
.map(|service| {
let event = self.present_events.get(&service.id);
let streams: Vec<_> = self
.streams
.get(&service.id)
.into_iter()
.flatten()
.map(|stream| json::stream(stream, stream_details(stream, event)))
.collect();
let mut value = json::service(service);
value["streams"] = streams.into();
value["event"] = event.map(json::event).into();
value
})
.collect();
serde_json::json!({
"network": self.network.as_ref().map(json::network),
"stream_id": stream_id,
"services": services,
})
}
fn print(&self, verbose: bool) {
match &self.network {
Some(network) => println!("Network: {:#06X} {}", network.id, or_unknown(&network.name)),
None => println!("Network: (not found)"),
}
let Some((stream_id, services)) = &self.services else {
println!("Services: (not found)");
return;
};
println!("Stream: {stream_id:#06X}");
println!("Services:");
for service in services {
print_service(service, 2);
let event = self.present_events.get(&service.id);
for stream in self.streams.get(&service.id).into_iter().flatten() {
print_stream(stream, stream_details(stream, event), 4);
}
match event {
Some(event) => print_event(event, 4, verbose),
None => println!(" (no event on air)"),
}
}
}
}
fn stream_details<'a>(stream: &'a Stream, event: Option<&'a Event>) -> &'a [String] {
event
.and_then(|event| {
event
.components
.iter()
.find(|component| Some(component.component_tag) == stream.component_tag)
})
.map_or(&stream.details, |component| &component.details)
}
pub fn run(options: &Options, verbose: bool) -> anyhow::Result<()> {
let mut status = Status::default();
si::read(
options.input.open()?,
Duration::from_secs(options.timeout),
|si| status.read(si),
)?;
if options.json {
println!("{}", status.to_json());
} else {
status.print(verbose);
}
Ok(())
}