pub const ONTOLOGY_NS: &str = "http://aegis.gastown.local/ontology/";
pub const CODE_BASE: &str = "http://aegis.gastown.local/ontology/code/";
pub const DOC_BASE: &str = "http://aegis.gastown.local/ontology/doc/";
pub const CHUNK_BASE: &str = "http://aegis.gastown.local/ontology/chunk/";
pub const INFERRED_BASE: &str = "http://aegis.gastown.local/ontology/inferred/";
pub fn iri_segment(raw: &str) -> String {
let mut out = String::with_capacity(raw.len());
for c in raw.chars() {
match c {
'%' => out.push_str("%25"),
' ' => out.push_str("%20"),
'<' => out.push_str("%3C"),
'>' => out.push_str("%3E"),
'"' => out.push_str("%22"),
'{' => out.push_str("%7B"),
'}' => out.push_str("%7D"),
'|' => out.push_str("%7C"),
'\\' => out.push_str("%5C"),
'^' => out.push_str("%5E"),
'`' => out.push_str("%60"),
'[' => out.push_str("%5B"),
']' => out.push_str("%5D"),
'\n' | '\t' => {}
_ => out.push(c),
}
}
out
}
pub fn path_segment(rel: &str) -> String {
iri_segment(rel).replace('/', "%2F")
}
pub fn iri_encode(s: &str) -> String {
s.replace('%', "%25")
.replace('/', "%2F")
.replace(' ', "%20")
.replace('<', "%3C")
.replace('>', "%3E")
.replace('"', "%22")
.replace('{', "%7B")
.replace('}', "%7D")
}
pub fn slugify(heading: &str) -> String {
let mut out = String::new();
let mut last_dash = true; for c in heading.chars() {
if c.is_alphanumeric() {
for lc in c.to_lowercase() {
out.push(lc);
}
last_dash = false;
} else if (c == ' ' || c == '-' || c == '_') && !last_dash {
out.push('-');
last_dash = true;
}
}
while out.ends_with('-') {
out.pop();
}
out
}
pub fn code_module_iri(repo: &str, path: &str) -> String {
format!("{CODE_BASE}{}/{}", iri_segment(repo), path_segment(path))
}
pub fn symbol_iri(repo: &str, path: &str, name: &str) -> String {
format!("{}::{}", code_module_iri(repo, path), iri_segment(name))
}
pub fn document_iri(repo: &str, path: &str) -> String {
format!("{DOC_BASE}{}/{}", iri_segment(repo), path_segment(path))
}
pub fn section_iri(repo: &str, path: &str, heading: &str) -> String {
format!(
"{}#{}",
document_iri(repo, path),
slugify(section_heading_leaf(heading))
)
}
pub fn section_heading_leaf(heading: &str) -> &str {
heading.rsplit(" > ").next().unwrap_or(heading)
}
pub fn chunk_iri(repo: &str, path: &str, start_line: u32) -> String {
format!(
"{CHUNK_BASE}{}/{}#C{start_line}",
iri_segment(repo),
path_segment(path)
)
}
mod parse;
pub use parse::{code_module_prefix_of, entity_iri_file_path};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_the_live_hank_lane() {
assert_eq!(
code_module_iri("quipu", "src/w3c.rs"),
"http://aegis.gastown.local/ontology/code/quipu/src%2Fw3c.rs"
);
assert_eq!(
symbol_iri("quipu", "src/w3c.rs", "negotiate"),
"http://aegis.gastown.local/ontology/code/quipu/src%2Fw3c.rs::negotiate"
);
assert_eq!(
code_module_iri("quipu", "tests/no_internal_identifiers.rs"),
"http://aegis.gastown.local/ontology/code/quipu/tests%2Fno_internal_identifiers.rs"
);
assert_eq!(
symbol_iri("quipu", "build.rs", "main"),
"http://aegis.gastown.local/ontology/code/quipu/build.rs::main"
);
assert_eq!(
document_iri("quipu", "docs/design/reasoner.md"),
"http://aegis.gastown.local/ontology/doc/quipu/docs%2Fdesign%2Freasoner.md"
);
assert_eq!(
section_iri("quipu", "README.md", "See it in action"),
"http://aegis.gastown.local/ontology/doc/quipu/README.md#see-it-in-action"
);
assert_eq!(
section_iri("quipu", "README.md", "REST API / Web UI"),
"http://aegis.gastown.local/ontology/doc/quipu/README.md#rest-api-web-ui"
);
assert_eq!(
section_iri("quipu", "README.md", "Guide > Setup"),
section_iri("quipu", "README.md", "Setup")
);
}
#[test]
fn never_mints_the_superseded_ingest_repos_lane() {
for iri in [
code_module_iri("quipu", "src/w3c.rs"),
symbol_iri("quipu", "src/w3c.rs", "negotiate"),
document_iri("quipu", "README.md"),
section_iri("quipu", "README.md", "Features"),
chunk_iri("quipu", "README.md", 7),
] {
assert!(
!iri.starts_with("http://aegis.gastown.local/code/"),
"regressed to the superseded ingest-repos.py lane: {iri}"
);
assert!(
!iri.contains("-L"),
"regressed to the -L{{line}} idiom: {iri}"
);
assert!(
!iri.contains("/S") || iri.contains("%2FS"),
"regressed to the S{{line}} section idiom: {iri}"
);
}
}
#[test]
fn slugify_matches_github_style() {
assert_eq!(slugify("Hello, World!"), "hello-world");
assert_eq!(slugify(" Multi Space "), "multi-space");
assert_eq!(slugify("snake_case name"), "snake-case-name");
assert_eq!(slugify("REST API / Web UI"), "rest-api-web-ui");
}
#[test]
fn encodes_the_characters_that_break_a_whole_document() {
assert_eq!(iri_segment("[Symbol.iterator]"), "%5BSymbol.iterator%5D");
assert_eq!(iri_segment("Foo<T>"), "Foo%3CT%3E");
assert_eq!(iri_segment("a|b\\c^d`e"), "a%7Cb%5Cc%5Ed%60e");
assert_eq!(iri_segment("50% done"), "50%25%20done");
assert_ne!(iri_segment("%2F"), iri_segment("/"));
}
#[test]
fn paths_are_one_opaque_segment() {
assert_eq!(path_segment("src/main.rs"), "src%2Fmain.rs");
assert_eq!(path_segment("docs/my file.md"), "docs%2Fmy%20file.md");
}
#[test]
fn chunks_are_off_the_code_and_doc_lanes() {
let c = chunk_iri("q", "README.md", 7);
assert_eq!(
c,
"http://aegis.gastown.local/ontology/chunk/q/README.md#C7"
);
assert!(!c.starts_with(CODE_BASE));
assert!(!c.starts_with(DOC_BASE));
}
}