aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cache.gleam97
-rw-r--r--src/database.gleam20
-rw-r--r--src/image.gleam29
-rw-r--r--src/mayu.gleam15
-rw-r--r--src/request.gleam19
-rw-r--r--src/svg.gleam20
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()
}