Compare commits
17
Commits
747518600c
..
0.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a4c1f17b7 | ||
|
|
3d104ef50c | ||
|
|
976fbada7e | ||
|
|
0d59ded2ec | ||
|
|
ab667742da | ||
|
|
647b5c2ad1 | ||
|
|
57173d14dd | ||
|
|
fde68ee833 | ||
|
|
610c819483 | ||
|
|
3fd4177cd4 | ||
|
|
9881b44fa2 | ||
|
|
36f9bb390d | ||
|
|
f5fd1089b1 | ||
|
|
5bdf69ef91 | ||
|
|
da2540c3fa | ||
|
|
d5cd8905bb | ||
|
|
66ad565cba |
@@ -1,3 +1,4 @@
|
|||||||
.stack-work/
|
.stack-work/
|
||||||
.vscode/
|
.vscode/
|
||||||
*.swp
|
*.swp
|
||||||
|
result
|
||||||
|
|||||||
@@ -1 +1,21 @@
|
|||||||
# utoy
|
# utoy
|
||||||
|
|
||||||
|
## Building the executable
|
||||||
|
|
||||||
|
```
|
||||||
|
$ $(nix-build utoy.nix)/bin/utoy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building the Docker image
|
||||||
|
|
||||||
|
```
|
||||||
|
$ docker load < $(nix-build nix/docker-image.nix)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Shell
|
||||||
|
|
||||||
|
Includes Stack, `haskell-language-server`, `gen-hie` etc.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ nix-shell
|
||||||
|
```
|
||||||
|
|||||||
+94
-30
@@ -61,28 +61,35 @@ app :: Application
|
|||||||
app = serve (Proxy :: Proxy API) server
|
app = serve (Proxy :: Proxy API) server
|
||||||
|
|
||||||
type API =
|
type API =
|
||||||
"bytes" :> Capture "bytes" Text :> Get '[PlainText, HTML] BytesModel
|
"utf8" :> Capture "bytes" Text :> Get '[PlainText, HTML] Utf8Model
|
||||||
:<|> "codepoints" :> Capture "codepoints" Text :> Get '[PlainText] CodepointsModel
|
:<|> "codepoints" :> Capture "codepoints" Text :> Get '[PlainText, HTML] CodepointsModel
|
||||||
|
:<|> "text" :> Capture "text" Text :> Get '[PlainText, HTML] TextModel
|
||||||
|
|
||||||
server :: Server API
|
server :: Server API
|
||||||
server =
|
server =
|
||||||
bytesR :<|> codepointsR
|
utf8R :<|> codepointsR :<|> textR
|
||||||
where
|
where
|
||||||
bytesR bytesP = do
|
utf8R bytesP = do
|
||||||
bytes <- Parsers.parseHexBytes bytesP `orThrow` const err400
|
bytes <- Parsers.parseHexBytes bytesP `orThrow` const err400
|
||||||
pure $ BytesModel $ Decode.decodeUtf8 bytes
|
pure $ mkUtf8Model bytes
|
||||||
|
|
||||||
codepointsR codepointsP = do
|
codepointsR codepointsP = do
|
||||||
codepoints' <- Parsers.parseCodepoints codepointsP `orThrow` const err400
|
codepoints' <- Parsers.parseCodepoints codepointsP `orThrow` const err400
|
||||||
pure $ mkCodepointsModel codepoints'
|
pure $ mkCodepointsModel codepoints'
|
||||||
|
|
||||||
|
textR textP = do
|
||||||
|
pure $ TextModel textP
|
||||||
|
|
||||||
-- /bytes/<bytes>
|
-- /bytes/<bytes>
|
||||||
|
|
||||||
newtype BytesModel = BytesModel
|
newtype Utf8Model = Utf8Model
|
||||||
{ codepoints :: [([Word8], Either String Char)]
|
{ codepoints :: [([Word8], Either String Char)]
|
||||||
}
|
}
|
||||||
|
|
||||||
instance MimeRender PlainText BytesModel where
|
mkUtf8Model :: [Word8] -> Utf8Model
|
||||||
|
mkUtf8Model = Utf8Model . Decode.decodeUtf8
|
||||||
|
|
||||||
|
instance MimeRender PlainText Utf8Model where
|
||||||
mimeRender _ model = renderText $
|
mimeRender _ model = renderText $
|
||||||
Table.render " " $ concat
|
Table.render " " $ concat
|
||||||
[ [ [ Table.cl $ Text.pack $ unwords $ map showByteHex bytes
|
[ [ [ Table.cl $ Text.pack $ unwords $ map showByteHex bytes
|
||||||
@@ -95,7 +102,7 @@ instance MimeRender PlainText BytesModel where
|
|||||||
Right c ->
|
Right c ->
|
||||||
[ Text.pack [c]
|
[ Text.pack [c]
|
||||||
, Text.pack $ printf "U+%04X" c
|
, Text.pack $ printf "U+%04X" c
|
||||||
, Text.pack $ intercalate ", " $ maybeToList (UnicodeNames.name c) ++ map (++ "*") (UnicodeNames.nameAliases c)
|
, Text.pack $ intercalate ", " $ allNames c
|
||||||
, Text.pack $ fromMaybe "" $ blockName c
|
, Text.pack $ fromMaybe "" $ blockName c
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -103,25 +110,19 @@ instance MimeRender PlainText BytesModel where
|
|||||||
| (bytes, eiC) <- model.codepoints
|
| (bytes, eiC) <- model.codepoints
|
||||||
]
|
]
|
||||||
|
|
||||||
instance MimeRender HTML BytesModel where
|
instance MimeRender HTML Utf8Model where
|
||||||
mimeRender _ model = renderHtml $ H.docTypeHtml $ do
|
mimeRender _ model = renderHtml $ documentWithBody $ do
|
||||||
H.head $ do
|
H.table $ for_ model.codepoints $ \(bytes, eiC) -> do
|
||||||
H.meta ! A.charset "utf-8"
|
H.tr $ do
|
||||||
H.title "utoy"
|
H.td $ H.pre $ H.toHtml $ unlines $ map unwords [map showByteHex bytes, map showByteBin bytes]
|
||||||
H.style $ H.toHtml $ Encoding.decodeUtf8 $(embedFile "utoy.css")
|
case eiC of
|
||||||
H.body $ do
|
Left err ->
|
||||||
H.table $ for_ model.codepoints $ \(bytes, eiC) -> do
|
H.td ! A.colspan "4" $ H.toHtml $ "Decoding error: " ++ err
|
||||||
H.tr $ do
|
Right c -> do
|
||||||
H.td $ H.pre $ H.toHtml $ unlines $ map unwords [map showByteHex bytes, map showByteBin bytes]
|
H.td $ H.input ! A.class_ "charbox" ! A.value (H.toValue [c])
|
||||||
case eiC of
|
H.td $ H.code $ printfHtml "U+%04X" c
|
||||||
Left err ->
|
H.td $ H.code $ H.toHtml $ intercalate ", " $ allNames c
|
||||||
H.td ! A.colspan "4" $ H.toHtml $ "Decoding error: " ++ err
|
H.td $ H.code $ H.toHtml $ fromMaybe "" $ blockName c
|
||||||
Right c -> do
|
|
||||||
H.td $ do
|
|
||||||
H.input ! A.value (H.toValue [c]) ! A.style "text-align: center; width: 2em; font-size: 1em;"
|
|
||||||
H.td $ H.code $ printfHtml "U+%04X" c
|
|
||||||
H.td $ H.code $ H.toHtml $ intercalate ", " $ maybeToList (UnicodeNames.name c) ++ map (++ "*") (UnicodeNames.nameAliases c)
|
|
||||||
H.td $ H.toHtml $ fromMaybe "" $ blockName c
|
|
||||||
|
|
||||||
-- /codepoints/<codepoints>
|
-- /codepoints/<codepoints>
|
||||||
|
|
||||||
@@ -130,7 +131,14 @@ newtype CodepointsModel = CodepointsModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
mkCodepointsModel :: [(Word, Word)] -> CodepointsModel
|
mkCodepointsModel :: [(Word, Word)] -> CodepointsModel
|
||||||
mkCodepointsModel = CodepointsModel . map go . concatMap (uncurry enumFromTo)
|
mkCodepointsModel =
|
||||||
|
CodepointsModel
|
||||||
|
-- Limit number of returned codepoints. Otherwise it's
|
||||||
|
-- too easy to provoke massive response bodies with requests like
|
||||||
|
-- /codepoints/0-99999999
|
||||||
|
. take 100000
|
||||||
|
. map go
|
||||||
|
. concatMap (uncurry enumFromTo)
|
||||||
where
|
where
|
||||||
go codepoint = (codepoint, toChar codepoint)
|
go codepoint = (codepoint, toChar codepoint)
|
||||||
|
|
||||||
@@ -151,13 +159,54 @@ instance MimeRender PlainText CodepointsModel where
|
|||||||
Right c ->
|
Right c ->
|
||||||
[ Text.pack [c]
|
[ Text.pack [c]
|
||||||
, Text.pack $ printf "U+%04X" c
|
, Text.pack $ printf "U+%04X" c
|
||||||
, Text.pack $ intercalate ", " $ maybeToList (UnicodeNames.name c) ++ map (++ "*") (UnicodeNames.nameAliases c)
|
, Text.pack $ intercalate ", " $ allNames c
|
||||||
, Text.pack $ fromMaybe "" $ blockName c
|
, Text.pack $ fromMaybe "" $ blockName c
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
| (codepoint, eiC) <- model.codepoints
|
| (codepoint, eiC) <- model.codepoints
|
||||||
]
|
]
|
||||||
|
|
||||||
|
instance MimeRender HTML CodepointsModel where
|
||||||
|
mimeRender _ model = renderHtml $ documentWithBody $ do
|
||||||
|
H.table $ for_ model.codepoints $ \(codepoint, eiC) ->
|
||||||
|
H.tr $ do
|
||||||
|
H.td $ H.code $ H.toHtml $ Text.pack $ printf "0x%X" codepoint
|
||||||
|
case eiC of
|
||||||
|
Left err -> do
|
||||||
|
H.td ! A.colspan "4" $ H.code $ H.toHtml $ "Decoding error: " <> Text.pack err
|
||||||
|
Right c -> do
|
||||||
|
H.td $ H.input ! A.class_ "charbox" ! A.value (H.toValue [c])
|
||||||
|
H.td $ H.code $ printfHtml "U+%04X" c
|
||||||
|
H.td $ H.code $ H.toHtml $ intercalate ", " $ allNames c
|
||||||
|
H.td $ H.code $ H.toHtml $ fromMaybe "" $ blockName c
|
||||||
|
|
||||||
|
-- /text/<text>
|
||||||
|
|
||||||
|
newtype TextModel = TextModel
|
||||||
|
{ text :: Text
|
||||||
|
}
|
||||||
|
|
||||||
|
instance MimeRender HTML TextModel where
|
||||||
|
mimeRender _ model = renderHtml $ documentWithBody $ do
|
||||||
|
H.table $ for_ (Text.unpack model.text) $ \c -> do
|
||||||
|
H.tr $ do
|
||||||
|
H.td $ H.input ! A.class_ "charbox" ! A.value (H.toValue [c])
|
||||||
|
H.td $ H.code $ printfHtml "U+%04X" c
|
||||||
|
H.td $ H.code $ H.toHtml $ intercalate ", " $ allNames c
|
||||||
|
H.td $ H.code $ H.toHtml $ fromMaybe "" $ blockName c
|
||||||
|
|
||||||
|
instance MimeRender PlainText TextModel where
|
||||||
|
mimeRender _ model = renderText $ Table.render " "
|
||||||
|
[ map (Table.cl)
|
||||||
|
[ Text.pack [c]
|
||||||
|
, Text.pack $ printf "U+%04X" c
|
||||||
|
, Text.pack $ intercalate ", " $ allNames c
|
||||||
|
, Text.pack $ fromMaybe "" $ blockName c
|
||||||
|
]
|
||||||
|
| c <- Text.unpack model.text
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
-- Utilities
|
-- Utilities
|
||||||
|
|
||||||
renderText :: Text -> BL.ByteString
|
renderText :: Text -> BL.ByteString
|
||||||
@@ -167,7 +216,13 @@ showByteHex :: Word8 -> String
|
|||||||
showByteHex = printf " %02X"
|
showByteHex = printf " %02X"
|
||||||
|
|
||||||
showByteBin :: Word8 -> String
|
showByteBin :: Word8 -> String
|
||||||
showByteBin = printf "%8b"
|
showByteBin = printf "%08b"
|
||||||
|
|
||||||
|
-- | Retrieve name and aliases (suffixed with @*@) of a 'Char'.
|
||||||
|
allNames :: Char -> [String]
|
||||||
|
allNames c =
|
||||||
|
maybeToList (UnicodeNames.name c)
|
||||||
|
++ map (++ "*") (UnicodeNames.nameAliases c)
|
||||||
|
|
||||||
blockName :: Char -> Maybe String
|
blockName :: Char -> Maybe String
|
||||||
blockName c = UnicodeBlocks.blockName . UnicodeBlocks.blockDefinition <$> UnicodeBlocks.block c
|
blockName c = UnicodeBlocks.blockName . UnicodeBlocks.blockDefinition <$> UnicodeBlocks.block c
|
||||||
@@ -179,6 +234,15 @@ orThrow (Right val) _ = pure val
|
|||||||
printfHtml :: PrintfArg a => String -> a -> H.Html
|
printfHtml :: PrintfArg a => String -> a -> H.Html
|
||||||
printfHtml fmt = (H.toHtml :: String -> H.Html) . printf fmt
|
printfHtml fmt = (H.toHtml :: String -> H.Html) . printf fmt
|
||||||
|
|
||||||
|
documentWithBody :: H.Html -> H.Html
|
||||||
|
documentWithBody body =
|
||||||
|
H.docTypeHtml $ do
|
||||||
|
H.head $ do
|
||||||
|
H.meta ! A.charset "utf-8"
|
||||||
|
H.title "utoy"
|
||||||
|
H.style $ H.toHtml $ Encoding.decodeUtf8 $(embedFile "static/utoy.css")
|
||||||
|
H.body body
|
||||||
|
|
||||||
-- HTML routes
|
-- HTML routes
|
||||||
|
|
||||||
data HTML
|
data HTML
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ cradle:
|
|||||||
component: "utoy:lib"
|
component: "utoy:lib"
|
||||||
|
|
||||||
- path: "./app/Main.hs"
|
- path: "./app/Main.hs"
|
||||||
component: "utoy:exe:utoy-exe"
|
component: "utoy:exe:utoy"
|
||||||
|
|
||||||
- path: "./test"
|
- path: "./test"
|
||||||
component: "utoy:test:utoy-test"
|
component: "utoy:test:utoy-test"
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
let
|
||||||
|
pkgs = import ./pkgs.nix {};
|
||||||
|
utoy = import ../utoy.nix;
|
||||||
|
in
|
||||||
|
pkgs.dockerTools.buildImage {
|
||||||
|
name = "git.pbrinkmeier.de/paul/utoy";
|
||||||
|
tag = utoy.version;
|
||||||
|
config.Cmd = [ "${utoy}/bin/utoy" ];
|
||||||
|
}
|
||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
# Adapted from new-template.hsfiles
|
# Adapted from new-template.hsfiles
|
||||||
|
|
||||||
name: utoy
|
name: utoy
|
||||||
version: 0.1.0.0
|
version: 0.5
|
||||||
git: "https://git.pbrinkmeier.de/paul/utoy"
|
git: "https://git.pbrinkmeier.de/paul/utoy"
|
||||||
license: MIT
|
license: MIT
|
||||||
author: "Paul Brinkmeier"
|
author: "Paul Brinkmeier"
|
||||||
@@ -10,7 +10,7 @@ copyright: "2023 Paul Brinkmeier"
|
|||||||
|
|
||||||
extra-source-files:
|
extra-source-files:
|
||||||
- README.md
|
- README.md
|
||||||
- utoy.css
|
- static/utoy.css
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
- base >= 4.7 && < 5
|
- base >= 4.7 && < 5
|
||||||
@@ -32,7 +32,7 @@ library:
|
|||||||
source-dirs: src
|
source-dirs: src
|
||||||
|
|
||||||
executables:
|
executables:
|
||||||
utoy-exe:
|
utoy:
|
||||||
main: Main.hs
|
main: Main.hs
|
||||||
source-dirs: app
|
source-dirs: app
|
||||||
ghc-options:
|
ghc-options:
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ cr :: Text -> Cell
|
|||||||
cr = C AlignRight
|
cr = C AlignRight
|
||||||
|
|
||||||
render :: Text -> [[Cell]] -> Text
|
render :: Text -> [[Cell]] -> Text
|
||||||
render delim cells = Text.intercalate "\n" $ map showRow cells
|
render delim cells = Text.unlines $ map showRow cells
|
||||||
where
|
where
|
||||||
showRow = Text.intercalate delim . map showCell . zipLongest columnWidths
|
showRow = Text.intercalate delim . map showCell . zipLongest columnWidths
|
||||||
|
|
||||||
|
|||||||
@@ -19,3 +19,9 @@ pre, code {
|
|||||||
pre {
|
pre {
|
||||||
margin: 0; font-size: 0.5em;
|
margin: 0; font-size: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.charbox {
|
||||||
|
text-align: center;
|
||||||
|
width: 2em;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
+3
-3
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
|||||||
-- see: https://github.com/sol/hpack
|
-- see: https://github.com/sol/hpack
|
||||||
|
|
||||||
name: utoy
|
name: utoy
|
||||||
version: 0.1.0.0
|
version: 0.5
|
||||||
author: Paul Brinkmeier
|
author: Paul Brinkmeier
|
||||||
maintainer: hallo@pbrinkmeier.de
|
maintainer: hallo@pbrinkmeier.de
|
||||||
copyright: 2023 Paul Brinkmeier
|
copyright: 2023 Paul Brinkmeier
|
||||||
@@ -14,7 +14,7 @@ license-file: LICENSE
|
|||||||
build-type: Simple
|
build-type: Simple
|
||||||
extra-source-files:
|
extra-source-files:
|
||||||
README.md
|
README.md
|
||||||
utoy.css
|
static/utoy.css
|
||||||
|
|
||||||
source-repository head
|
source-repository head
|
||||||
type: git
|
type: git
|
||||||
@@ -36,7 +36,7 @@ library
|
|||||||
, text
|
, text
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
executable utoy-exe
|
executable utoy
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
hs-source-dirs:
|
hs-source-dirs:
|
||||||
app
|
app
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
let
|
||||||
|
pkgs = import ./nix/pkgs.nix {};
|
||||||
|
settings = import ./nix/settings.nix;
|
||||||
|
haskellDeps = import ./nix/haskell-deps.nix;
|
||||||
|
|
||||||
|
haskellPackages = pkgs.haskell.packages."${settings.ghc}";
|
||||||
|
|
||||||
|
utoy =
|
||||||
|
{ mkDerivation }:
|
||||||
|
mkDerivation {
|
||||||
|
version = "0.5";
|
||||||
|
pname = "utoy";
|
||||||
|
license = pkgs.lib.licenses.mit;
|
||||||
|
src =
|
||||||
|
let
|
||||||
|
buildFiles = [
|
||||||
|
./LICENSE
|
||||||
|
./utoy.cabal
|
||||||
|
./Setup.hs
|
||||||
|
./app
|
||||||
|
./src
|
||||||
|
./static
|
||||||
|
./test
|
||||||
|
];
|
||||||
|
in
|
||||||
|
pkgs.lib.sources.cleanSourceWith {
|
||||||
|
src = ./.;
|
||||||
|
filter = path: _type: pkgs.lib.any (prefix: pkgs.lib.hasPrefix (toString prefix) path) buildFiles;
|
||||||
|
};
|
||||||
|
|
||||||
|
libraryHaskellDepends = haskellDeps haskellPackages;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
pkgs.haskell.lib.justStaticExecutables
|
||||||
|
(haskellPackages.callPackage utoy {})
|
||||||
Reference in New Issue
Block a user