1#![warn(missing_docs)]
2
3#[macro_use]
53extern crate lazy_static;
54#[macro_use]
55extern crate rust_i18n;
56
57#[cfg(feature = "spider")]
58pub use spider;
59
60pub mod engine;
62pub mod i18n;
64
65pub use accessibility_scraper;
66pub use accessibility_scraper::fast_html5ever;
67pub use accessibility_scraper::Html;
68pub use accessibility_scraper::ElementRef;
69
70pub use crate::engine::audit::auditor::Auditor;
71pub use crate::engine::issue::Issue;
72
73i18n!("locales", fallback = "en");
74
75#[derive(Default)]
77pub enum Conformance {
78 #[default]
80 WCAGAAA,
81}
82
83#[derive(Default)]
85#[cfg(feature = "tokio")]
86pub struct AuditConfig {
87 pub html: String,
89 pub css: String,
91 pub bounding_box: bool,
93 pub locale: String,
95 pub conformance: Conformance,
97 #[cfg(feature = "spider")]
99 pub url: String,
100}
101
102#[derive(Default)]
104#[cfg(not(feature = "tokio"))]
105pub struct AuditConfig<'a> {
106 pub html: &'a str,
108 pub css: &'a str,
110 pub bounding_box: bool,
112 pub locale: &'a str,
114 pub conformance: Conformance,
116 #[cfg(feature = "spider")]
118 pub url: &'a str,
119}
120
121#[cfg(not(feature = "tokio"))]
122impl<'a> AuditConfig<'a> {
123 pub fn new(html: &'a str, css: &'a str, bounding_box: bool, locale: &'a str) -> Self {
125 AuditConfig {
126 html: html.into(),
127 css: css.into(),
128 bounding_box,
129 locale: locale.into(),
130 ..Default::default()
131 }
132 }
133
134 pub fn basic(html: &'a str) -> Self {
136 AuditConfig {
137 html: html.into(),
138 ..Default::default()
139 }
140 }
141
142 #[cfg(feature = "spider")]
144 pub fn new_website(url: &'a str, css: &'a str, bounding_box: bool, locale: &'a str) -> Self {
145 AuditConfig {
146 url: url.into(),
147 css: css.into(),
148 bounding_box,
149 locale: locale.into(),
150 ..Default::default()
151 }
152 }
153
154 #[cfg(not(feature = "spider"))]
156 pub fn new_website(
157 _url: &'a str,
158 _css: &'a str,
159 _bounding_box: bool,
160 _locale: &'a str,
161 ) -> Self {
162 AuditConfig::default()
163 }
164}
165
166#[cfg(feature = "tokio")]
167impl AuditConfig {
168 pub fn new(html: &str, css: &str, bounding_box: bool, locale: &str) -> Self {
170 AuditConfig {
171 html: html.into(),
172 css: css.into(),
173 bounding_box,
174 locale: locale.into(),
175 ..Default::default()
176 }
177 }
178
179 pub fn basic(html: &str) -> Self {
181 AuditConfig {
182 html: html.into(),
183 ..Default::default()
184 }
185 }
186
187 #[cfg(feature = "spider")]
189 pub fn new_website(url: &str, css: &str, bounding_box: bool, locale: &str) -> Self {
190 AuditConfig {
191 url: url.into(),
192 css: css.into(),
193 bounding_box,
194 locale: locale.into(),
195 ..Default::default()
196 }
197 }
198 #[cfg(not(feature = "spider"))]
200 pub fn new_website(_url: &str, _css: &str, _bounding_box: bool, _locale: &str) -> Self {
201 AuditConfig::default()
202 }
203}
204
205#[cfg(all(feature = "tokio", not(feature = "spider")))]
207pub async fn audit(config: AuditConfig) -> Vec<Issue> {
208 let document = accessibility_scraper::Html::parse_document(&config.html).await;
209 let auditor = Auditor::new(&document, &config.css, config.bounding_box, &config.locale);
210 engine::audit::wcag::WCAGAAA::audit(auditor).await
211}
212
213#[cfg(feature = "spider")]
214#[derive(Debug, Clone)]
215pub enum AuditResults {
217 Page(spider::hashbrown::HashMap<String, Vec<Issue>>),
219 Html(Vec<Issue>),
221}
222
223#[cfg(all(feature = "spider"))]
225pub async fn audit(config: &AuditConfig) -> AuditResults {
226 if !config.url.is_empty() {
227 use spider::website::Website;
228 let mut website: Website = Website::new(&config.url);
229 let mut rx2: tokio::sync::broadcast::Receiver<spider::page::Page> =
230 website.subscribe(16).unwrap();
231 let bounding_box = config.bounding_box;
232 let locale = config.locale.clone();
233
234 let audits = tokio::spawn(async move {
235 let mut issues: spider::hashbrown::HashMap<String, Vec<Issue>> =
236 spider::hashbrown::HashMap::new();
237
238 while let Ok(res) = rx2.recv().await {
239 let document = accessibility_scraper::Html::parse_document(&res.get_html()).await;
240 let auditor = Auditor::new(&document, &"", bounding_box, &locale);
241 let issue = engine::audit::wcag::WCAGAAA::audit(auditor).await;
242 issues.insert(res.get_url().into(), issue);
243 }
244
245 issues
246 });
247
248 website.crawl().await;
249 website.unsubscribe();
250 AuditResults::Page(audits.await.unwrap_or_default())
251 } else {
252 let document = accessibility_scraper::Html::parse_document(&config.html).await;
253 let auditor = Auditor::new(&document, &config.css, config.bounding_box, &config.locale);
254 AuditResults::Html(engine::audit::wcag::WCAGAAA::audit(auditor).await)
255 }
256}
257
258#[cfg(not(feature = "tokio"))]
260pub fn audit(config: &AuditConfig) -> Vec<Issue> {
261 let document = accessibility_scraper::Html::parse_document(&config.html);
262 let auditor = Auditor::new(&document, &config.css, config.bounding_box, &config.locale);
263 engine::audit::wcag::WCAGAAA::audit(auditor)
264}