#![allow(dead_code)]
use std::path::Path;
pub fn component_src(relative: &str) -> String {
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join(relative);
std::fs::read_to_string(&path)
.unwrap_or_else(|err| panic!("{} must be readable: {err}", path.display()))
}
pub fn assert_chrome_call_is_under(source: &str, guard: &str) {
let calls = source.matches("apply_field_chrome(").count()
+ source
.matches("apply_field_chrome_with_focus_ring(")
.count()
+ source.matches("apply_field_chrome_overlay(").count()
+ source.matches("apply_field_chrome_ringless(").count();
assert_eq!(
calls, 1,
"the component must have exactly one chrome call site, found {calls}"
);
let guard_at = source
.find(guard)
.unwrap_or_else(|| panic!("the chrome call must be under `{guard}`"));
let body_at = guard_at + guard.len();
let body_len = guard_body_len(&source[body_at..]);
assert!(
source[body_at..body_at + body_len].contains("apply_field_chrome(")
|| source[body_at..body_at + body_len].contains("apply_field_chrome_with_focus_ring(")
|| source[body_at..body_at + body_len].contains("apply_field_chrome_overlay(")
|| source[body_at..body_at + body_len].contains("apply_field_chrome_ringless("),
"the only chrome call must sit inside the `{guard}` guard"
);
}
fn guard_body_len(after_open: &str) -> usize {
let mut depth = 1usize;
for (index, ch) in after_open.char_indices() {
match ch {
'{' => depth += 1,
'}' => {
depth -= 1;
if depth == 0 {
return index;
}
}
_ => {}
}
}
panic!("the guard block must close");
}
pub fn enclosing_function<'a>(source: &'a str, marker: &str) -> Option<&'a str> {
let marker_index = source.find(marker)?;
let line_start = source[..marker_index].rfind('\n').map_or(0, |i| i + 1);
let mut start = None;
if starts_function(&source[line_start..]) {
start = Some(line_start);
} else {
let mut offset = 0;
for line in source[..line_start].split_inclusive('\n') {
if starts_function(line) {
start = Some(offset);
}
offset += line.len();
}
}
let start = start?;
let indent_len = source[start..].len() - source[start..].trim_start().len();
let indent = &source[start..start + indent_len];
let rest = &source[start..];
let close = format!("\n{indent}}}");
let end = rest.find(&close).map(|index| start + index + 1)?;
Some(&source[start..end])
}
fn starts_function(line: &str) -> bool {
let line = line.trim_start();
let line = line
.strip_prefix("pub(crate) ")
.or_else(|| line.strip_prefix("pub(super) "))
.or_else(|| line.strip_prefix("pub "))
.or_else(|| line.strip_prefix("async "))
.unwrap_or(line);
line.starts_with("fn ")
}
pub fn scope_contains(source: &str, marker: &str, needle: &str) -> Result<(), String> {
let scope = enclosing_function(source, marker)
.ok_or_else(|| format!("no function contains marker {marker:?}"))?;
if scope.contains(needle) {
Ok(())
} else {
Err(format!(
"the function owning {marker:?} does not read {needle:?}"
))
}
}