summaryrefslogtreecommitdiff
path: root/src/bahnhofname.gleam
diff options
context:
space:
mode:
Diffstat (limited to 'src/bahnhofname.gleam')
-rw-r--r--src/bahnhofname.gleam117
1 files changed, 111 insertions, 6 deletions
diff --git a/src/bahnhofname.gleam b/src/bahnhofname.gleam
index 232f0f7..36ea035 100644
--- a/src/bahnhofname.gleam
+++ b/src/bahnhofname.gleam
@@ -4,14 +4,113 @@ import gleam/http.{Get}
import gleam/bit_builder.{BitBuilder}
import gleam/erlang/process
import gleam/io
+import gleam/int
import gleam/string
+import gleam/bit_string
import gleam/list
import gleam/map.{Map}
import gleam/result.{lazy_unwrap}
import gleam/uri
import gleam/hackney
+import gleam/option.{None, Some}
import mist
+fn do_distlist(
+ b: String,
+ distlist: List(Int),
+ grapheme: String,
+ new_distlist: List(Int),
+ last_dist: Int,
+) {
+ case #(b, distlist) {
+ #("", _) -> list.reverse(new_distlist)
+ #(_, [distlist_hd, distlist_snd, ..distlist_tl]) -> {
+ let assert Ok(b_hd) = string.first(b)
+ let b_tl = string.drop_left(b, up_to: 1)
+ let diff = case #(b_hd, grapheme) {
+ #(a, b) if a != b -> 1
+ _ -> 0
+ }
+ let minimum =
+ int.min(int.min(last_dist + 1, distlist_snd + 1), distlist_hd + diff)
+ do_distlist(
+ b_tl,
+ [distlist_snd, ..distlist_tl],
+ grapheme,
+ [minimum, ..new_distlist],
+ minimum,
+ )
+ }
+ }
+}
+
+fn do_distance(a: String, b: String, distlist: List(Int), step: Int) {
+ case a {
+ "" -> result.unwrap(list.last(distlist), -1)
+ _ -> {
+ let assert Ok(src_hd) = string.first(a)
+ let src_tl = string.drop_left(a, up_to: 1)
+ let distlist = do_distlist(b, distlist, src_hd, [step], step)
+ do_distance(src_tl, b, distlist, step + 1)
+ }
+ }
+}
+
+fn levenshtein(a: String, b: String) -> Int {
+ case #(a, b) {
+ #(a, b) if a == b -> 0
+ #("", b) -> string.length(b)
+ #(a, "") -> string.length(a)
+ #(a, b) -> {
+ let distlist = list.range(0, string.length(b))
+ do_distance(a, b, distlist, 1)
+ }
+ }
+}
+
+fn unpercent(encoded: String) -> String {
+ let #([head], chunks) =
+ encoded
+ |> string.split(on: "%")
+ |> list.split(at: 1)
+
+ let assert Ok(res) =
+ chunks
+ |> list.map(fn(str) {
+ case string.length(str) < 2 {
+ True -> bit_string.from_string(str)
+ False -> {
+ let assert Ok(codepoint) =
+ str
+ |> string.slice(at_index: 0, length: 2)
+ |> int.base_parse(16)
+ <<codepoint:8, string.drop_left(str, 2):utf8>>
+ }
+ }
+ })
+ |> list.prepend(bit_string.from_string(head))
+ |> bit_string.concat
+ |> bit_string.to_string
+ res
+}
+
+fn guess_station(query: String, stations: Map(String, String)) -> String {
+ query
+ stations
+ |> map.keys
+ |> list.map(fn(a) { #(levenshtein(query, a), a) })
+ |> list.fold(
+ from: #(string.length(query), query),
+ with: fn(a, b) {
+ case a.0 < b.0 {
+ True -> a
+ False -> b
+ }
+ },
+ )
+ |> fn(a: #(Int, String)) { a.1 }
+}
+
fn the_lookup(
query: String,
stations: Map(String, String),
@@ -21,7 +120,7 @@ fn the_lookup(
|> lazy_unwrap(fn() {
io.println(query)
map.get(stations, query)
- |> lazy_unwrap(fn() { "unknown" })
+ |> lazy_unwrap(fn() { guess_station(query, stations) })
})
}
@@ -32,13 +131,14 @@ fn lookup_station(
baseurl: String,
) -> Response(BitBuilder) {
let #(code, text) = case request {
- Request(method: Get, path: "/help", ..) -> #(
+ Request(method: Get, path: "/help", ..)
+ | Request(method: Get, path: "/", ..) -> #(
200,
- "ds100 → Name: " <> baseurl <> "/FF\n" <> "Name → ds100: " <> baseurl <> "/Frankfurt Hbf",
+ "ds100 → Name: " <> baseurl <> "/NN\n" <> "Name → ds100: " <> baseurl <> "/Nürnberg Hbf",
)
Request(method: Get, path: "/" <> path, ..) -> #(
200,
- the_lookup(string.replace(path, each: "%20", with: " "), stations, ds100s),
+ the_lookup(unpercent(path), stations, ds100s),
)
_ -> #(404, "intended usage is e.g. curl " <> baseurl <> "/FF")
}
@@ -49,6 +149,11 @@ fn lookup_station(
"x-data-source",
"https://data.deutschebahn.com/dataset/data-betriebsstellen.html",
)
+ |> response.prepend_header(
+ "x-sources-at",
+ "https://stuebinm.eu/git/bahnhof.name",
+ )
+ |> response.prepend_header("content-type", "text/plain; charset=utf8")
|> response.set_body(body)
}
@@ -89,8 +194,8 @@ pub fn main() {
|> list.map(fn(a) { #(a.1, a.0) })
|> map.from_list
- let assert Ok(_) = mist.run_service(
- 1234,
+ mist.run_service(
+ 2345,
fn(req) { lookup_station(req, stationmap, ds100map, baseurl) },
max_body_limit: 100,
)