🐙 Templated web page generator for your git repositories
git clone https://github.com/m-col/gitja
Files | Refs | Readme | License

Commit 33e1a764b90ab2413989896c072e1c3e63a83308
Parent: 5983ebce640f9cba7f9d19cf47ab9e6ab1921263
Author: mcol <mcol@posteo.net>
Date: 2021-12-01 21:09:50 +0000
Committer: mcol <mcol@posteo.net>
Committed: 2021-12-01 21:18:25 +0000

Expose whether a file is considered binary by git

Closes #14

test/test.sh Modified

@@ -33,11 +33,13 @@
     "index.html"
     "link.html"
     "style.css"
+    "so_called_binary_file"
     "static/a_nice_file"
     "gitserve/index.html"
     "gitserve/commit/0292014748caae952bbc8dd6225680d83c0a5135.html"
     "gitserve/file/test.expected.gitserve.file.html"
     "gitserve/file/test.templates.style.css.html"
+    "gitserve/file/test.templates.so_called_binary_file.html"
 )
 
 stack run -- -c test/config.dhall -q || exit 1

test/templates/style.css Modified

@@ -1 +1 @@
-👄
+html

test/templates/so_called_binary_file Added

@@ -0,0 +1 @@
+👄

test/templates/repo/file.html Modified

@@ -6,7 +6,8 @@
 file: {{ file }}
 file.path: {{ file.path }}
 file.href: {{ file.href }}
-file.contents: {{ file.contents }}
+file.contents: {% if file.is_binary %}(binary){% else %}{{ file.contents }}{% endif %}
+
 file.mode: {{ file.mode }}
 file.mode_octal: {{ file.mode_octal }}
 file.mode_symbolic: {{ file.mode_symbolic }}

test/expected/style.css Modified

@@ -1 +1 @@
-👄
+html

test/expected/so_called_binary_file Added

@@ -0,0 +1 @@
+👄

test/expected/gitserve/file/test.templates.style.css.html Modified

@@ -3,7 +3,7 @@
 file: test/templates/style.css
 file.path: test/templates/style.css
 file.href: test.templates.style.css.html
-file.contents: 👄
+file.contents: html
 file.mode: Plain
 file.mode_octal: 00644
 file.mode_symbolic: -rw-r--r--

test/expected/gitserve/file/test.templates.so_called_binary_file.html Added

@@ -0,0 +1,22 @@
+test/templates/so_called_binary_file
+scope: file
+file: test/templates/so_called_binary_file
+file.path: test/templates/so_called_binary_file
+file.href: test.templates.so_called_binary_file.html
+file.contents: (binary)
+file.mode: Plain
+file.mode_octal: 00644
+file.mode_symbolic: -rw-r--r--
+if file.is_directory then "directory" else "file": "file"
+
+file scope also should have data from repo scope available:
+host: https://github.com/m-col/gitserve
+repos: gitserve
+name: gitserve
+description: 🐙 Templated web page generator for your git repositories
+commits: (skipped)
+tree: (skipped)
+tags: 1
+branches: 1
+readme.path: README.rst
+license.path: LICENSE

test/expected/gitserve/file/test.expected.gitserve.file.html Modified

@@ -3,7 +3,7 @@
 file: test/expected/gitserve/file
 file.path: test/expected/gitserve/file
 file.href: test.expected.gitserve.file.html
-file.contents: [&quot;test/expected/gitserve/file/test.expected.gitserve.file.html&quot;,&quot;test/expected/gitserve/file/test.templates.style.css.html&quot;]
+file.contents: [&quot;test/expected/gitserve/file/test.expected.gitserve.file.html&quot;,&quot;test/expected/gitserve/file/test.templates.so_called_binary_file.html&quot;,&quot;test/expected/gitserve/file/test.templates.style.css.html&quot;]
 file.mode: Directory
 file.mode_octal: 40000
 file.mode_symbolic: drwxr-xr-x

templates/stagit/repo/file.html Modified

@@ -29,11 +29,13 @@
     </tbody>
   </table>
 {% else %}
-  <pre id="blob">
-{% for line in split(file.contents, '\n') %}
+  {% if file.is_binary %}
+  (File is binary)
+  {% else %}
+<pre id="blob">{% for line in split(file.contents, '\n') %}
 <a href="#l{{ loop.index }}" class="line" id="l{{ loop.index }}">{{ printf("%7v", loop.index) }}</a> {{ line }}
-{% endfor %}
+{% endfor %}</pre>
+  {% endif %}
 {% endif %}
-  </pre>
 </div>
 {% endblock %}

templates/docs/repo/file.html Modified

@@ -24,10 +24,12 @@
   </tbody>
 </table>
 {% else %}
-  <pre id="blob">
-{% for line in split(file.contents, '\n') %}
+  {% if file.is_binary %}
+  (File is binary)
+  {% else %}
+<pre id="blob">{% for line in split(file.contents, '\n') %}
 <a href="#l{{ loop.index }}" class="line" id="l{{ loop.index }}">{{ printf("%5v", loop.index) }}</a>  {{ line }}
-{% endfor %}
+{% endfor %}</pre>
+  {% endif %}
 {% endif %}
-</pre>
 {% endblock %}

templates/base/repo/file.html Modified

@@ -24,10 +24,12 @@
   </tbody>
 </table>
 {% else %}
-  <pre>
-{% for line in split(file.contents, '\n') %}
+  {% if file.is_binary %}
+  (File is binary)
+  {% else %}
+<pre>{% for line in split(file.contents, '\n') %}
 <a href="#l{{ loop.index }}" class="line" id="l{{ loop.index }}">{{ printf("%5v", loop.index) }}</a>  {{ line }}
