mirror of
https://github.com/stefanoamorelli/crabrl.git
synced 2026-04-18 07:10:42 +00:00
style: apply rustfmt to entire codebase
- Fix all formatting issues for CI compliance - Consistent code style across all files - Proper struct/enum formatting - Fixed import ordering
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use crabrl::Parser;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
fn parse_small_file(c: &mut Criterion) {
|
||||
let parser = Parser::new();
|
||||
@@ -21,4 +21,3 @@ fn parse_medium_file(c: &mut Criterion) {
|
||||
|
||||
criterion_group!(benches, parse_small_file, parse_medium_file);
|
||||
criterion_main!(benches);
|
||||
|
||||
|
||||
@@ -34,4 +34,3 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,4 +20,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,13 @@ fn main() {
|
||||
Ok(doc) => {
|
||||
let elapsed = start.elapsed();
|
||||
let ms = elapsed.as_secs_f64() * 1000.0;
|
||||
println!("crabrl found: {} facts, {} contexts, {} units (in {:.3}ms)",
|
||||
println!(
|
||||
"crabrl found: {} facts, {} contexts, {} units (in {:.3}ms)",
|
||||
doc.facts.len(),
|
||||
doc.contexts.len(),
|
||||
doc.units.len(),
|
||||
ms);
|
||||
ms
|
||||
);
|
||||
|
||||
// Additional stats
|
||||
println!("Facts: {}", doc.facts.len());
|
||||
@@ -37,5 +39,3 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ pub mod validator;
|
||||
pub use simple_parser::Parser;
|
||||
|
||||
// Re-export main types
|
||||
pub use model::{Document, Fact, Context, Unit};
|
||||
pub use model::{Context, Document, Fact, Unit};
|
||||
|
||||
// Create validator wrapper for the CLI
|
||||
pub struct Validator {
|
||||
|
||||
44
src/main.rs
44
src/main.rs
@@ -6,7 +6,7 @@ use colored::*;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
|
||||
use crabrl::{Parser, Validator, ValidationConfig};
|
||||
use crabrl::{Parser, ValidationConfig, Validator};
|
||||
|
||||
/// High-performance XBRL parser and validator
|
||||
#[derive(ClapParser)]
|
||||
@@ -62,10 +62,15 @@ fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
match cli.command {
|
||||
Commands::Parse { input, json: _, stats } => {
|
||||
Commands::Parse {
|
||||
input,
|
||||
json: _,
|
||||
stats,
|
||||
} => {
|
||||
let start = Instant::now();
|
||||
let parser = Parser::new();
|
||||
let doc = parser.parse_file(&input)
|
||||
let doc = parser
|
||||
.parse_file(&input)
|
||||
.with_context(|| format!("Failed to parse {}", input.display()))?;
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
@@ -76,14 +81,21 @@ fn main() -> Result<()> {
|
||||
|
||||
if stats {
|
||||
println!(" Time: {:.2}ms", elapsed.as_secs_f64() * 1000.0);
|
||||
println!(" Throughput: {:.0} facts/sec",
|
||||
doc.facts.len() as f64 / elapsed.as_secs_f64());
|
||||
println!(
|
||||
" Throughput: {:.0} facts/sec",
|
||||
doc.facts.len() as f64 / elapsed.as_secs_f64()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Commands::Validate { input, profile, strict } => {
|
||||
Commands::Validate {
|
||||
input,
|
||||
profile,
|
||||
strict,
|
||||
} => {
|
||||
let parser = Parser::new();
|
||||
let doc = parser.parse_file(&input)
|
||||
let doc = parser
|
||||
.parse_file(&input)
|
||||
.with_context(|| format!("Failed to parse {}", input.display()))?;
|
||||
|
||||
let config = match profile.as_str() {
|
||||
@@ -95,9 +107,17 @@ fn main() -> Result<()> {
|
||||
let result = validator.validate(&doc)?;
|
||||
|
||||
if result.is_valid {
|
||||
println!("{} {} - Document is valid", "✓".green().bold(), input.display());
|
||||
println!(
|
||||
"{} {} - Document is valid",
|
||||
"✓".green().bold(),
|
||||
input.display()
|
||||
);
|
||||
} else {
|
||||
println!("{} {} - Validation failed", "✗".red().bold(), input.display());
|
||||
println!(
|
||||
"{} {} - Validation failed",
|
||||
"✗".red().bold(),
|
||||
input.display()
|
||||
);
|
||||
println!(" Errors: {}", result.errors.len());
|
||||
println!(" Warnings: {}", result.warnings.len());
|
||||
|
||||
@@ -150,8 +170,10 @@ fn main() -> Result<()> {
|
||||
println!(" Median: {:.3}ms", median.as_secs_f64() * 1000.0);
|
||||
println!(" Mean: {:.3}ms", mean.as_secs_f64() * 1000.0);
|
||||
println!(" Max: {:.3}ms", max.as_secs_f64() * 1000.0);
|
||||
println!(" Throughput: {:.0} facts/sec",
|
||||
doc_facts as f64 / mean.as_secs_f64());
|
||||
println!(
|
||||
" Throughput: {:.0} facts/sec",
|
||||
doc_facts as f64 / mean.as_secs_f64()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
src/model.rs
13
src/model.rs
@@ -5,7 +5,6 @@ use std::collections::HashMap;
|
||||
// Core XBRL Data Structures - Full Specification Support
|
||||
// ============================================================================
|
||||
|
||||
|
||||
#[repr(C, align(64))]
|
||||
#[derive(Clone)]
|
||||
pub struct FactStorage {
|
||||
@@ -111,8 +110,13 @@ pub struct Scenario {
|
||||
// Period with forever support
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Period {
|
||||
Instant { date: CompactString },
|
||||
Duration { start: CompactString, end: CompactString },
|
||||
Instant {
|
||||
date: CompactString,
|
||||
},
|
||||
Duration {
|
||||
start: CompactString,
|
||||
end: CompactString,
|
||||
},
|
||||
Forever,
|
||||
}
|
||||
|
||||
@@ -347,6 +351,3 @@ impl Document {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -26,17 +26,16 @@ impl Parser {
|
||||
let text = String::from_utf8_lossy(data);
|
||||
|
||||
// Count facts (very simplified)
|
||||
let fact_count = text.matches("<us-gaap:").count() +
|
||||
text.matches("<dei:").count() +
|
||||
text.matches("<ifrs:").count();
|
||||
let fact_count = text.matches("<us-gaap:").count()
|
||||
+ text.matches("<dei:").count()
|
||||
+ text.matches("<ifrs:").count();
|
||||
|
||||
// Count contexts
|
||||
let context_count = text.matches("<context ").count() +
|
||||
text.matches("<xbrli:context").count();
|
||||
let context_count =
|
||||
text.matches("<context ").count() + text.matches("<xbrli:context").count();
|
||||
|
||||
// Count units
|
||||
let unit_count = text.matches("<unit ").count() +
|
||||
text.matches("<xbrli:unit").count();
|
||||
let unit_count = text.matches("<unit ").count() + text.matches("<xbrli:unit").count();
|
||||
|
||||
// Create dummy document with approximate counts
|
||||
let mut doc = Document {
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
// Comprehensive XBRL validation
|
||||
use crate::{model::*, Result, Error};
|
||||
use crate::{model::*, Error, Result};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ValidationError {
|
||||
InvalidContextRef { fact_index: usize, context_id: u16 },
|
||||
InvalidUnitRef { fact_index: usize, unit_id: u16 },
|
||||
CalculationInconsistency { concept: String, expected: f64, actual: f64 },
|
||||
InvalidDataType { concept: String, expected_type: String, actual_value: String },
|
||||
MissingRequiredElement { element: String },
|
||||
DuplicateId { id: String },
|
||||
InvalidContextRef {
|
||||
fact_index: usize,
|
||||
context_id: u16,
|
||||
},
|
||||
InvalidUnitRef {
|
||||
fact_index: usize,
|
||||
unit_id: u16,
|
||||
},
|
||||
CalculationInconsistency {
|
||||
concept: String,
|
||||
expected: f64,
|
||||
actual: f64,
|
||||
},
|
||||
InvalidDataType {
|
||||
concept: String,
|
||||
expected_type: String,
|
||||
actual_value: String,
|
||||
},
|
||||
MissingRequiredElement {
|
||||
element: String,
|
||||
},
|
||||
DuplicateId {
|
||||
id: String,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct XbrlValidator {
|
||||
@@ -137,7 +155,10 @@ impl XbrlValidator {
|
||||
});
|
||||
}
|
||||
}
|
||||
UnitType::Divide { numerator, denominator } => {
|
||||
UnitType::Divide {
|
||||
numerator,
|
||||
denominator,
|
||||
} => {
|
||||
if numerator.is_empty() || denominator.is_empty() {
|
||||
errors.push(ValidationError::MissingRequiredElement {
|
||||
element: format!("Numerator/denominator for unit {}", unit.id),
|
||||
@@ -229,7 +250,7 @@ impl ValidationContext {
|
||||
|
||||
pub fn add_rule<F>(&mut self, rule: F)
|
||||
where
|
||||
F: Fn(&Document) -> Vec<ValidationError> + 'static
|
||||
F: Fn(&Document) -> Vec<ValidationError> + 'static,
|
||||
{
|
||||
self.custom_rules.push(Box::new(rule));
|
||||
}
|
||||
@@ -268,8 +289,10 @@ pub fn sec_validation_rules(doc: &Document) -> Vec<ValidationError> {
|
||||
|
||||
for ctx in &doc.contexts {
|
||||
// Check for current period context
|
||||
if ctx.id.contains("CurrentYear") || ctx.id.contains("CurrentPeriod") ||
|
||||
ctx.id.contains("DocumentPeriodEndDate") {
|
||||
if ctx.id.contains("CurrentYear")
|
||||
|| ctx.id.contains("CurrentPeriod")
|
||||
|| ctx.id.contains("DocumentPeriodEndDate")
|
||||
{
|
||||
has_current_period = true;
|
||||
}
|
||||
|
||||
@@ -291,8 +314,10 @@ pub fn sec_validation_rules(doc: &Document) -> Vec<ValidationError> {
|
||||
for i in 0..doc.facts.concept_ids.len() {
|
||||
if i < doc.concept_names.len() {
|
||||
let concept = &doc.concept_names[i];
|
||||
if concept.contains("dei:") || concept.contains("DocumentType") ||
|
||||
concept.contains("EntityRegistrantName") {
|
||||
if concept.contains("dei:")
|
||||
|| concept.contains("DocumentType")
|
||||
|| concept.contains("EntityRegistrantName")
|
||||
{
|
||||
has_dei_elements = true;
|
||||
}
|
||||
}
|
||||
@@ -390,8 +415,10 @@ pub fn ifrs_validation_rules(doc: &Document) -> Vec<ValidationError> {
|
||||
Period::Duration { start, end: _ } => {
|
||||
has_reporting_period = true;
|
||||
// IFRS requires comparative information
|
||||
if start.contains("PY") || ctx.id.contains("PriorYear") ||
|
||||
ctx.id.contains("Comparative") {
|
||||
if start.contains("PY")
|
||||
|| ctx.id.contains("PriorYear")
|
||||
|| ctx.id.contains("Comparative")
|
||||
{
|
||||
has_comparative_period = true;
|
||||
}
|
||||
}
|
||||
@@ -436,7 +463,8 @@ pub fn ifrs_validation_rules(doc: &Document) -> Vec<ValidationError> {
|
||||
for member in &segment.explicit_members {
|
||||
// IFRS dimensions should follow specific patterns
|
||||
if !member.dimension.contains(":") {
|
||||
dimension_validations.push(format!("Invalid dimension format: {}", member.dimension));
|
||||
dimension_validations
|
||||
.push(format!("Invalid dimension format: {}", member.dimension));
|
||||
}
|
||||
if member.dimension.contains("ifrs") || member.dimension.contains("ifrs-full") {
|
||||
// Valid IFRS dimension
|
||||
@@ -486,13 +514,19 @@ pub fn ifrs_validation_rules(doc: &Document) -> Vec<ValidationError> {
|
||||
let concept = &doc.concept_names[i];
|
||||
let lower = concept.to_lowercase();
|
||||
|
||||
if lower.contains("financialposition") || lower.contains("balancesheet") ||
|
||||
lower.contains("assets") || lower.contains("liabilities") {
|
||||
if lower.contains("financialposition")
|
||||
|| lower.contains("balancesheet")
|
||||
|| lower.contains("assets")
|
||||
|| lower.contains("liabilities")
|
||||
{
|
||||
has_financial_position = true;
|
||||
}
|
||||
|
||||
if lower.contains("comprehensiveincome") || lower.contains("profitorloss") ||
|
||||
lower.contains("income") || lower.contains("revenue") {
|
||||
if lower.contains("comprehensiveincome")
|
||||
|| lower.contains("profitorloss")
|
||||
|| lower.contains("income")
|
||||
|| lower.contains("revenue")
|
||||
{
|
||||
has_comprehensive_income = true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user