wavepeek 2.1.0

Command-line tool for RTL waveform inspection with deterministic machine-friendly output.
Documentation
pub mod change;
pub mod docs;
mod expr_runtime;
pub mod extract;
pub mod info;
pub mod property;
pub mod schema;
pub mod scope;
pub mod signal;
pub mod skill;
pub mod time;
pub mod value;
mod value_format;

use serde::Serialize;

use crate::cli;
use crate::diagnostic::Diagnostic;
use crate::error::WavepeekError;
use crate::output::{self, JsonlWriter};
use crate::output_mode::OutputMode;

#[derive(Debug)]
pub enum Command {
    Schema(cli::schema::SchemaArgs),
    Info(cli::info::InfoArgs),
    Scope(cli::scope::ScopeArgs),
    Signal(cli::signal::SignalArgs),
    Value(cli::value::ValueArgs),
    Change(cli::change::ChangeArgs),
    Property(cli::property::PropertyArgs),
    ExtractGeneric(cli::extract::GenericArgs),
    Docs(cli::docs::DocsArgs),
    Skill(cli::skill::SkillArgs),
}

#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CommandName {
    Schema,
    Info,
    Scope,
    Signal,
    Value,
    Change,
    Property,
    ExtractGeneric,
    Docs,
    DocsTopics,
    DocsShow,
    DocsSearch,
    DocsExport,
    Skill,
}

impl Command {
    pub const fn name(&self) -> CommandName {
        match self {
            Self::Schema(_) => CommandName::Schema,
            Self::Info(_) => CommandName::Info,
            Self::Scope(_) => CommandName::Scope,
            Self::Signal(_) => CommandName::Signal,
            Self::Value(_) => CommandName::Value,
            Self::Change(_) => CommandName::Change,
            Self::Property(_) => CommandName::Property,
            Self::ExtractGeneric(_) => CommandName::ExtractGeneric,
            Self::Docs(_) => CommandName::Docs,
            Self::Skill(_) => CommandName::Skill,
        }
    }

    pub const fn output_mode(&self) -> OutputMode {
        match self {
            Self::Schema(_) => OutputMode::Human,
            Self::Info(args) => OutputMode::from_json_flags(args.json, args.jsonl),
            Self::Scope(args) => OutputMode::from_json_flags(args.json, args.jsonl),
            Self::Signal(args) => OutputMode::from_json_flags(args.json, args.jsonl),
            Self::Value(args) => OutputMode::from_json_flags(args.json, args.jsonl),
            Self::Change(args) => OutputMode::from_json_flags(args.json, args.jsonl),
            Self::Property(args) => OutputMode::from_json_flags(args.json, args.jsonl),
            Self::ExtractGeneric(args) => OutputMode::from_json_flags(args.json, args.jsonl),
            Self::Docs(_) | Self::Skill(_) => OutputMode::Human,
        }
    }
}

impl CommandName {
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Schema => "schema",
            Self::Info => "info",
            Self::Scope => "scope",
            Self::Signal => "signal",
            Self::Value => "value",
            Self::Change => "change",
            Self::Property => "property",
            Self::ExtractGeneric => "extract generic",
            Self::Docs => "docs",
            Self::DocsTopics => "docs topics",
            Self::DocsShow => "docs show",
            Self::DocsSearch => "docs search",
            Self::DocsExport => "docs export",
            Self::Skill => "skill",
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct DocsTopicsData {
    pub topics: Vec<crate::docs::TopicSummary>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct DocsSearchMatchData {
    pub topic: crate::docs::TopicSummary,
    pub match_kind: crate::docs::MatchKind,
    pub matched_tokens: usize,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct DocsSearchData {
    pub query: String,
    pub matches: Vec<DocsSearchMatchData>,
}

#[derive(Debug, Clone, Copy, Default)]
pub struct HumanRenderOptions {
    pub scope_tree: bool,
    pub signals_abs: bool,
}

#[allow(dead_code)]
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum CommandData {
    Schema(String),
    Text(String),
    Info(info::InfoData),
    Scope(Vec<scope::ScopeEntry>),
    Signal(Vec<signal::SignalEntry>),
    Value(value::ValueData),
    Change(Vec<change::ChangeSnapshot>),
    Property(Vec<property::PropertyCaptureRow>),
    ExtractGeneric(extract::ExtractGenericData),
    DocsTopics(DocsTopicsData),
    DocsSearch(DocsSearchData),
}

#[derive(Debug, Serialize)]
pub struct CommandResult {
    #[serde(skip)]
    pub command: CommandName,
    #[serde(skip)]
    pub output_mode: OutputMode,
    #[serde(skip)]
    pub human_options: HumanRenderOptions,
    pub data: CommandData,
    pub diagnostics: Vec<Diagnostic>,
}

pub fn run(command: Command) -> Result<CommandResult, WavepeekError> {
    match command {
        Command::Schema(args) => schema::run(args),
        Command::Info(args) => info::run(args),
        Command::Scope(args) => scope::run(args),
        Command::Signal(args) => signal::run(args),
        Command::Value(args) => value::run(args),
        Command::Change(args) => change::run(args),
        Command::Property(args) => property::run(args),
        Command::ExtractGeneric(args) => extract::run(args),
        Command::Docs(args) => docs::run(args),
        Command::Skill(args) => skill::run(args),
    }
}

pub fn run_jsonl<W: std::io::Write>(
    command: Command,
    writer: &mut JsonlWriter<W>,
) -> Result<(), WavepeekError> {
    match command {
        Command::Change(args) => change::run_jsonl(args, writer),
        Command::Property(args) => property::run_jsonl(args, writer),
        Command::ExtractGeneric(args) => extract::run_jsonl(args, writer),
        Command::Info(_) | Command::Scope(_) | Command::Signal(_) | Command::Value(_) => {
            let result = run(command)?;
            output::write_jsonl_result(result, writer)
        }
        Command::Schema(_) | Command::Docs(_) | Command::Skill(_) => Err(WavepeekError::Args(
            "--jsonl is available only for waveform commands".to_string(),
        )),
    }
}

#[cfg(test)]
mod tests {
    use super::CommandName;

    #[test]
    fn command_name_strings_exercise_all_variants() {
        assert_eq!(CommandName::Schema.as_str(), "schema");
        assert_eq!(CommandName::Info.as_str(), "info");
        assert_eq!(CommandName::Scope.as_str(), "scope");
        assert_eq!(CommandName::Signal.as_str(), "signal");
        assert_eq!(CommandName::Value.as_str(), "value");
        assert_eq!(CommandName::Change.as_str(), "change");
        assert_eq!(CommandName::Property.as_str(), "property");
        assert_eq!(CommandName::ExtractGeneric.as_str(), "extract generic");
        assert_eq!(CommandName::Docs.as_str(), "docs");
        assert_eq!(CommandName::DocsTopics.as_str(), "docs topics");
        assert_eq!(CommandName::DocsShow.as_str(), "docs show");
        assert_eq!(CommandName::DocsSearch.as_str(), "docs search");
        assert_eq!(CommandName::DocsExport.as_str(), "docs export");
        assert_eq!(CommandName::Skill.as_str(), "skill");
    }
}