irmf_include_resolver/
lib.rs1use regex::Regex;
10use thiserror::Error;
11
12#[derive(Error, Debug)]
14pub enum ResolverError {
15 #[error("Network error: {0}")]
17 NetworkError(#[from] reqwest::Error),
18}
19
20pub async fn resolve_includes(source: &str) -> Result<String, ResolverError> {
28 let mut resolved_source = String::new();
29 let include_re = Regex::new("^#include\\s+\"([^\"]+)\"").unwrap();
30
31 for line in source.lines() {
32 let trimmed = line.trim();
33 if let Some(caps) = include_re.captures(trimmed) {
34 let inc = &caps[1];
35 if let Some(url) = parse_include_url(inc) {
36 if let Ok(content) = fetch_url(&url).await {
37 resolved_source.push_str(&content);
38 resolved_source.push('\n');
39 }
40 continue;
41 }
42 }
43 resolved_source.push_str(line);
44 resolved_source.push('\n');
45 }
46
47 Ok(resolved_source)
48}
49
50fn parse_include_url(inc: &str) -> Option<String> {
52 if !inc.ends_with(".glsl") && !inc.ends_with(".wgsl") {
53 return None;
54 }
55
56 const LYGIA_BASE_URL: &str = "https://lygia.xyz";
57 const GITHUB_RAW_PREFIX: &str = "https://raw.githubusercontent.com/";
58
59 if let Some(stripped) = inc.strip_prefix("lygia.xyz/") {
60 Some(format!("{}/{}", LYGIA_BASE_URL, stripped))
61 } else if let Some(stripped) = inc.strip_prefix("lygia/") {
62 Some(format!("{}/{}", LYGIA_BASE_URL, stripped))
63 } else if let Some(stripped) = inc.strip_prefix("github.com/") {
64 let location = stripped.replace("/blob/", "/");
65 Some(format!("{}{}", GITHUB_RAW_PREFIX, location))
66 } else {
67 None
68 }
69}
70
71async fn fetch_url(url: &str) -> Result<String, ResolverError> {
73 println!("Fetching {}", url);
74 let content = reqwest::get(url).await?.text().await?;
75 Ok(content)
76}