# Share URL maps: enduring-word slugs + logos numeric book ids import Base def unmap_str(p: Map<&2, String> & String) -> String: match p: case (_, v): v def unhas_str(p: Map<&2, String> & Bool) -> Bool: match p: case (_, b): b def ew_map() -> Map<&2, String>: Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.new(&2, String), "1chronicles", "1CH"), "1corinthians", "1CO"), "1john", "1JN"), "1kings", "1KI"), "1peter", "1PE"), "1samuel", "1SA"), "1thessalonians", "1TH"), "1timothy", "1TI"), "2chronicles", "2CH"), "2corinthians", "2CO"), "2john", "2JN"), "2kings", "2KI"), "2peter", "2PE"), "2samuel", "2SA"), "2thessalonians", "2TH"), "2timothy", "2TI"), "3john", "3JN"), "acts", "ACT"), "amos", "AMO"), "colossians", "COL"), "daniel", "DAN"), "deuteronomy", "DEU"), "ecclesiastes", "ECC"), "ephesians", "EPH"), "esther", "EST"), "exodus", "EXO"), "ezekiel", "EZK"), "ezra", "EZR"), "galatians", "GAL"), "genesis", "GEN"), "habakkuk", "HAB"), "haggai", "HAG"), "hebrews", "HEB"), "hosea", "HOS"), "isaiah", "ISA"), "james", "JAS"), "judges", "JDG"), "jeremiah", "JER"), "john", "JHN"), "job", "JOB"), "joel", "JOL"), "jonah", "JON"), "joshua", "JOS"), "jude", "JUD"), "lamentations", "LAM"), "leviticus", "LEV"), "luke", "LUK"), "malachi", "MAL"), "matthew", "MAT"), "micah", "MIC"), "mark", "MRK"), "nahum", "NAM"), "nehemiah", "NEH"), "numbers", "NUM"), "obadiah", "OBA"), "philemon", "PHM"), "philippians", "PHP"), "proverbs", "PRO"), "psalm", "PSA"), "revelation", "REV"), "romans", "ROM"), "ruth", "RUT"), "song-of-solomon", "SNG"), "titus", "TIT"), "zechariah", "ZEC"), "zephaniah", "ZEP") def logos_map() -> Map<&2, String>: Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.set(&2, String, Map.new(&2, String), "1", "GEN"), "2", "EXO"), "3", "LEV"), "4", "NUM"), "5", "DEU"), "6", "JOS"), "7", "JDG"), "8", "RUT"), "9", "1SA"), "10", "2SA"), "11", "1KI"), "12", "2KI"), "13", "1CH"), "14", "2CH"), "15", "EZR"), "16", "NEH"), "17", "EST"), "18", "JOB"), "19", "PSA"), "20", "PRO"), "21", "ECC"), "22", "SNG"), "23", "ISA"), "24", "JER"), "25", "LAM"), "26", "EZK"), "27", "DAN"), "28", "HOS"), "29", "JOL"), "30", "AMO"), "31", "OBA"), "32", "JON"), "33", "MIC"), "34", "NAM"), "35", "HAB"), "36", "ZEP"), "37", "HAG"), "38", "ZEC"), "39", "MAL"), "40", "MAT"), "41", "MRK"), "42", "LUK"), "43", "JHN"), "44", "ACT"), "45", "ROM"), "46", "1CO"), "47", "2CO"), "48", "GAL"), "49", "EPH"), "50", "PHP"), "51", "COL"), "52", "1TH"), "53", "2TH"), "54", "1TI"), "55", "2TI"), "56", "TIT"), "57", "PHM"), "58", "HEB"), "59", "JAS"), "60", "1PE"), "61", "2PE"), "62", "1JN"), "63", "2JN"), "64", "3JN"), "65", "JUD"), "66", "REV"), "61", "MAT"), "62", "MRK"), "63", "LUK"), "64", "JHN"), "65", "ACT"), "66", "ROM"), "67", "1CO"), "68", "2CO"), "69", "GAL"), "70", "EPH"), "71", "PHP"), "72", "COL"), "73", "1TH"), "74", "2TH"), "75", "1TI"), "76", "2TI"), "77", "TIT"), "78", "PHM"), "79", "HEB"), "80", "JAS"), "81", "1PE"), "82", "2PE"), "83", "1JN"), "84", "2JN"), "85", "3JN"), "86", "JUD"), "87", "REV") def ew_lookup_go(+slug: String, hit: Bool) -> Maybe<&2, String>: match hit: case False{}: None{} case True{}: Some{unmap_str(Map.get(String, "", ew_map(), slug))} def ew_lookup(+slug: String) -> Maybe<&2, String>: ew_lookup_go(slug, unhas_str(Map.has(&2, String, ew_map(), slug))) def logos_lookup_go(+num: String, hit: Bool) -> Maybe<&2, String>: match hit: case False{}: None{} case True{}: Some{unmap_str(Map.get(String, "", logos_map(), num))} def logos_lookup(+num: String) -> Maybe<&2, String>: logos_lookup_go(num, unhas_str(Map.has(&2, String, logos_map(), num))) def char1(+c: Char) -> String: String.from_list(c <> Nil{}) def drop_n(n: Nat, s: String) -> String: match n: case 0n: s case 1n+p: match s: case SNil{}: "" case SCon{h, t}: drop_n(p, t) type AfPh is Data: AfCheck{here: Bool} AfNext{} def take_after_go(fuel: Nat, ph: AfPh, cur: String, +needle: String) -> String: match fuel: case 0n: "" case 1n+p: match ph: case AfCheck{here}: match here: case True{}: drop_n(String.length(needle), cur) case False{}: take_after_go(p, AfNext{}, cur, needle) case AfNext{}: match cur: case SNil{}: "" case SCon{h, +t}: take_after_go(p, AfCheck{String.starts_with(t, needle)}, t, needle) def take_after_substr(+hay: String, +needle: String) -> String: take_after_go( Nat.add(String.length(hay), 2n), AfCheck{String.starts_with(hay, needle)}, hay, needle ) type TuSt is Data: TuCheck{} TuDecide{stop: Bool} TuTake{} def take_until_slash_go(fuel: Nat, st: TuSt, rest: String, acc: String) -> String: match fuel: case 0n: acc case 1n+p: match st: case TuCheck{}: match rest: case SNil{}: acc case SCon{+h, +t}: take_until_slash_go(p, TuDecide{Char.is_eq(h, '/')}, rest, acc) case TuDecide{stop}: match stop: case True{}: acc case False{}: take_until_slash_go(p, TuTake{}, rest, acc) case TuTake{}: match rest: case SNil{}: acc case SCon{+h, +t}: take_until_slash_go(p, TuCheck{}, t, String.append(acc, char1(h))) def take_until_slash(+s: String) -> String: take_until_slash_go(Nat.add(Nat.mul(3n, String.length(s)), 3n), TuCheck{}, s, "") # Enduring Word slug-chapter → BOOK.CH type SpSt is Data: SpScan{} SpScanDig{h: Char, is_d: Bool} SpDigits{acc: String} SpDigClass{h: Char, acc: String, is_d: Bool, is_dash: Bool} def split_slug_ch_fin(+slug_rev: String, +ch_rev: String, ch_empty: Bool) -> Maybe<&2, String> & Maybe<&2, String>: match ch_empty: case True{}: (None{}, None{}) case False{}: (Some{String.reverse(slug_rev)}, Some{String.reverse(ch_rev)}) def split_slug_ch_go(fuel: Nat, st: SpSt, rest: String, digs: String) -> Maybe<&2, String> & Maybe<&2, String>: match fuel: case 0n: (None{}, None{}) case 1n+p: match st: case SpScan{}: match rest: case SNil{}: (None{}, None{}) case SCon{+h, +t}: split_slug_ch_go(p, SpScanDig{h, Char.is_digit(h)}, t, "") case SpScanDig{+h, is_d}: match is_d: case False{}: (None{}, None{}) case True{}: split_slug_ch_go(p, SpDigits{char1(h)}, rest, char1(h)) case SpDigits{+acc}: match rest: case SNil{}: (None{}, None{}) case SCon{+h, +t}: split_slug_ch_go(p, SpDigClass{h, acc, Char.is_digit(h), Char.is_eq(h, '-')}, t, acc) case SpDigClass{+h, +acc, is_d, is_dash}: match is_d: case True{}: split_slug_ch_go(p, SpDigits{String.append(acc, char1(h))}, rest, String.append(acc, char1(h))) case False{}: match is_dash: case True{}: split_slug_ch_fin(rest, acc, String.is_empty(acc)) case False{}: (None{}, None{}) def split_slug_ch(+s: String) -> Maybe<&2, String> & Maybe<&2, String>: split_slug_ch_go( Nat.add(Nat.mul(3n, String.length(s)), 4n), SpScan{}, String.reverse(s), "" ) def ew_combine(book_m: Maybe<&2, String>, ch_m: Maybe<&2, String>) -> Maybe<&2, String>: match book_m: case None{}: None{} case Some{+book}: match ch_m: case None{}: None{} case Some{+ch}: Some{String.append(String.append(book, "."), ch)} def ew_from_pair(pair: Maybe<&2, String> & Maybe<&2, String>) -> Maybe<&2, String>: match pair: case (slug_m, ch_m): match slug_m: case None{}: None{} case Some{+slug}: ew_combine(ew_lookup(slug), ch_m) def ew_from_path(+path: String) -> Maybe<&2, String>: ew_from_pair(split_slug_ch(take_until_slash(take_after_substr(String.to_lower(path), "bible-commentary/")))) # logos numeric BOOK.CH.V after bibleref= type TdSt is Data: TdCheck{} TdDecide{stop: Bool} TdTake{} def take_dot_go(fuel: Nat, st: TdSt, rest: String, acc: String) -> String & String: match fuel: case 0n: (acc, rest) case 1n+p: match st: case TdCheck{}: match rest: case SNil{}: (acc, "") case SCon{+h, +t}: take_dot_go(p, TdDecide{Char.is_eq(h, '.')}, rest, acc) case TdDecide{stop}: match stop: case True{}: (acc, drop_n(1n, rest)) case False{}: take_dot_go(p, TdTake{}, rest, acc) case TdTake{}: match rest: case SNil{}: (acc, "") case SCon{+h, +t}: take_dot_go(p, TdCheck{}, t, String.append(acc, char1(h))) def take_dot_field(+s: String) -> String & String: take_dot_go(Nat.add(Nat.mul(3n, String.length(s)), 3n), TdCheck{}, s, "") def logos_parse_book_go(+rest: String, +book: String, empty: Bool) -> Maybe<&2, String>: match empty: case True{}: None{} case False{}: Some{String.append(String.append(book, "."), rest)} def logos_parse_book(+rest: String, book_m: Maybe<&2, String>) -> Maybe<&2, String>: match book_m: case None{}: None{} case Some{+book}: logos_parse_book_go(rest, book, String.is_empty(rest)) def logos_parse_pair(pair: String & String) -> Maybe<&2, String>: match pair: case (book_num, rest): logos_parse_book(rest, logos_lookup(book_num)) def logos_from_path(+s: String) -> Maybe<&2, String>: logos_parse_pair(take_dot_field(take_until_slash(take_after_substr(String.to_lower(s), "bibleref=")))) def pick_nonempty_go(+a: String, +b: String, empty: Bool) -> String: match empty: case False{}: a case True{}: b def pick_nonempty(+a: String, +b: String) -> String: pick_nonempty_go(a, b, String.is_empty(a)) def maybe_str(m: Maybe<&2, String>) -> String: match m: case None{}: "" case Some{s}: s # Skip leading digits+/ after bible/ type SkSt is Data: SkCheck{} SkDecide{is_d: Bool} SkSkip{} SkSkipDig{is_d: Bool, is_slash: Bool} def skip_num_go(fuel: Nat, st: SkSt, rest: String) -> String: match fuel: case 0n: rest case 1n+p: match st: case SkCheck{}: match rest: case SNil{}: rest case SCon{+h, +t}: skip_num_go(p, SkDecide{Char.is_digit(h)}, rest) case SkDecide{is_d}: match is_d: case False{}: rest case True{}: skip_num_go(p, SkSkip{}, rest) case SkSkip{}: match rest: case SNil{}: "" case SCon{+h, +t}: skip_num_go(p, SkSkipDig{Char.is_digit(h), Char.is_eq(h, '/')}, rest) case SkSkipDig{is_d, is_slash}: match is_d: case True{}: match rest: case SNil{}: "" case SCon{h, t}: skip_num_go(p, SkSkip{}, t) case False{}: match is_slash: case True{}: match rest: case SNil{}: "" case SCon{h, t}: t case False{}: rest def bible_com_ref(+s: String) -> String: skip_num_go( Nat.add(String.length(s), 4n), SkCheck{}, take_after_substr(String.to_lower(s), "bible/") ) def route_bible_ref(+s: String) -> String: take_until_slash(take_after_substr(String.to_lower(s), "v1/p/")) def extract_share_logos_go(+s: String, +logos: String) -> String: pick_nonempty_go(logos, pick_nonempty(bible_com_ref(s), route_bible_ref(s)), String.is_empty(logos)) def extract_share_logos(+s: String) -> String: extract_share_logos_go(s, maybe_str(logos_from_path(s))) def extract_share_ref_go(+s: String, +ew: String) -> String: pick_nonempty_go(ew, extract_share_logos(s), String.is_empty(ew)) def extract_share_ref(+s: String) -> String: extract_share_ref_go(s, maybe_str(ew_from_path(s)))