wowlab_docgen_cli/infra/
badge.rs1const DEFAULT_REPO: &str = "legacy3/wowlab";
4
5#[must_use]
7pub fn build(workspace: &crate::WorkspaceIndex, names: &[String]) -> String {
8 let repo = detect_repo(workspace);
9
10 names
11 .iter()
12 .filter_map(|name| render_badge(name, workspace, &repo))
13 .collect::<Vec<_>>()
14 .join("\n ")
15}
16
17fn detect_repo(workspace: &crate::WorkspaceIndex) -> String {
18 let cargo_path = workspace.root().join("Cargo.toml");
19 let Some(content) = workspace.contents(&cargo_path) else {
20 return DEFAULT_REPO.to_string();
21 };
22
23 for line in content.lines() {
24 let trimmed = line.trim();
25
26 if let Some(rest) = trimmed.strip_prefix("repository") {
27 let rest = rest.trim().strip_prefix('=').unwrap_or(rest);
28 let rest = rest.trim().trim_matches('"');
29
30 if let Some(gh) = rest.strip_prefix("https://github.com/") {
31 let slug = gh.trim_end_matches('/');
32
33 if !slug.is_empty() {
35 return slug.to_string();
36 }
37 }
38 }
39 }
40
41 DEFAULT_REPO.to_string()
42}
43
44fn render_badge(name: &str, workspace: &crate::WorkspaceIndex, repo: &str) -> Option<String> {
45 match name {
46 "ci" => {
47 if !workspace.contains(&workspace.root().join(".github/workflows/ci.yml")) {
48 return None;
49 }
50
51 Some(format!(
52 r#"<a href="https://github.com/{repo}/actions/workflows/ci.yml"><img src="https://github.com/{repo}/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>"#
53 ))
54 }
55 "discord" => Some(
56 r#"<a href="https://wowlab.gg/go/discord"><img src="https://img.shields.io/badge/discord-join-5865F2?logo=discord&logoColor=white" alt="Discord" /></a>"#.to_string(),
57 ),
58 "license" => Some(
59 r#"<a href="LICENSE.md"><img src="https://img.shields.io/badge/license-PolyForm%20Noncommercial-green" alt="License" /></a>"#.to_string(),
60 ),
61 "website" => Some(
62 r#"<a href="https://wowlab.gg"><img src="https://img.shields.io/badge/website-wowlab.gg-blue" alt="Website" /></a>"#.to_string(),
63 ),
64 _ => None,
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use googletest::prelude::*;
71 use wowlab_fs::path::Path;
72
73 use super::*;
74 use crate::{WorkspaceFile, WorkspaceIndex};
75
76 fn workspace(files: &[(&str, &str)]) -> WorkspaceIndex {
77 let root = Path::new("/ws");
78 let files = files
79 .iter()
80 .map(|(path, contents)| WorkspaceFile {
81 path: root.join(path),
82 contents: Some(Box::from(*contents)),
83 })
84 .collect();
85
86 WorkspaceIndex::new(root.to_path_buf(), files)
87 }
88
89 fn build(files: &[(&str, &str)], names: &[String]) -> String {
90 super::build(&workspace(files), names)
91 }
92
93 fn detect_repo(files: &[(&str, &str)]) -> String {
94 super::detect_repo(&workspace(files))
95 }
96
97 #[gtest]
98 fn build_static_badges() -> Result<()> {
99 let result = build(
100 &[],
101 &[
102 "discord".to_string(),
103 "license".to_string(),
104 "website".to_string(),
105 ],
106 );
107
108 verify_that!(result.as_str(), contains_substring("alt=\"Discord\""))?;
109 verify_that!(result.as_str(), contains_substring("alt=\"License\""))?;
110 verify_that!(result.as_str(), contains_substring("alt=\"Website\""))?;
111
112 Ok(())
113 }
114
115 #[gtest]
116 fn build_ci_badge_requires_workflow() -> Result<()> {
117 let result = build(&[], &["ci".to_string()]);
118
119 verify_eq!(result, "")?;
120
121 let result = build(
122 &[(".github/workflows/ci.yml", "name: CI")],
123 &["ci".to_string()],
124 );
125
126 verify_that!(
127 result.as_str(),
128 contains_substring("actions/workflows/ci.yml/badge.svg")
129 )?;
130
131 Ok(())
132 }
133
134 #[gtest]
135 fn build_skips_unknown_names() -> Result<()> {
136 let result = build(
137 &[],
138 &[
139 "discord".to_string(),
140 "bogus".to_string(),
141 "license".to_string(),
142 ],
143 );
144
145 verify_eq!(result.lines().count(), 2)
146 }
147
148 #[gtest]
149 fn build_empty_for_no_names() -> Result<()> {
150 verify_eq!(build(&[], &[]), "")
151 }
152
153 #[gtest]
154 fn detect_repo_from_cargo_toml() -> Result<()> {
155 verify_eq!(
156 detect_repo(&[(
157 "Cargo.toml",
158 "[workspace.package]\nrepository = \"https://github.com/myorg/myrepo\"\n",
159 )]),
160 "myorg/myrepo"
161 )
162 }
163
164 #[gtest]
165 fn detect_repo_fallback() -> Result<()> {
166 verify_eq!(detect_repo(&[]), DEFAULT_REPO)
167 }
168
169 #[gtest]
170 fn ci_badge_uses_detected_repo() -> Result<()> {
171 let result = build(
172 &[
173 (".github/workflows/ci.yml", "name: CI"),
174 (
175 "Cargo.toml",
176 "[workspace.package]\nrepository = \"https://github.com/cool/project\"\n",
177 ),
178 ],
179 &["ci".to_string()],
180 );
181
182 verify_that!(result.as_str(), contains_substring("cool/project"))
183 }
184}