use super::{listed, plural};
use crate::spec::{ComponentKind, ConnectionDirection, Locale};
use std::fmt::Write as _;
pub(crate) const fn not_stated(locale: Locale) -> &'static str {
match locale {
Locale::En => "not stated",
Locale::De => "nicht angegeben",
}
}
pub(crate) fn perspective(locale: Locale, title: &str, entries: &[String]) -> String {
let entries = entries.join("; ");
match locale {
Locale::En => format!("Perspective {title}: {entries}."),
Locale::De => format!("Perspektive {title}: {entries}."),
}
}
pub(crate) fn steps(locale: Locale, steps: &[String]) -> String {
let steps = steps.join("; ");
match locale {
Locale::En => format!("Steps: {steps}."),
Locale::De => format!("Schritte: {steps}."),
}
}
pub(crate) fn step_number(locale: Locale, step: u8) -> String {
match locale {
Locale::En => format!("step {step}"),
Locale::De => format!("Schritt {step}"),
}
}
pub(crate) fn architecture_opening(
locale: Locale,
components: usize,
connections: usize,
boundaries: usize,
) -> String {
let boundaries = match (locale, boundaries) {
(_, 0) => String::new(),
(Locale::En, count) => format!(" in {count} boundar{}", plural(count, "y", "ies")),
(Locale::De, count) => format!(" in {count} {}", plural(count, "Grenze", "Grenzen")),
};
match locale {
Locale::En => format!(
"Architecture diagram with {components} component{} and {connections} connection{}{boundaries}.",
plural(components, "", "s"),
plural(connections, "", "s"),
),
Locale::De => format!(
"Architekturdiagramm mit {components} {} und {connections} {}{boundaries}.",
plural(components, "Komponente", "Komponenten"),
plural(connections, "Verbindung", "Verbindungen"),
),
}
}
pub(crate) fn view_scope(
locale: Locale,
(components, all_components): (usize, usize),
(connections, all_connections): (usize, usize),
) -> String {
match locale {
Locale::En => format!(
"It is a view that shows {components} of the model's {all_components} component{} and {connections} of its {all_connections} connection{}.",
plural(all_components, "", "s"),
plural(all_connections, "", "s"),
),
Locale::De => format!(
"Die Sicht zeigt {components} der {all_components} {} und {connections} der {all_connections} {} des Modells.",
plural(all_components, "Komponente", "Komponenten"),
plural(all_connections, "Verbindung", "Verbindungen"),
),
}
}
pub(crate) fn deployment(
locale: Locale,
environment: &str,
instances: usize,
components: usize,
) -> String {
match locale {
Locale::En => format!(
"It shows the environment {environment}: {instances} instance{} of {components} component{} of the model; only the connections listed for the instances are drawn.",
plural(instances, "", "s"),
plural(components, "", "s"),
),
Locale::De => format!(
"Sie zeigt die Umgebung {environment}: {instances} {} von {components} {} des Modells; gezeichnet sind nur die für die Instanzen angegebenen Verbindungen.",
plural(instances, "Instanz", "Instanzen"),
plural(components, "Komponente", "Komponenten"),
),
}
}
pub(crate) fn boundary(label: &str, parent: Option<&str>, held: &[&str]) -> String {
let parent = parent.map_or_else(String::new, |parent| format!(" (in {parent})"));
format!("{label}{parent}: {}.", held.join(", "))
}
pub(crate) const fn component_kind(locale: Locale, kind: ComponentKind) -> &'static str {
match (locale, kind) {
(Locale::En, ComponentKind::Person) => "person",
(Locale::En, ComponentKind::Frontend) => "frontend",
(Locale::En, ComponentKind::Service) => "service",
(Locale::En, ComponentKind::Database) => "database",
(Locale::En, ComponentKind::Queue) => "queue",
(Locale::En, ComponentKind::Storage) => "storage",
(Locale::En, ComponentKind::Cache) => "cache",
(Locale::En, ComponentKind::External) => "external system",
(Locale::En, ComponentKind::Security) => "security",
(Locale::En, ComponentKind::Node) => "node",
(Locale::En, ComponentKind::Artifact) => "artifact",
(Locale::De, ComponentKind::Person) => "Person",
(Locale::De, ComponentKind::Frontend) => "Oberfläche",
(Locale::De, ComponentKind::Service) => "Dienst",
(Locale::De, ComponentKind::Database) => "Datenbank",
(Locale::De, ComponentKind::Queue) => "Warteschlange",
(Locale::De, ComponentKind::Storage) => "Speicher",
(Locale::De, ComponentKind::Cache) => "Cache",
(Locale::De, ComponentKind::External) => "externes System",
(Locale::De, ComponentKind::Security) => "Sicherheit",
(Locale::De, ComponentKind::Node) => "Knoten",
(Locale::De, ComponentKind::Artifact) => "Artefakt",
}
}
pub(crate) const fn connection_direction(
locale: Locale,
direction: ConnectionDirection,
) -> Option<&'static str> {
match (locale, direction) {
(_, ConnectionDirection::Forward) => None,
(Locale::En, ConnectionDirection::Both) => Some("both ways"),
(Locale::En, ConnectionDirection::None) => Some("without direction"),
(Locale::De, ConnectionDirection::Both) => Some("in beide Richtungen"),
(Locale::De, ConnectionDirection::None) => Some("ohne Richtung"),
}
}
pub(crate) fn navigable(
locale: Locale,
direction: ConnectionDirection,
to: &str,
) -> Option<String> {
match (locale, direction) {
(_, ConnectionDirection::None) => None,
(Locale::En, ConnectionDirection::Forward) => Some(format!("navigable to {to}")),
(Locale::En, ConnectionDirection::Both) => Some("navigable both ways".to_owned()),
(Locale::De, ConnectionDirection::Forward) => Some(format!("navigierbar zu {to}")),
(Locale::De, ConnectionDirection::Both) => {
Some("in beide Richtungen navigierbar".to_owned())
}
}
}
pub(crate) fn connection_note(
locale: Locale,
label: Option<&str>,
technology: Option<&str>,
) -> Option<String> {
let via = match locale {
Locale::En => "via",
Locale::De => "über",
};
match (label, technology) {
(None, None) => None,
(Some(label), None) => Some(label.to_owned()),
(None, Some(technology)) => Some(format!("{via} {technology}")),
(Some(label), Some(technology)) => Some(format!("{label}, {via} {technology}")),
}
}
pub(crate) fn requires_interface(locale: Locale, interface: &str) -> String {
match locale {
Locale::En => format!("requires {interface}"),
Locale::De => format!("benötigt {interface}"),
}
}
pub(crate) fn interfaces(locale: Locale, provided: &[(&str, Vec<String>)]) -> String {
provided
.iter()
.map(|(component, interfaces)| {
let interfaces = listed(locale, interfaces);
match locale {
Locale::En => format!("{component} provides {interfaces}."),
Locale::De => format!("{component} stellt {interfaces} bereit."),
}
})
.collect::<Vec<_>>()
.join(" ")
}
pub(crate) fn component(locale: Locale, label: &str, kind: &str, next: &[String]) -> String {
let next = match (locale, next.is_empty()) {
(Locale::En, true) => "no outgoing connection".to_owned(),
(Locale::De, true) => "keine ausgehende Verbindung".to_owned(),
(Locale::En, false) => format!("connects to {}", listed(locale, next)),
(Locale::De, false) => format!("verbunden mit {}", listed(locale, next)),
};
format!("{label} ({kind}): {next}.")
}
pub(crate) fn classes_opening(
locale: Locale,
classes: usize,
relations: usize,
packages: usize,
) -> String {
let packages = match (locale, packages) {
(_, 0) => String::new(),
(Locale::En, count) => format!(" in {count} package{}", plural(count, "", "s")),
(Locale::De, count) => format!(" in {count} {}", plural(count, "Paket", "Paketen")),
};
match locale {
Locale::En => format!(
"Class diagram with {classes} class{} and {relations} relation{}{packages}.",
plural(classes, "", "es"),
plural(relations, "", "s")
),
Locale::De => format!(
"Klassendiagramm mit {classes} {} und {relations} {}{packages}.",
plural(classes, "Klasse", "Klassen"),
plural(relations, "Beziehung", "Beziehungen")
),
}
}
pub(crate) fn class_sentence(
locale: Locale,
label: &str,
(attributes, methods): (usize, usize),
relations: &[String],
) -> String {
let members = match locale {
Locale::En => format!(
"{attributes} attribute{}, {methods} method{}",
plural(attributes, "", "s"),
plural(methods, "", "s")
),
Locale::De => format!(
"{attributes} {}, {methods} {}",
plural(attributes, "Attribut", "Attribute"),
plural(methods, "Methode", "Methoden")
),
};
let mut sentence = format!("{label}: {members}.");
if !relations.is_empty() {
let joined = relations.join("; ");
sentence.push(' ');
write!(sentence, "{label} {joined}.").expect("writing to String cannot fail");
}
sentence
}
pub(crate) fn class_kind(locale: Locale, kind: crate::spec::ClassKind) -> &'static str {
use crate::spec::ClassKind;
match (locale, kind) {
(Locale::En, ClassKind::Class) => "Class",
(Locale::En, ClassKind::Interface) => "Interface",
(Locale::En, ClassKind::Abstract) => "Abstract class",
(Locale::En, ClassKind::Enum) => "Enumeration",
(Locale::En, ClassKind::Entity) => "Entity",
(Locale::De, ClassKind::Class) => "Klasse",
(Locale::De, ClassKind::Interface) => "Schnittstelle",
(Locale::De, ClassKind::Abstract) => "Abstrakte Klasse",
(Locale::De, ClassKind::Enum) => "Aufzählung",
(Locale::De, ClassKind::Entity) => "Entität",
}
}
pub(crate) fn relation_phrase(
locale: Locale,
(kind, from_side): (crate::spec::RelationKind, bool),
other: &str,
cardinality: Option<&str>,
label: Option<&str>,
) -> String {
use crate::spec::RelationKind;
let verb = match (locale, kind, from_side) {
(Locale::En, RelationKind::Association, _) => "is associated with",
(Locale::En, RelationKind::Inheritance, true) => "inherits from",
(Locale::En, RelationKind::Inheritance, false) => "is the parent of",
(Locale::En, RelationKind::Realization, true) => "implements",
(Locale::En, RelationKind::Realization, false) => "is implemented by",
(Locale::En, RelationKind::Composition, true) => "is composed of",
(Locale::En, RelationKind::Composition, false) => "is part of",
(Locale::En, RelationKind::Aggregation, true) => "holds",
(Locale::En, RelationKind::Aggregation, false) => "is held by",
(Locale::En, RelationKind::Dependency, true) => "depends on",
(Locale::En, RelationKind::Dependency, false) => "is needed by",
(Locale::De, RelationKind::Association, _) => "ist verbunden mit",
(Locale::De, RelationKind::Inheritance, true) => "erbt von",
(Locale::De, RelationKind::Inheritance, false) => "ist Elternklasse von",
(Locale::De, RelationKind::Realization, true) => "implementiert",
(Locale::De, RelationKind::Realization, false) => "wird implementiert von",
(Locale::De, RelationKind::Composition, true) => "besteht aus",
(Locale::De, RelationKind::Composition, false) => "ist Teil von",
(Locale::De, RelationKind::Aggregation, true) => "enthält",
(Locale::De, RelationKind::Aggregation, false) => "wird enthalten von",
(Locale::De, RelationKind::Dependency, true) => "hängt ab von",
(Locale::De, RelationKind::Dependency, false) => "wird gebraucht von",
};
let mut phrase = format!("{verb} {other}");
if let Some(cardinality) = cardinality {
write!(phrase, " ({cardinality})").expect("writing to String cannot fail");
}
if let Some(label) = label {
write!(phrase, " [{label}]").expect("writing to String cannot fail");
}
phrase
}