Skip to content

Latest commit

 

History

History
320 lines (231 loc) · 9.31 KB

File metadata and controls

320 lines (231 loc) · 9.31 KB

Lens Module

The lens module is Monocle's use‑case layer. Each lens exposes a cohesive set of operations (search, parse, RPKI lookup, etc.) through a stable, programmatic API that can be reused by:

  • the monocle CLI,
  • the WebSocket server (monocle server),
  • GUI frontends (planned: GPUI),
  • other Rust applications embedding Monocle as a library.

A lens is responsible for domain logic, input normalization/validation, and returning typed results. Formatting is handled consistently via the unified OutputFormat.


Directory Layout

lens/
├── mod.rs              # Lens module exports (feature-gated)
├── README.md           # This document
├── utils.rs            # OutputFormat + formatting helpers
│
├── time/               # Time parsing / formatting
│   └── mod.rs
│
├── country/            # Country lookup (lib)
├── ip/                 # IP information lookups
│   └── mod.rs
├── parse/              # MRT parsing + progress callbacks
│   └── mod.rs
├── search/             # Search across public MRT files
│   ├── mod.rs
│   └── query_builder.rs
├── rpki/               # RPKI operations
│   ├── mod.rs
│   └── commons.rs
├── pfx2as/             # Prefix→AS mapping types
│   └── mod.rs
├── as2rel/             # AS relationship lookups
│   ├── mod.rs
│   ├── args.rs
│   └── types.rs
│
└── inspect/            # Unified AS/prefix inspection
    ├── mod.rs          # InspectLens implementation
    └── types.rs        # Result types, section selection

Feature Requirements

All lenses are available with the lib feature, which is the default for library usage:

# Library usage (all lenses + database)
monocle = { version = "1.1", default-features = false, features = ["lib"] }

The lib feature includes:

  • TimeLens - Time parsing and formatting
  • CountryLens - Country code/name lookup
  • IpLens - IP information lookup
  • ParseLens - MRT file parsing
  • SearchLens - BGP message search across MRT files
  • RpkiLens - RPKI validation
  • Pfx2asLens - Prefix-to-AS mapping
  • As2relLens - AS relationship lookups
  • InspectLens - Unified AS/prefix inspection

Design Philosophy

1) Interface-neutral, frontend-friendly

Lenses are designed to be called from multiple frontends:

  • CLI: commands call into lenses and print results using OutputFormat.
  • WebSocket: handlers call lenses and stream progress/results via WsOpSink.
  • GUI: a UI can call lenses on a worker thread and stream progress/results back to the UI.
  • Library: users can call lens APIs directly without going through CLI argument parsing.

2) Clear responsibilities

A typical operation should look like:

  • CLI / GUI: parse user input → construct Args → call lens method
  • Lens: validate and execute → return typed results (and optionally progress events)
  • Formatting: done via OutputFormat (shared across the project)

The lens layer should not own long-term persistence primitives directly. When persistence is needed, lenses depend on database/ (for SQLite + caches) or external crates (Broker/Parser/etc.).

3) Stable, typed APIs

Prefer:

  • typed argument structs (often Serialize/Deserialize)
  • typed result structs/enums (often Serialize/Deserialize)
  • explicit error returns (typically anyhow::Result<_>)

This makes it straightforward to:

  • serialize requests/results for GUI message passing,
  • test units without a CLI,
  • keep output formatting consistent.

Output Formatting: Unified OutputFormat

Monocle uses a single output format enum across commands and lenses:

  • table (default)
  • markdown / md
  • json
  • json-pretty
  • json-line / jsonl / ndjson
  • psv

The OutputFormat type lives in:

  • lens/utils.rs

A common pattern is:

  • lens methods return Vec<T> (or similar)
  • the CLI chooses an OutputFormat
  • formatting is done by calling format.format(&results) (or equivalent helper)

This keeps formatting logic consistent and avoids per-command format flags drifting over time.


Progress Reporting (GUI-friendly)

Some lenses can emit progress updates via callbacks (used by CLI progress bars today and intended for GUIs):

  • Parse: emits periodic progress while processing messages.
  • Search: emits progress for broker querying, file processing, and completion.

Progress types are designed to be:

  • Send + Sync friendly (callbacks may be called from parallel workers),
  • serializable (Serialize/Deserialize) for easy GUI integration.

Lens Categories

Database-backed lenses

These lenses require access to the persistent SQLite database (typically via MonocleDatabase):

  • As2relLens - AS-level relationships
  • InspectLens - Unified AS/prefix inspection (uses ASInfo, AS2Rel, RPKI, Pfx2as repositories)

