Skip to main content

ripbi_core/
lib.rs

1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3#![deny(rustdoc::broken_intra_doc_links)]
4
5//! Core library for ripbi: Power BI ingestion, DAX lexing, and dependency-graph analysis.
6//!
7//! This crate never prints or exits; all fallible operations return [`Result`].
8//!
9//! Analysis starts from a [`TabularDatabase`] — the format-agnostic semantic model
10//! every source format normalizes into — paired with a [`ModelIndex`] for resolving
11//! the names that DAX and report bindings refer to objects by:
12//!
13//! ```
14//! use ripbi_core::{Measure, ModelIndex, Table, TabularDatabase};
15//!
16//! let db = TabularDatabase {
17//!     tables: vec![Table {
18//!         name: "Sales".to_string(),
19//!         measures: vec![Measure {
20//!             name: "Total".to_string(),
21//!             expression: "SUM(Sales[Amount])".to_string(),
22//!             ..Default::default()
23//!         }],
24//!         ..Default::default()
25//!     }],
26//!     ..Default::default()
27//! };
28//!
29//! // Names compare case-insensitively, as the Analysis Services engine does.
30//! let index = ModelIndex::build(&db);
31//! assert!(index.resolve_table("SALES").is_some());
32//!
33//! // Expressions are enumerated with their owner, ready for the DAX lexer.
34//! let expressions = db.dax_expressions();
35//! assert_eq!(expressions.len(), 1);
36//! assert_eq!(expressions[0].text, "SUM(Sales[Amount])");
37//! ```
38//!
39//! The report side is one [`ReportModel`] per report sharing the model: its
40//! [`bindings`](ReportModel::bindings) are the reachability roots, and its
41//! [`dax_expressions`](ReportModel::dax_expressions) add report-level measures on
42//! top of the model's own expressions.
43//!
44//! Source formats are ingested through [`ingest`]: `semantic_model` parses a TMDL
45//! `.SemanticModel` folder into a [`TabularDatabase`], and `report` parses a PBIR
46//! `.Report` folder into a [`ReportModel`]. Every ingestion entry point returns
47//! [`Ingested`], pairing the parsed value with the [`SkipNotice`]s it recorded —
48//! warnings as data, so the CLI decides how to surface them.
49
50pub mod dax;
51pub mod graph;
52pub mod identity;
53pub mod ingest;
54pub mod m;
55pub mod model;
56pub mod report;
57
58pub use dax::{
59    Binding, RawRef, Token, TokenKind, bind, quoted_names, references, tokenize, unescape_name,
60};
61pub use graph::{
62    AutoDateTimeStatus, AutoDateTimeVerdict, BindingEdge, BindingSite, BrokenBinding, BrokenReason,
63    DependencyGraph, Provenance, StructuralEdge, UnusedObject, UsedBy,
64};
65pub use identity::{FieldRef, NameKey, ObjectId};
66pub use ingest::{Ingested, SkipKind, SkipNotice};
67pub use model::index::{
68    ColumnHandle, ExpressionHandle, FunctionHandle, HierarchyHandle, MeasureHandle, ModelIndex,
69    Resolved, TableHandle, UnqualifiedMatches,
70};
71pub use model::{
72    CalculationGroup, CalculationItem, Calendar, Column, ColumnKind, DaxExpressionKind,
73    DaxExpressionRef, ExpressionOwner, Function, Hierarchy, HierarchyLevel, HierarchyRef, Kpi,
74    MExpressionRef, Measure, Partition, PartitionSource, Relationship, Role, SharedExpression,
75    Table, TablePermission, TabularDatabase, Variation,
76};
77pub use report::{
78    BindingKind, BindingRef, Bookmark, BookmarkSection, BookmarkVisual, DatasetReference,
79    DrillthroughParameter, FieldTarget, FieldWell, Filter, Page, PageBinding, PageBindingKind,
80    Projection, ReportMeasure, ReportModel, Visual,
81};
82
83use thiserror::Error;
84
85/// Errors produced by ripbi-core.
86#[derive(Debug, Error)]
87pub enum Error {
88    /// A file or directory could not be read.
89    #[error("I/O error: {0}")]
90    Io(#[from] std::io::Error),
91    /// A `.pbix`/`.pbit` archive could not be opened or is malformed.
92    #[error("invalid archive: {0}")]
93    Archive(#[from] zip::result::ZipError),
94    /// A model or report JSON document could not be parsed.
95    #[error("invalid JSON: {0}")]
96    Json(#[from] serde_json::Error),
97    /// A TMDL document is structurally malformed and could not be parsed.
98    #[error("malformed TMDL: {0}")]
99    Tmdl(String),
100    /// The path is not a Power BI source this crate recognizes.
101    #[error("unsupported or unrecognized source format: {0}")]
102    UnsupportedFormat(String),
103}
104
105/// A result whose error is this crate's [`enum@Error`].
106pub type Result<T> = std::result::Result<T, Error>;