Skip to main content

searchfox_lib/
spec_refs.rs

1use crate::client::SearchfoxClient;
2use crate::search::{SearchOptions, SearchResult};
3use anyhow::Result;
4use url::Url;
5
6const SPEC_REF_PATH_CATEGORIES: &[(&str, &str)] = &[
7    ("testing/web-platform", "Web-Platform Test"),
8    ("js/src/tests/test262", "Test262"),
9    ("js/src/jit-test/tests/wasm", "WebAssembly Test"),
10];
11
12pub fn categorize_spec_ref(path: &str) -> &'static str {
13    let lower = path.to_lowercase();
14    for (prefix, category) in SPEC_REF_PATH_CATEGORIES {
15        if lower.starts_with(prefix) {
16            return category;
17        }
18    }
19    if lower.contains("test") {
20        return "Test";
21    }
22    "Code"
23}
24
25pub fn spec_ref_category_names() -> &'static [&'static str] {
26    &[
27        "Code",
28        "Test",
29        "Test262",
30        "WebAssembly Test",
31        "Web-Platform Test",
32    ]
33}
34
35pub fn spec_refs_query(spec_url: &str) -> Result<String> {
36    let parsed = Url::parse(spec_url).map_err(|_| anyhow::anyhow!("Invalid URL: {spec_url}"))?;
37    let domain = parsed
38        .host_str()
39        .ok_or_else(|| anyhow::anyhow!("No host in URL: {spec_url}"))?
40        .to_string();
41    let anchor = parsed
42        .fragment()
43        .ok_or_else(|| anyhow::anyhow!("URL must contain a #fragment: {spec_url}"))?
44        .to_string();
45    Ok(format!(
46        "re:{}[^\\s]*#{}\\b",
47        regex::escape(&domain),
48        regex::escape(&anchor)
49    ))
50}
51
52impl SearchfoxClient {
53    pub async fn search_spec_refs(
54        &self,
55        spec_url: &str,
56        limit: usize,
57    ) -> Result<Vec<SearchResult>> {
58        let query = spec_refs_query(spec_url)?;
59        let options = SearchOptions {
60            query: Some(query),
61            limit,
62            ..Default::default()
63        };
64        self.search(&options).await
65    }
66}