-{% endfor %}
+{% endfor %}</pre>
+{% endif %}
 {% endif %}
-</pre>
 {% endblock %}

src/Types.hs Modified

@@ -10,6 +10,10 @@
 
 module Types where
 
+import Bindings.Libgit2.Blob (c'git_blob_is_binary, c'git_blob_lookup)
+import Control.Monad (when)
+import Control.Monad.Catch (throwM)
+import Control.Monad.IO.Class (liftIO)
 import Control.Monad.Trans.Reader (ReaderT)
 import Data.ByteString.UTF8 (toString)
 import Data.Default (def)
@@ -19,8 +23,10 @@
 import qualified Data.Text as T
 import Data.Text.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
+import Foreign (peek)
+import Foreign.ForeignPtr (mallocForeignPtr, withForeignPtr)
 import Git
-import Git.Libgit2 (LgRepo)
+import Git.Libgit2 (LgRepo, getOid, repoObj)
 import Path (Abs, Dir, Path, dirname, toFilePath)
 import System.FilePath (takeFileName)
 import Text.Ginger.GVal (GVal, ToGVal, asBoolean, asHtml, asList, asLookup, asText, toGVal)
@@ -106,7 +112,7 @@
     , treeFileMode :: TreeEntryMode
     }
 
-data TreeFileContents = FileContents Text | FolderContents [TreeFile]
+data TreeFileContents = BinaryContents | FileContents Text | FolderContents [TreeFile]
 
 data TreeEntryMode = ModeDirectory | ModePlain | ModeExecutable | ModeSymlink | ModeSubmodule
     deriving stock (Show)
@@ -136,6 +142,24 @@
 modeToSymbolic ModeSubmodule = "git-module"
 
 {-
+This
+-}
+getBlobContents :: BlobOid LgRepo -> ReaderT LgRepo IO TreeFileContents
+getBlobContents oid = do
+    repo <- getRepository
+    blobPtr <- liftIO mallocForeignPtr
+    isBinary <- liftIO . withForeignPtr (repoObj repo) $ \repoPtr ->
+        withForeignPtr blobPtr $ \blobPtr' ->
+            withForeignPtr (getOid . untag $ oid) $ \oidPtr -> do
+                r1 <- c'git_blob_lookup blobPtr' repoPtr oidPtr
+                when (r1 < 0) $ throwM (Git.BackendError "Could not lookup blob")
+                c'git_blob_is_binary =<< peek blobPtr'
+
+    if toEnum . fromEnum $ isBinary -- This reads weird, but it goes CInt, to Int, to Bool.
+        then return BinaryContents
+        else FileContents . decodeUtf8With lenientDecode <$> catBlob oid
+
+{-
 GVal implementations for data definitions above, allowing commits to be rendered in
 Ginger templates.
 -}
@@ -151,6 +175,7 @@
 
 instance ToGVal RunRepo TreeFileContents where
     toGVal :: TreeFileContents -> GVal RunRepo
+    toGVal BinaryContents = def
     toGVal (FileContents text) = toGVal . strip $ text
     toGVal (FolderContents treeFiles) =
         def
@@ -168,13 +193,19 @@
     "mode" -> Just . toGVal . drop 4 . show . treeFileMode $ treefile
     "mode_octal" -> Just . toGVal . modeToOctal . treeFileMode $ treefile
     "mode_symbolic" -> Just . toGVal . modeToSymbolic . treeFileMode $ treefile
+    "is_binary" -> Just . toGVal . treeFileIsBinary $ treefile
     "is_directory" -> Just . toGVal . treeFileIsDirectory $ treefile
     _ -> Nothing
 
+treeFileIsBinary :: TreeFile -> Bool
+treeFileIsBinary treefile = case treeFileContents treefile of
+    BinaryContents -> True
+    _ -> False
+
 treeFileIsDirectory :: TreeFile -> Bool
 treeFileIsDirectory treefile = case treeFileContents treefile of
-    FileContents _ -> False
     FolderContents _ -> True
+    _ -> False
 
 {-
 Get the name of a tree file path's HTML file.

src/Repositories.hs Modified

@@ -163,7 +163,7 @@
 prependParent parent (path, entry) = (mconcat [parent, "/", path], entry)
 
 getEntryContents :: (TreeFilePath, TreeEntry LgRepo) -> ReaderT LgRepo IO TreeFileContents
-getEntryContents (_, BlobEntry oid _) = FileContents . decodeUtf8With lenientDecode <$> catBlob oid
+getEntryContents (_, BlobEntry oid _) = getBlobContents oid
 getEntryContents (path, TreeEntry oid) = FolderContents <$> getTree' path oid
 getEntryContents (_, CommitEntry oid) = return . FileContents . pack . show . untag $ oid
 

gitserve.cabal Modified

@@ -25,12 +25,14 @@
                      , data-default
                      , dhall
                      , directory
+                     , exceptions
                      , extra
                      , file-embed
                      , filepath
                      , ginger >= 0.10.4.0
                      , gitlib
                      , gitlib-libgit2
+                     , hlibgit2
                      , optparse-applicative
                      , path
                      , path-io >= 1.3.0

DOCUMENTATION.md Modified

@@ -181,6 +181,7 @@
 |            | mode\_octal      | Mode in octal form e.g. "00644" for plain files.         |
 |            | mode\_symbolic   | Mode in symbolic form e.g. ""-rw-r--r--" for plain files.|
 |            | is\_directory    | A boolean, useful for ginger conditionals.               |
+|            | is\_binary       | A boolean, tells you if the contents can be rendered.    |
 | ref        | name             | The tag or branch name.                                  |
 |            | commit           | The commit pointed to by the tag or branch.              |