///|
/// Parse once and prepare the high-level data used by inspectors and CI tools.
pub fn inspect_step(source : String) -> Inspection raise ParseError {
  let model = parse(source)
  {
    model,
    summary: model.summary(),
    metrics: analyze_model(source, model),
    validation: validate(model),
    capabilities: capability_report(model),
  }
}

///|
pub fn Inspection::is_valid(self : Inspection) -> Bool {
  self.validation.is_valid()
}

///|
pub fn Inspection::scene(self : Inspection) -> BimScene {
  build_scene(self.model)
}

///|
pub fn Inspection::headline(self : Inspection) -> String {
  self.metrics.headline() + "; " + validation_summary(self.validation)
}

///|
pub fn Inspection::to_json(self : Inspection) -> String {
  let scene = self.scene()
  "{\"summary\":{\"schema\":" +
  json_string(self.summary.schema) +
  ",\"entities\":" +
  self.summary.entity_count.to_string() +
  ",\"products\":" +
  self.summary.product_count.to_string() +
  ",\"properties\":" +
  self.summary.property_set_count.to_string() +
  ",\"geometry\":" +
  self.summary.geometry_count.to_string() +
  "},\"valid\":" +
  self.is_valid().to_string() +
  ",\"scene\":" +
  scene.to_json() +
  "}"
}

///|
pub fn product_json_by_id(model : Model, id : Int) -> String? {
  match product_by_id(model.products(), id) {
    Some(product) => Some(product_to_json(product))
    None => None
  }
}

///|
pub fn products_json_named(model : Model, fragment : String) -> String {
  let products = products_named(model.index(), fragment)
  "[" + join_json(products.map(product_to_json)) + "]"
}