///|
/// One independently scored health dimension in a brand book review.
pub struct QualityDimension {
name : String
score : Int
summary : String
} derive(Debug, Eq)
///|
pub fn QualityDimension::new(
name : String,
score : Int,
summary : String,
) -> QualityDimension {
{ name, score: clamp_score(score), summary }
}
///|
pub fn QualityDimension::passed(
self : QualityDimension,
threshold : Int,
) -> Bool {
self.score >= threshold
}
///|
pub fn QualityDimension::label(self : QualityDimension) -> String {
self.name + " (" + self.score.to_string() + "/100)"
}
///|
pub fn QualityDimension::to_json(self : QualityDimension) -> String {
"{\"name\":\"" +
json_escape(self.name) +
"\",\"score\":" +
self.score.to_string() +
",\"summary\":\"" +
json_escape(self.summary) +
"\"}"
}
///|
pub struct BrandQualityReport {
score : Int
dimensions : Array[QualityDimension]
audit : AuditReport
} derive(Debug, Eq)
///|
pub fn BrandQualityReport::from_brand_book(
book : BrandBook,
) -> BrandQualityReport {
let validation = book.validate_with_policy(ValidationPolicy::strict())
let accessibility = book.accessibility_score()
let asset = book.asset_health()
let component = book.component_score()
let typography = typography_dimension_score(book.typography)
let documentation = documentation_score(book)
let export_score = export_dimension_score(book)
let dimensions : Array[QualityDimension] = [
QualityDimension::new(
"validation",
score_from_report(validation),
validation.summary(),
),
QualityDimension::new(
"accessibility", accessibility, "contrast pairs and readable text guidance",
),
QualityDimension::new(
"assets", asset, "logo safety and asset policy checks",
),
QualityDimension::new(
"components", component, "component contract completeness",
),
QualityDimension::new(
"typography", typography, "scale rhythm and font fallbacks",
),
QualityDimension::new(
"documentation", documentation, "metadata, voice, misuse guidance, and examples",
),
QualityDimension::new(
"exports", export_score, "stable machine-readable output formats",
),
]
let total = dimensions.fold(init=0, fn(sum, dimension) {
sum + dimension.score
})
{
score: if dimensions.length() == 0 {
0
} else {
total / dimensions.length()
},
dimensions,
audit: validation,
}
}
///|
pub fn BrandQualityReport::passed(
self : BrandQualityReport,
threshold : Int,
) -> Bool {
self.score >= threshold && !self.audit.has_errors()
}
///|
pub fn BrandQualityReport::dimension(
self : BrandQualityReport,
name : String,
) -> QualityDimension? {
let target = normalize_identifier(name)
for dimension in self.dimensions {
if normalize_identifier(dimension.name) == target {
return Some(dimension)
}
}
None
}
///|
pub fn BrandQualityReport::weakest_dimension(
self : BrandQualityReport,
) -> QualityDimension? {
if self.dimensions.length() == 0 {
None
} else {
let mut weakest = self.dimensions[0]
for dimension in self.dimensions {
if dimension.score < weakest.score {
weakest = dimension
}
}
Some(weakest)
}
}
///|
pub fn BrandQualityReport::strongest_dimension(
self : BrandQualityReport,
) -> QualityDimension? {
if self.dimensions.length() == 0 {
None
} else {
let mut strongest = self.dimensions[0]
for dimension in self.dimensions {
if dimension.score > strongest.score {
strongest = dimension
}
}
Some(strongest)
}
}
///|
pub fn BrandQualityReport::dimension_names(
self : BrandQualityReport,
) -> Array[String] {
self.dimensions.map(fn(dimension) { dimension.name })
}
///|
pub fn BrandQualityReport::to_json(self : BrandQualityReport) -> String {
"{\"score\":" +
self.score.to_string() +
",\"dimensions\":[" +
self.dimensions.map(fn(dimension) { dimension.to_json() }).join(",") +
"]}"
}
///|
pub fn BrandQualityReport::to_markdown(self : BrandQualityReport) -> String {
let lines : Array[String] = [
"# Quality report",
"",
"Overall score: **" + self.score.to_string() + "/100**",
"",
"| Dimension | Score | Assessment |",
"| --- | ---: | --- |",
]
for dimension in self.dimensions {
lines.push(
"| " +
dimension.name +
" | " +
dimension.score.to_string() +
" | " +
dimension.summary +
" |",
)
}
lines.push("")
lines.push("Validation: " + self.audit.summary())
lines.join("\n")
}
///|
pub fn BrandQualityReport::to_badges(
self : BrandQualityReport,
) -> Array[String] {
let result : Array[String] = []
for dimension in self.dimensions {
let level = if dimension.score >= 90 {
"excellent"
} else if dimension.score >= 70 {
"healthy"
} else if dimension.score >= 50 {
"review"
} else {
"critical"
}
result.push(dimension.name + ":" + level)
}
result
}
///|
pub fn BrandQualityReport::action_items(
self : BrandQualityReport,
) -> Array[String] {
let result : Array[String] = []
for dimension in self.dimensions {
if dimension.score < 80 {
result.push("Improve " + dimension.name + ": " + dimension.summary)
}
}
if self.audit.has_errors() {
result.push("Resolve validation errors before publishing")
}
result
}
///|
pub fn BrandQualityReport::score_label(self : BrandQualityReport) -> String {
if self.score >= 90 {
"excellent"
} else if self.score >= 75 {
"healthy"
} else if self.score >= 60 {
"needs review"
} else {
"not ready"
}
}
///|
pub fn BrandQualityReport::audit_markdown(self : BrandQualityReport) -> String {
self.audit.to_markdown()
}
///|
fn typography_dimension_score(typography : Typography) -> Int {
let report = typography.audit_rhythm(TypographyPolicy::strict())
if report.has_errors() {
50
} else if typography.accessible_step_count() == typography.scale.length() {
100
} else {
85
}
}
///|
fn documentation_score(book : BrandBook) -> Int {
let mut score = 0
if book.name.trim().to_owned() != "" {
score += 20
}
if book.tagline.trim().to_owned() != "" {
score += 15
}
if book.voice.tone.trim().to_owned() != "" {
score += 15
}
if book.voice.writing_principles.length() >= 2 {
score += 15
}
if book.forbidden.length() >= 2 {
score += 15
}
if book.metadata.source.trim().to_owned() != "" {
score += 10
}
if book.metadata.license.trim().to_owned() != "" {
score += 10
}
score
}
///|
fn export_dimension_score(book : BrandBook) -> Int {
let manifest = ExportManifest::for_brand_book(book)
if manifest.len() >= 6 && manifest.total_bytes() > 0 {
100
} else if manifest.len() >= 3 {
75
} else {
40
}
}
///|
fn score_from_report(report : AuditReport) -> Int {
let mut score = 100 - report.error_count() * 25 - report.warning_count() * 5
if score < 0 {
score = 0
}
score
}
///|
fn clamp_score(value : Int) -> Int {
if value < 0 {
0
} else if value > 100 {
100
} else {
value
}
}