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>;