///|
/// Distribution metrics for reviewing a theme before it is exported.
pub struct TokenMetrics {
total : Int
colors : Int
numbers : Int
texts : Int
booleans : Int
aliases : Int
documented : Int
empty_values : Int
} derive(Debug, Eq)
///|
pub fn ThemeDocument::metrics(self : ThemeDocument) -> TokenMetrics {
let counts = self.kind_counts()
let mut documented = 0
let mut empty_values = 0
for token in self.tokens {
if token.description.trim().to_owned() != "" {
documented += 1
}
if theme_value_is_empty(token.value) {
empty_values += 1
}
}
{
total: self.tokens.length(),
colors: counts[0],
numbers: counts[1],
texts: counts[2],
booleans: counts[3],
aliases: self.aliases.length(),
documented,
empty_values,
}
}
///|
pub fn TokenMetrics::documentation_rate(self : TokenMetrics) -> Int {
if self.total == 0 {
100
} else {
self.documented * 100 / self.total
}
}
///|
pub fn TokenMetrics::empty_rate(self : TokenMetrics) -> Int {
if self.total == 0 {
0
} else {
self.empty_values * 100 / self.total
}
}
///|
pub fn TokenMetrics::kind_count(self : TokenMetrics, kind : TokenKind) -> Int {
match kind {
Color => self.colors
Number => self.numbers
Text => self.texts
Boolean => self.booleans
}
}
///|
pub fn TokenMetrics::density_score(self : TokenMetrics) -> Int {
let mut score = 100
if self.empty_rate() > 0 {
score -= self.empty_rate()
}
if self.documentation_rate() < 80 {
score -= 20
}
if self.total == 0 {
score -= 50
}
if score < 0 {
0
} else {
score
}
}
///|
pub fn TokenMetrics::is_healthy(self : TokenMetrics) -> Bool {
self.total > 0 && self.empty_values == 0 && self.documentation_rate() >= 80
}
///|
pub fn TokenMetrics::to_markdown(self : TokenMetrics) -> String {
"| Metric | Value |\n| --- | ---: |\n" +
"| Total tokens | " +
self.total.to_string() +
" |\n" +
"| Color tokens | " +
self.colors.to_string() +
" |\n" +
"| Number tokens | " +
self.numbers.to_string() +
" |\n" +
"| Text tokens | " +
self.texts.to_string() +
" |\n" +
"| Boolean tokens | " +
self.booleans.to_string() +
" |\n" +
"| Aliases | " +
self.aliases.to_string() +
" |\n" +
"| Documentation rate | " +
self.documentation_rate().to_string() +
"% |\n" +
"| Empty-value rate | " +
self.empty_rate().to_string() +
"% |\n" +
"| Density score | " +
self.density_score().to_string() +
"/100 |"
}
///|
pub fn TokenMetrics::to_json(self : TokenMetrics) -> String {
"{\"total\":" +
self.total.to_string() +
",\"colors\":" +
self.colors.to_string() +
",\"numbers\":" +
self.numbers.to_string() +
",\"texts\":" +
self.texts.to_string() +
",\"booleans\":" +
self.booleans.to_string() +
",\"aliases\":" +
self.aliases.to_string() +
",\"documentationRate\":" +
self.documentation_rate().to_string() +
",\"densityScore\":" +
self.density_score().to_string() +
"}"
}
///|
pub fn ThemeDocument::metric_markdown(self : ThemeDocument) -> String {
self.metrics().to_markdown()
}
///|
pub fn ThemeDocument::metric_json(self : ThemeDocument) -> String {
self.metrics().to_json()
}
///|
pub fn ThemeDocument::has_only_documented_tokens(self : ThemeDocument) -> Bool {
self.metrics().documented == self.metrics().total
}
///|
pub fn ThemeDocument::has_kind(self : ThemeDocument, kind : TokenKind) -> Bool {
self.metrics().kind_count(kind) > 0
}
///|
pub fn ThemeDocument::value_for(
self : ThemeDocument,
path : String,
) -> ThemeValue? {
match self.find(path) {
Some(token) => Some(token.value)
None => None
}
}
///|
pub fn ThemeDocument::values_for_kind(
self : ThemeDocument,
kind : TokenKind,
) -> Array[ThemeValue] {
self.tokens
.filter(fn(token) { token.kind == kind })
.map(fn(token) { token.value })
}
///|
pub fn ThemeDocument::paths_for_kind(
self : ThemeDocument,
kind : TokenKind,
) -> Array[String] {
self.tokens
.filter(fn(token) { token.kind == kind })
.map(fn(token) { token.path.canonical() })
}
///|
fn theme_value_is_empty(value : ThemeValue) -> Bool {
match value {
ColorValue(text) => text.trim().to_owned() == ""
NumberValue(number) => number == 0
TextValue(text) => text.trim().to_owned() == ""
BooleanValue(_) => false
}
}