arib-cli 0.2.0

Reads the signalling of ARIB broadcasts and descrambles them, as an example of the arib crate
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::ops::ControlFlow;
use std::time::Duration;

use clap::Args;

use crate::input::InputArgs;
use crate::json;
use crate::print::print_event;
use crate::si::{self, Event, Si};

/// Lists the events, the programme guide, the stream carries.
#[derive(Clone, Debug, Args)]
pub struct Options {
    #[command(flatten)]
    input: InputArgs,

    /// Lists the events of the service only.
    #[arg(long, value_parser = crate::print::parse_id)]
    service: Option<u16>,

    /// Seconds to read the stream for, if it does not end before.
    #[arg(long, default_value_t = 30)]
    timeout: u64,

    /// Prints the events as JSON Lines as they are found, again whenever more is found of one.
    #[arg(long)]
    json: bool,
}

pub fn run(options: &Options, verbose: bool) -> anyhow::Result<()> {
    // The same event comes again and again, in the present and following table and in the
    // schedule.
    let mut events = BTreeMap::<u16, BTreeMap<u16, Event>>::new();

    si::read(
        options.input.open()?,
        Duration::from_secs(options.timeout),
        |si| {
            if let Si::Events(found) = si
                && options
                    .service
                    .is_none_or(|service_id| service_id == found.service_id)
            {
                let service = events.entry(found.service_id).or_default();
                for event in found.events {
                    // The sections of the extended information repeat an event with only its
                    // details, which are put together with what the others tell of it.
                    let changed = match service.entry(event.id) {
                        Entry::Occupied(known) => {
                            let known = known.into_mut();
                            known.merge(event).then_some(known)
                        }
                        Entry::Vacant(entry) => Some(entry.insert(event)),
                    };
                    if options.json
                        && let Some(event) = changed
                    {
                        let mut value = json::event(event);
                        value["service_id"] = found.service_id.into();
                        println!("{value}");
                    }
                }
            }
            ControlFlow::Continue(())
        },
    )?;

    if options.json {
        return Ok(());
    }

    for (service_id, events) in events {
        println!("Service: {service_id:#06X}");

        let mut events: Vec<_> = events.into_values().collect();
        events.sort_by_key(|event| event.start_time);
        for event in &events {
            print_event(event, 2, verbose);
        }
    }

    Ok(())
}