Database-optional lenses

These lenses can use the database for caching but don't strictly require it:

  • RpkiLens - Uses database for current data cache; historical queries use bgpkit-commons directly

Standalone lenses

These lenses do not require a persistent database reference:

  • TimeLens - Time parsing and formatting
  • CountryLens - Country code/name lookup (uses bgpkit-commons)
  • IpLens - IP information lookup (uses external API)
  • ParseLens - MRT file parsing
  • SearchLens - BGP message search across MRT files

Usage Examples

Note: code below is intentionally example-focused; check the module docs / rustdoc for exact function signatures where needed.

TimeLens

use monocle::lens::time::{TimeLens, TimeParseArgs};
use monocle::utils::OutputFormat;

let lens = TimeLens::new();
let args = TimeParseArgs::new(vec![
    "1697043600".to_string(),
    "2023-10-11T00:00:00Z".to_string(),
]);

let results = lens.parse(&args)?;
let out = OutputFormat::Table.format(&results);
println!("{}", out);

InspectLens (database-backed)

use monocle::database::MonocleDatabase;
use monocle::lens::inspect::{InspectLens, InspectQueryOptions};
use monocle::utils::OutputFormat;

let db = MonocleDatabase::open_in_dir("~/.local/share/monocle")?;
let lens = InspectLens::new(&db);

// Query AS information
let options = InspectQueryOptions::default();
let result = lens.query_asn(13335, &options)?;
println!("AS{}: {}", result.asn, result.name.unwrap_or_default());

// Query prefix information
let result = lens.query_prefix("1.1.1.0/24".parse()?, &options)?;
println!("{:?}", result);

// Search by name
let results = lens.search_by_name("cloudflare", 20)?;
for r in results {
    println!("AS{}: {}", r.asn, r.name.unwrap_or_default());
}

As2relLens (database-backed)

use monocle::database::MonocleDatabase;
use monocle::lens::as2rel::{As2relLens, As2relSearchArgs};
use monocle::utils::OutputFormat;

let db = MonocleDatabase::open_in_dir("~/.local/share/monocle")?;
let lens = As2relLens::new(&db);

// Update data if needed
if lens.needs_update() {
    lens.update()?;
}

// Query relationships for an ASN
let args = As2relSearchArgs::new(13335);
let results = lens.search(&args)?;
println!("{}", OutputFormat::Table.format(&results));

SearchLens with progress

use monocle::lens::search::{SearchLens, SearchFilters, SearchProgress};
use std::sync::Arc;

let lens = SearchLens::new();
let filters = SearchFilters {
    // ...
    ..Default::default()
};

let on_progress = Arc::new(|p: SearchProgress| {
    // send to UI / update progress bar
    eprintln!("{:?}", p);
});

let on_elem = Arc::new(|elem, collector| {
    // stream results somewhere
});

let summary = lens.search_with_progress(&filters, Some(on_progress), on_elem)?;
eprintln!("{:?}", summary);

Adding a New Lens (high level)

For a detailed contributor walkthrough, see DEVELOPMENT.md. In short:

  1. Create src/lens/<newlens>/mod.rs (and args.rs / types.rs if needed)
  2. Define:
    • <NewLens>Args (input)
    • <NewLens>Result (output)
    • <NewLens>Lens (operations)
  3. Add feature gate in src/lens/mod.rs:
    #[cfg(feature = "lib")]
    pub mod newlens;
  4. Wire into (optional):
    • CLI command module under src/bin/commands/
    • WebSocket handler under src/server/handlers/

Naming Conventions

Consistent naming makes lenses predictable:

  • Lens struct: <Name>Lens (e.g., TimeLens, RpkiLens, InspectLens)
  • Arg structs: <Name><Op>Args (e.g., As2relSearchArgs, RpkiRoaLookupArgs)
  • Result structs: <Name><Op>Result (e.g., As2relSearchResult)
  • Module names: snake_case (as2rel, pfx2as, inspect)

Error Handling

  • Lens methods generally return anyhow::Result<T>.
  • Favor descriptive messages that help CLI and GUI users.
  • Avoid panics; the library should be robust when called from long-running frontends.

Testing Notes

  • Prefer unit tests close to lens code for pure logic.
  • For filesystem/network interactions, use #[ignore] tests or inject test data.
  • Search/parse workloads should be tested with small fixtures where possible.

Related Documentation

  • ARCHITECTURE.md (project-level architecture)
  • DEVELOPMENT.md (contributor guide)
  • src/server/README.md (WebSocket API protocol)
  • src/database/README.md (database module overview)
  • examples/README.md (example code by feature tier)