diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cache.gleam | 97 | ||||
| -rw-r--r-- | src/database.gleam | 20 | ||||
| -rw-r--r-- | src/image.gleam | 29 | ||||
| -rw-r--r-- | src/mayu.gleam | 15 | ||||
| -rw-r--r-- | src/request.gleam | 19 | ||||
| -rw-r--r-- | src/svg.gleam | 20 |
6 files changed, 86 insertions, 114 deletions
diff --git a/src/cache.gleam b/src/cache.gleam index ccba2a8..7e4ff21 100644 --- a/src/cache.gleam +++ b/src/cache.gleam @@ -4,6 +4,7 @@ import gleam/int import gleam/list import gleam/option.{type Option} import gleam/result +import gleam/string import image import simplifile import wisp @@ -27,53 +28,61 @@ pub fn load_themes() { }, dict.new(), fn(accumulated_themes, theme) { - dict.insert( - accumulated_themes, - theme, - list.range(0, 9) - |> list.fold(dict.new(), fn(accumulated_digits, digit) { - let path = - "./themes/" - <> theme - <> "/" - <> int.to_string(digit) - <> "." - <> case theme { - "gelbooru-h" | "moebooru-h" | "lain" | "garukura" -> "png" - _ -> "gif" - } + dict.insert(accumulated_themes, theme, load_theme(theme)) + }, + ) +} - case simplifile.read_bits(from: path) { - Ok(image_data) -> { - case image.get_image_information(image_data) { - Ok(info) -> - dict.insert( - accumulated_digits, - digit, - CachedImage( - base64: bit_array.base64_encode(image_data, False), - info: info, - ), - ) - Error(_) -> { - wisp.log_error( - "Error getting image information for " <> path, - ) +fn load_theme(theme) -> Dict(Int, CachedImage) { + let theme_directory = "./themes/" <> theme - accumulated_digits - } - } - } - Error(_) -> { - wisp.log_error("Error reading image file " <> path) + case simplifile.read_directory(theme_directory) { + Ok(files) -> + list.fold(files, dict.new(), fn(accumulated_digits, file) { + case parse_digit_filename(file) { + Ok(digit) -> + load_cached_image(theme_directory <> "/" <> file) + |> result.map(dict.insert(accumulated_digits, digit, _)) + |> result.unwrap(accumulated_digits) + Error(_) -> accumulated_digits + } + }) + Error(_) -> { + wisp.log_error("Error reading theme directory " <> theme_directory) - accumulated_digits - } - } - }), - ) - }, - ) + dict.new() + } + } +} + +fn parse_digit_filename(file) { + case string.split(file, ".") { + [digit, _extension] -> int.parse(digit) + _ -> Error(Nil) + } +} + +fn load_cached_image(path) { + case simplifile.read_bits(from: path) { + Ok(image_data) -> + case image.get_image_information(image_data) { + Ok(info) -> + Ok(CachedImage( + base64: bit_array.base64_encode(image_data, False), + info: info, + )) + Error(_) -> { + wisp.log_error("Error getting image information for " <> path) + + Error(Nil) + } + } + Error(_) -> { + wisp.log_error("Error reading image file " <> path) + + Error(Nil) + } + } } pub fn get_image(cache, theme, digit) -> Option(CachedImage) { diff --git a/src/database.gleam b/src/database.gleam index 07264fd..053bc28 100644 --- a/src/database.gleam +++ b/src/database.gleam @@ -8,11 +8,9 @@ pub type Counter { } pub fn setup(connection) { - let _ = + let assert Ok(_) = sqlight.exec( - "pragma foreign_keys = off; - - create table if not exists tb_count ( + "create table if not exists tb_count ( id integer primary key autoincrement not null unique, name text not null unique, num int not null default (0) @@ -64,18 +62,8 @@ pub fn get_counter(connection, name) { option.unwrap(row.2, ""), option.unwrap(row.3, ""), )) - Ok([]) -> { - wisp.log_error("Database query returned no rows unexpectedly.") - - Error("Unreachable entity") - } - Ok([_, _, ..]) -> { - wisp.log_error("Database query returned multiple rows unexpectedly.") - - Error("Unreachable entity") - } - Error(_) -> { - wisp.log_error("Database query failed.") + _ -> { + wisp.log_error("Database query failed or returned unexpected rows.") Error("Database operation failed") } diff --git a/src/image.gleam b/src/image.gleam index 93fe300..0ce82ea 100644 --- a/src/image.gleam +++ b/src/image.gleam @@ -4,8 +4,17 @@ pub type ImageInformation { pub fn get_image_information(image) { case image { - <<0x89, "PNG\r\n":utf8, 0x1A, "\n":utf8, _rest:bits>> -> - parse_png_chunks(image, 8) + << + 0x89, + "PNG\r\n":utf8, + 0x1A, + "\n":utf8, + _length:32, + "IHDR":utf8, + width:32, + height:32, + _rest:bits, + >> -> Ok(ImageInformation(width, height, "png")) << "GIF":utf8, _version:bytes-3, @@ -16,19 +25,3 @@ pub fn get_image_information(image) { _ -> Error("Unsupported image format") } } - -fn parse_png_chunks(image, offset) { - case image { - << - _:unit(8)-size(offset), - _length:32, - "IHDR":utf8, - width:32, - height:32, - _rest:bits, - >> -> Ok(ImageInformation(width, height, "png")) - <<_:size(offset), length:32, _:4, _:bits>> -> - parse_png_chunks(image, offset + length + 12) - _ -> Error("Invalid PNG chunk") - } -} diff --git a/src/mayu.gleam b/src/mayu.gleam index b204dc0..bb05b77 100644 --- a/src/mayu.gleam +++ b/src/mayu.gleam @@ -1,6 +1,8 @@ import cache import database +import envoy import gleam/erlang/process +import gleam/string import mist import request import simplifile @@ -12,14 +14,13 @@ pub fn main() { let _ = simplifile.create_directory("./data") let image_cache = cache.load_themes() - let index_html = case simplifile.read("index.html") { - Ok(content) -> content - Error(_) -> { - wisp.log_error("Failed to read index.html") - - "" - } + let version_tag = case envoy.get("MAYU_VERSION") { + Ok(version) -> "(v" <> version <> ")" + Error(_) -> "" } + let assert Ok(index_html_source) = simplifile.read("index.html") + let index_html = + string.replace(index_html_source, "{{ MAYU_VERSION }}", version_tag) use connection <- sqlight.with_connection("./data/count.db") diff --git a/src/request.gleam b/src/request.gleam index 5ef74c1..a56780b 100644 --- a/src/request.gleam +++ b/src/request.gleam @@ -1,9 +1,7 @@ import database -import envoy import gleam/int import gleam/json import gleam/list -import gleam/string import gleam/string_builder import svg import wisp @@ -25,20 +23,7 @@ pub fn handle(request, connection, image_cache, index_html) { [] -> case index_html { "" -> wisp.not_found() - content -> - wisp.html_response( - string_builder.from_string( - string.replace( - content, - "{{ MAYU_VERSION }}", - case envoy.get("MAYU_VERSION") { - Ok(version) -> "(v" <> version <> ")" - Error(_) -> "" - }, - ), - ), - 200, - ) + content -> wisp.html_response(string_builder.from_string(content), 200) } ["heart-beat"] -> wisp.html_response(string_builder.from_string("alive"), 200) @@ -60,7 +45,7 @@ pub fn handle(request, connection, image_cache, index_html) { case list.key_find(query, "padding") { Ok(padding) -> case int.parse(padding) { - Ok(n) -> n + Ok(n) -> int.clamp(n, min: 0, max: 32) Error(_) -> 6 } _ -> 6 diff --git a/src/svg.gleam b/src/svg.gleam index 0178dbd..4014910 100644 --- a/src/svg.gleam +++ b/src/svg.gleam @@ -2,15 +2,15 @@ import cache import gleam/int import gleam/list import gleam/option.{Some} -import gleam/string_builder +import gleam/string_builder.{type StringBuilder} import image type XmlImages { - XmlImages(xml: String, width: Int, height: Int) + XmlImages(xml: StringBuilder, width: Int, height: Int) } -fn image(base64, image: image.ImageInformation, width) { - string_builder.new() +fn append_image(svgs, base64, image: image.ImageInformation, width) { + svgs |> string_builder.append("<image height=\"") |> string_builder.append(int.to_string(image.height)) |> string_builder.append("\" width=\"") @@ -22,12 +22,11 @@ fn image(base64, image: image.ImageInformation, width) { |> string_builder.append(";base64,") |> string_builder.append(base64) |> string_builder.append("\"/>") - |> string_builder.to_string() } fn images(image_cache, theme, digits, width, height, svgs) { case digits { - [] -> XmlImages(string_builder.to_string(svgs), width, height) + [] -> XmlImages(svgs, width, height) [digit, ..rest] -> case cache.get_image(image_cache, theme, digit) { Some(cached) -> @@ -37,10 +36,7 @@ fn images(image_cache, theme, digits, width, height, svgs) { rest, width + cached.info.width, int.max(height, cached.info.height), - string_builder.append( - svgs, - image(cached.base64, cached.info, width), - ), + append_image(svgs, cached.base64, cached.info, width), ) _ -> images(image_cache, theme, rest, width, height, svgs) } @@ -53,7 +49,7 @@ pub fn xml(image_cache, theme, number, padding) { image_cache, theme, { - let assert Ok(digits) = int.digits(number, 10) + let assert Ok(digits) = int.digits(int.absolute_value(number), 10) let digits_padding = padding - list.length(digits) case digits_padding { @@ -78,7 +74,7 @@ pub fn xml(image_cache, theme, number, padding) { |> string_builder.append( "\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><title>Mayu</title><g>", ) - |> string_builder.append(xml.xml) + |> string_builder.append_builder(xml.xml) |> string_builder.append("</g></svg>") |> string_builder.to_string() } |