1use crate::error::Result;
4use crate::generators::badges::{self as badges_gen, BadgesConfig};
5use crate::generators::cargo_add::{self as cargo_add_gen};
6use crate::generators::cargo_install::{self as cargo_install_gen};
7use crate::generators::contributors::{self as contributors_gen, ContributorsConfig};
8use crate::generators::description::{self as description_gen};
9use crate::generators::file::{self as file_gen};
10use crate::generators::with_automdrs::{self as with_automdrs_gen, WithAutomdrsConfig};
11use crate::parser::cargo::ParsedManifest;
12use crate::parser::tag_options::{option_bool, parse_tag_options};
13use urlogger::{LogLevel, log};
14
15#[derive(Debug, Clone)]
17pub struct UpdateContext {
18 pub config: ParsedManifest,
19 pub manifest_dir: std::path::PathBuf,
21}
22
23impl UpdateContext {
24 pub fn new(config: ParsedManifest, manifest_dir: std::path::PathBuf) -> Self {
25 Self {
26 config,
27 manifest_dir,
28 }
29 }
30}
31
32pub trait BlockHandler: Send + Sync {
34 fn generate(
35 &self,
36 block_name: &str,
37 open_tag_line: &str,
38 context: &UpdateContext,
39 ) -> Result<Vec<String>>;
40}
41
42fn parse_badges_config(open_tag: &str) -> BadgesConfig {
43 let opts = parse_tag_options(open_tag, "badges");
44 log!(LogLevel::Trace, "badges config: {:?}", opts);
45 BadgesConfig {
46 version: option_bool(&opts, &["showCrateVersion", "version"]),
47 downloads: option_bool(&opts, &["showCrateDownloads", "downloads"]),
48 docs: option_bool(&opts, &["showCrateDocs", "docs"]),
49 commit_activity: option_bool(&opts, &["showCommitActivity", "commit_activity"]),
50 repo_stars: option_bool(&opts, &["showRepoStars", "repo_stars"]),
51 }
52}
53
54fn parse_contributors_config(open_tag: &str) -> ContributorsConfig {
55 let opts = parse_tag_options(open_tag, "contributors");
56 log!(LogLevel::Trace, "contributors config: {:?}", opts);
57 ContributorsConfig {
58 author: opts.get("author").cloned().unwrap_or_default(),
59 license: opts.get("license").cloned().unwrap_or_default(),
60 }
61}
62
63fn parse_with_automdrs_config(open_tag: &str) -> WithAutomdrsConfig {
64 let opts = parse_tag_options(open_tag, "with-automdrs");
65 log!(LogLevel::Trace, "with-automdrs config: {:?}", opts);
66 WithAutomdrsConfig {
67 message: opts.get("message").cloned().unwrap_or_default(),
68 }
69}
70
71#[derive(Debug, Default)]
73pub struct DefaultHandler;
74
75impl BlockHandler for DefaultHandler {
76 fn generate(
77 &self,
78 block_name: &str,
79 open_tag_line: &str,
80 context: &UpdateContext,
81 ) -> Result<Vec<String>> {
82 match block_name {
83 "badges" => {
84 let config = parse_badges_config(open_tag_line);
85 Ok(badges_gen::generate(&config, &context.config))
86 }
87 "contributors" => {
88 let config = parse_contributors_config(open_tag_line);
89 Ok(contributors_gen::generate(&config, &context.config))
90 }
91 "with-automdrs" => {
92 let config = parse_with_automdrs_config(open_tag_line);
93 Ok(with_automdrs_gen::generate(&config))
94 }
95 "cargo-install" => Ok(cargo_install_gen::generate(&context.config)),
96 "cargo-add" => Ok(cargo_add_gen::generate(&context.config)),
97 "description" => Ok(description_gen::generate(&context.config)),
98 "file" => {
99 let opts = parse_tag_options(open_tag_line, "file");
100 let src = opts.get("src").cloned().unwrap_or_default();
101 Ok(file_gen::generate(&context.manifest_dir, &src)?)
102 }
103 _ => Ok(vec![]),
104 }
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::parser::cargo::ParsedManifest;
112
113 fn context() -> UpdateContext {
114 UpdateContext::new(
115 ParsedManifest {
116 name: "test-crate".to_string(),
117 description: "d".to_string(),
118 username: "u".to_string(),
119 repository_name: "r".to_string(),
120 },
121 std::path::PathBuf::from("."),
122 )
123 }
124
125 #[test]
126 fn test_update_context_new() {
127 let ctx = context();
128 assert_eq!(ctx.config.name, "test-crate");
129 }
130
131 #[test]
132 fn test_generate_badges() {
133 let h = DefaultHandler::default();
134 let out = h
135 .generate(
136 "badges",
137 "<!-- automdrs:badges version docs -->",
138 &context(),
139 )
140 .unwrap();
141 assert!(!out.is_empty());
142 assert!(out[0].contains("crates/v/test-crate"));
143 }
144
145 #[test]
146 fn test_generate_contributors() {
147 let h = DefaultHandler::default();
148 let out = h
149 .generate(
150 "contributors",
151 "<!-- automdrs:contributors author=\"A\" license=\"MIT\" -->",
152 &context(),
153 )
154 .unwrap();
155 assert_eq!(out.len(), 1);
156 assert!(out[0].contains("A"));
157 assert!(out[0].contains("MIT"));
158 }
159
160 #[test]
161 fn test_generate_with_automdrs() {
162 let h = DefaultHandler::default();
163 let out = h
164 .generate(
165 "with-automdrs",
166 "<!-- automdrs:with-automdrs -->",
167 &context(),
168 )
169 .unwrap();
170 assert_eq!(out.len(), 1);
171 assert!(out[0].contains("automd-rs"));
172 }
173
174 #[test]
175 fn test_generate_unknown_block() {
176 let h = DefaultHandler::default();
177 let out = h
178 .generate("unknown", "<!-- automdrs:unknown -->", &context())
179 .unwrap();
180 assert_eq!(out, Vec::<String>::new());
181 }
182
183 #[test]
184 fn test_generate_file() {
185 let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
186 let ctx = UpdateContext::new(
187 ParsedManifest {
188 name: "test".to_string(),
189 description: "d".to_string(),
190 username: "u".to_string(),
191 repository_name: "r".to_string(),
192 },
193 manifest_dir.to_path_buf(),
194 );
195 let h = DefaultHandler::default();
196 let out = h
197 .generate("file", "<!-- automdrs:file src=\"./src/main.rs\" -->", &ctx)
198 .unwrap();
199 assert!(!out.is_empty());
200 assert_eq!(out[0], "```rust");
201 }
202}