diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bahnhofname.gleam | 229 |
1 files changed, 132 insertions, 97 deletions
diff --git a/src/bahnhofname.gleam b/src/bahnhofname.gleam index 725d0ae..0612587 100644 --- a/src/bahnhofname.gleam +++ b/src/bahnhofname.gleam @@ -1,7 +1,7 @@ import gleam/http/response.{Response} -import gleam/http/request.{Request,get_header} +import gleam/http/request.{Request, get_header} import gleam/http.{Get} -import gleam/bit_builder.{BitBuilder} +import gleam/bit_builder import gleam/erlang/process import gleam/erlang/atom import gleam/erlang/file @@ -18,53 +18,69 @@ import gleam/result import mist const ds100_domain = "ds100.bahnhof.name" + const ril100_domain = "ril100.bahnhof.name" + const leitpunkt_domain = "leitpunkt.bahnhof.name" + const domain = "bahnhof.name" + const proto = "https://" -external type Index -external type Field +type Index + +type Field + +@external(erlang, "Elixir.Haystack.Index", "new") +fn index_new(a: atom.Atom) -> Index + +@external(erlang, "Elixir.Haystack.Index", "ref") +fn index_ref(a: Index, b: Field) -> Index + +@external(erlang, "Elixir.Haystack.Index", "field") +fn index_field(a: Index, b: Field) -> Index + +@external(erlang, "Elixir.Haystack.Index.Field", "term") +fn field_term(a: String) -> Field + +@external(erlang, "Elixir.Haystack.Index.Field", "new") +fn field_new(a: String) -> Field + +@external(erlang, "Elixir.Haystack.Index", "add") +fn index_add(a: Index, b: List(a)) -> Index -external fn index_new(atom.Atom) -> Index = - "Elixir.Haystack.Index" "new" +@external(erlang, "Elixir.IO", "inspect") +pub fn inspect(a: a) -> a -external fn index_ref(Index, Field) -> Index = - "Elixir.Haystack.Index" "ref" +type Query -external fn index_field(Index, Field) -> Index = - "Elixir.Haystack.Index" "field" +type Clause -external fn field_term(String) -> Field = - "Elixir.Haystack.Index.Field" "term" +type Expression -external fn field_new(String) -> Field = - "Elixir.Haystack.Index.Field" "new" +@external(erlang, "Elixir.Haystack.Query", "new") +fn query_new() -> Query -external fn index_add(Index, List(a)) -> Index = - "Elixir.Haystack.Index" "add" +@external(erlang, "Elixir.Haystack.Query", "clause") +fn query_clause(a: Query, b: Clause) -> Query -pub external fn inspect(a) -> a = - "Elixir.IO" "inspect" +@external(erlang, "Elixir.Haystack.Query", "run") +fn query_run(a: Query, b: Index) -> List(Map(atom.Atom, String)) -external type Query -external type Clause -external type Expression -external fn query_new() -> Query = - "Elixir.Haystack.Query" "new" -external fn query_clause(Query, Clause) -> Query = - "Elixir.Haystack.Query" "clause" -external fn query_run(Query, Index) -> List(Map(atom.Atom, String)) = - "Elixir.Haystack.Query" "run" -external fn clause_new(atom.Atom) -> Clause = - "Elixir.Haystack.Query.Clause" "new" -external fn query_expressions(Clause, List(Expression)) -> Clause = - "Elixir.Haystack.Query.Clause" "expressions" -external fn query_expression_new(atom.Atom, List(#(atom.Atom, String))) -> Expression = - "Elixir.Haystack.Query.Expression" "new" +@external(erlang, "Elixir.Haystack.Query.Clause", "new") +fn clause_new(a: atom.Atom) -> Clause -external fn tokenize(String) -> List(Map(atom.Atom, String)) = - "Elixir.Haystack.Tokenizer" "tokenize" +@external(erlang, "Elixir.Haystack.Query.Clause", "expressions") +fn query_expressions(a: Clause, b: List(Expression)) -> Clause + +@external(erlang, "Elixir.Haystack.Query.Expression", "new") +fn query_expression_new( + a: atom.Atom, + b: List(#(atom.Atom, String)), +) -> Expression + +@external(erlang, "Elixir.Haystack.Tokenizer", "tokenize") +fn tokenize(a: String) -> List(Map(atom.Atom, String)) type IdKind { DS100 @@ -100,7 +116,7 @@ fn unpercent(encoded: String) -> String { |> list.prepend(bit_string.from_string(head)) |> bit_string.concat |> bit_string.to_string - |> result.map(fn (str) { string.replace(str, "_", " ") }) + |> result.map(fn(str) { string.replace(str, "_", " ") }) res } @@ -116,7 +132,7 @@ fn lookup_exact(query: String, lookup: Map(String, String)) -> #(Int, String) { fn lookup_fuzzy( query: String, kind: IdKind, - fuzzy: fn(String, IdKind) -> Matched(String) + fuzzy: fn(String, IdKind) -> Matched(String), ) -> #(Int, String) { case fuzzy(query, kind) { Exact(res) -> #(200, res) @@ -125,7 +141,7 @@ fn lookup_fuzzy( } } -fn if_not(res: #(Int,t), fallback: fn() -> #(Int,t)) -> #(Int,t) { +fn if_not(res: #(Int, t), fallback: fn() -> #(Int, t)) -> #(Int, t) { inspect(case res { #(200, _) -> res _ -> fallback() @@ -136,43 +152,43 @@ fn lookup_station( request: Request(t), ds100_to_name: Map(String, String), leitpunkt_to_name: Map(String, String), - fuzzy: fn (String, IdKind) -> Matched(String) -) -> Response(BitBuilder) { + fuzzy: fn(String, IdKind) -> Matched(String), +) -> Response(mist.ResponseData) { let #(code, text) = case request { // blackhole favicon.ico requests instead of using the index Request(method: Get, path: "/favicon.ico", ..) -> #(404, "") Request(method: Get, path: "/help", ..) | Request(method: Get, path: "/", ..) -> #( 200, - "ril100 → Name: " <> proto<>ril100_domain<>"/HG\n" <> - "Name → ril100: " <> proto<>ril100_domain <> "/Göttingen\n\n" <> - "Leitpunkt → Name: " <> proto<>leitpunkt_domain<>"/GOE\n" <> - "Name → Leitpunkt: " <> proto<>leitpunkt_domain <> "/Göttingen\n\n"<> - "Fuzzy:" <> proto<>domain<>"/...", + "ril100 → Name: " <> proto <> ril100_domain <> "/HG\n" <> "Name → ril100: " <> proto <> ril100_domain <> "/Göttingen\n\n" <> "Leitpunkt → Name: " <> proto <> leitpunkt_domain <> "/GOE\n" <> "Name → Leitpunkt: " <> proto <> leitpunkt_domain <> "/Göttingen\n\n" <> "Fuzzy:" <> proto <> domain <> "/...", ) Request(method: Get, path: "/" <> path, ..) -> { let query = unpercent(path) case get_header(request, "x-forwarded-host") { - Ok(domain) if domain == leitpunkt_domain -> query + Ok(domain) if domain == leitpunkt_domain -> + query |> lookup_exact(leitpunkt_to_name) - |> if_not(fn() {lookup_fuzzy(query,Leitpunkt,fuzzy)}) - Ok(domain) if domain == ril100_domain || domain == ds100_domain -> query + |> if_not(fn() { lookup_fuzzy(query, Leitpunkt, fuzzy) }) + Ok(domain) if domain == ril100_domain || domain == ds100_domain -> + query |> lookup_exact(ds100_to_name) - |> if_not(fn() {lookup_fuzzy(query,DS100, fuzzy)}) + |> if_not(fn() { lookup_fuzzy(query, DS100, fuzzy) }) _ -> { let by_ds100 = lookup_exact(query, ds100_to_name) let by_lp = lookup_exact(query, leitpunkt_to_name) case #(by_ds100.0, by_lp.0) { - #(200, _) -> #(302, proto<>ril100_domain<>"/"<>path) - #(_, 200) -> #(302, proto<>leitpunkt_domain<>"/"<>path) - _ -> #(302, proto<>ril100_domain<>"/"<>path) + #(200, _) -> #(302, proto <> ril100_domain <> "/" <> path) + #(_, 200) -> #(302, proto <> leitpunkt_domain <> "/" <> path) + _ -> #(302, proto <> ril100_domain <> "/" <> path) } } } } - _ -> #(404, "intended usage is e.g. curl " <> proto<>domain<>"/FF") + _ -> #(404, "intended usage is e.g. curl " <> proto <> domain <> "/FF") } - let body = bit_builder.from_string(text) + let body = text + |> bit_builder.from_string + |> mist.Bytes response.new(code) |> response.prepend_header( @@ -184,17 +200,20 @@ fn lookup_station( "https://stuebinm.eu/git/bahnhof.name", ) |> response.prepend_header("content-type", "text/plain; charset=utf8") - |> fn (a) { case code == 302 { - True -> response.prepend_header(a, "location", text) - _ -> a - } } + |> fn(a) { + case code == 302 { + True -> response.prepend_header(a, "location", text) + _ -> a + } + } |> response.set_body(body) } pub fn main() { let assert Ok(bahn_ril100) = fetch_data() - let ds100s = read_csv(bahn_ril100) + let ds100s = + read_csv(bahn_ril100) |> list.filter_map(fn(fields) { case fields { [_, ds100, name, ..] -> Ok(#(name, ds100)) @@ -215,22 +234,31 @@ pub fn main() { let name_to_leitpunkt = map.from_list(leitpunkte) let ds100_to_name = map.from_list(list.map(ds100s, swap)) let leitpunkt_to_name = map.from_list(list.map(leitpunkte, swap)) - let ds100index = index_new(atom.create_from_string("ds100")) + let ds100index = + index_new(atom.create_from_string("ds100")) |> index_ref(field_term("id")) |> index_field(field_new("name")) - |> index_add(ds100s - |> list.map(fn(tuple) {case tuple { - #(name, ds100) - -> map.from_list([#("id", ds100), #("name", name)] - )}})) - let leitpunkt_index = index_new(atom.create_from_string("leitpunkt")) + |> index_add( + ds100s + |> list.map(fn(tuple) { + case tuple { + #(name, ds100) -> map.from_list([#("id", ds100), #("name", name)]) + } + }), + ) + let leitpunkt_index = + index_new(atom.create_from_string("leitpunkt")) |> index_ref(field_term("id")) |> index_field(field_new("name")) - |> index_add(leitpunkte - |> list.map(fn(tuple) {case tuple { - #(name, leitpunkt) - -> map.from_list([#("id", leitpunkt), #("name", name)] - )}})) + |> index_add( + leitpunkte + |> list.map(fn(tuple) { + case tuple { + #(name, leitpunkt) -> + map.from_list([#("id", leitpunkt), #("name", name)]) + } + }), + ) let ref = atom.create_from_string("ref") let fuzzy = fn(searchterm: String, kind: IdKind) -> List(String) { @@ -242,26 +270,36 @@ pub fn main() { let match = atom.create_from_string("match") let field = atom.create_from_string("field") let term = atom.create_from_string("term") - let expressions = tokenize(inspect(searchterm)) - |> list.filter_map(fn (a) { map.get(a, atom.create_from_string("v")) }) - |> list.map(fn (token) { query_expression_new(match, [#(field, "name"), #(term, token)]) }) - let clause = query_expressions(clause_new(atom.create_from_string("all")), expressions) + let expressions = + tokenize(inspect(searchterm)) + |> list.filter_map(fn(a) { map.get(a, atom.create_from_string("v")) }) + |> list.map(fn(token) { + query_expression_new(match, [#(field, "name"), #(term, token)]) + }) + let clause = + query_expressions(clause_new(atom.create_from_string("all")), expressions) let query = query_clause(query, clause) - let matches = query_run(query, index) - |> list.filter_map(fn (a) { map.get(a, ref) }) + let matches = + query_run(query, index) + |> list.filter_map(fn(a) { map.get(a, ref) }) inspect(matches) case list.length(matches) > 5 { True -> { let query = query_new() - let clause = query_expressions( - clause_new(atom.create_from_string("all")), - [query_expression_new(match, [#(field, "name"), #(term, "hbf")]) , ..expressions] - ) + let clause = + query_expressions( + clause_new(atom.create_from_string("all")), + [ + query_expression_new(match, [#(field, "name"), #(term, "hbf")]), + ..expressions + ], + ) let query = query_clause(query, clause) - let narrow = query_run(query, index) - |> list.filter_map(fn (a) { map.get(a, ref) }) + let narrow = + query_run(query, index) + |> list.filter_map(fn(a) { map.get(a, ref) }) case narrow { [] -> matches _ -> narrow @@ -279,10 +317,9 @@ pub fn main() { case map.get(stations, searchterm) { Ok(id) -> Exact(id) _ -> { - let results = fuzzy(searchterm, kind) - |> list.filter_map(fn (res) { - map.get(ids, string.uppercase(res)) - }) + let results = + fuzzy(searchterm, kind) + |> list.filter_map(fn(res) { map.get(ids, string.uppercase(res)) }) case results { [res] -> Fuzzy(res) [res, ..] -> Fuzzy(res) @@ -294,16 +331,14 @@ pub fn main() { io.println("compiled indices, starting server …") - let _ = mist.run_service( - 2345, - fn(req) { lookup_station( - req, - ds100_to_name, - leitpunkt_to_name, - exact_then_fuzzy - ) }, - max_body_limit: 100, - ) + let assert Ok(_) = + fn(req: Request(mist.Connection)) -> Response(mist.ResponseData) { + lookup_station(req, ds100_to_name, leitpunkt_to_name, exact_then_fuzzy) + } + |> mist.new + |> mist.port(2345) + |> mist.start_http + process.sleep_forever() } |