diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..f592a4b
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,27 @@
+name: Build & cache
+on: [push]
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: cachix/install-nix-action@v31
+ with:
+ extra_nix_config: |
+ trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
+ substituters = https://cache.iog.io/ https://cache.nixos.org/
+ allow-import-from-derivation = true
+ accept-flake-config = true
+ download-buffer-size = 524288000
+ nix_path: nixpkgs=channel:nixos-unstable
+ - uses: cachix/cachix-action@v15
+ with:
+ name: inguncache
+ authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
+ - run: |
+ # nix run "github:input-output-hk/haskell.nix#hix" -- init
+ # echo "------ Content of flake.nix ------"
+ # cat flake.nix
+ # echo "----- Content of nix/hix.nix -----"
+ # cat nix/hix.nix
+ nix build .#answers-script:exe:answers-script
diff --git a/.gitignore b/.gitignore
index df50a24..189a004 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,85 +1,3 @@
-.cabal-sandbox/
-cabal.sandbox.config
-cabal.project.local
-.ghc.environment.*
-cabal-dev/
-.hpc/
-*.hi
-*.o
-*.p_hi
-*.prof
-*.tix
-dist
-dist-*
-register.sh
-./cabal.config
-cabal-tests.log
-bootstrap/*.plan.json
-
-/Cabal/dist/
-/Cabal/tests/Setup
-/Cabal/Setup
-/Cabal/source-file-list
-
-/cabal-install/dist/
-/cabal-install/Setup
-/cabal-install/source-file-list
-
-.stylish-haskell.yaml
-.stylish-haskell.yml
-.ghci
-.ghcid
-
-# Output of release and bootstrap
-_build
-
-# editor temp files
-
-*#
-.#*
-*~
-.*.swp
-*.bak
-
-# GHC build
-
-Cabal/GNUmakefile
-Cabal/dist-boot/
-Cabal/dist-install/
-Cabal/ghc.mk
-
-
-# TAGS files
-TAGS
-tags
-ctags
-
-# stack artifacts
-/.stack-work/
-stack.yaml.lock
-
-# Shake artifacts
-.shake*
-progress.txt
-
-# test files
-register.sh
-
-# windows test artifacts
-cabal-testsuite/**/*.exe
-cabal-testsuite/**/*.bat
-
-# python artifacts from documentation builds
-*.pyc
-.python-sphinx-virtualenv/
-/doc/.skjold_cache/
-
-# macOS folder metadata
-.DS_Store
-
-# benchmarks
-bench.html
-
-# Emacs
-.projectile
-/test/dst/
\ No newline at end of file
+/dist-newstyle
+/test/dst
+/cabal.project.freeze
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..d2f848d
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,4 @@
+[submodule "test/answers-db"]
+ path = test/answers-db
+ url = https://github.com/ingun37/answers-db.git
+ branch = test-data
diff --git a/ChangeLog.md b/CHANGELOG.md
similarity index 100%
rename from ChangeLog.md
rename to CHANGELOG.md
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e48f0a9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Copyright (c) 2025, Ingun Jon
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
index 93334bf..00dfce4 100644
--- a/README.md
+++ b/README.md
@@ -1,45 +1,72 @@
-# Answers Script
+# Build MacOS
-## Generate default.nix
+```sh
+# for building gitlib packages
+brew install pkgconf
+brew install icu4c
+brew install openssl@3
-```shell
-nix-shell -p cabal2nix
-cabal2nix --no-check ./. > default.nix
-```
-
-## Generate shell.nix
+# /usr/local/opt/openssl is hard coded in the gitlib build setting.
+ln -s $(brew --prefix openssl@3)/3.5.2 /usr/local/opt/openssl
-```shell
-nix-shell -p cabal2nix
-cabal2nix --shell --no-check ./. > shell.nix
+echo "export PKG_CONFIG_PATH=\"$(brew --prefix)/opt/icu4c/lib/pkgconfig\"" >> ~/.zprofile
```
-## Build
+```sh
+cabal build
+```
-**Don't just build using Cabal!!** it will take forever because of Pandoc.
+# Data flow
-Build in Nix environment
+```mermaid
+flowchart LR
+ n1["src"] --> n2(("directory-tree
readDirectoryWithL"))
+ n3["myReader"] --> n2
+ n2 --> n4["AnchoredDirTree FileType"]
+ n4 --> n5(("directory-tree
filterDir"))
+ n5 --> n8(("unfoldTree"))
+ n9["myUnfolder"] --> n8
+ n8 --> n10["Tree Item"]
+ n11["myFilter"] --> n5
+ n10 --> n12(("recurse"))
+ n13["myWriter"] --> n12
+ n12 --> n14["[Effect]"]
-```shell
-# Enter Nix environment defined in shell.nix
-nix-shell
-# Use executable
-answers-script ...
+ n1@{ shape: rect}
+ n9@{ shape: rect}
```
-## Test
+# CI/CD
-```shell
-nix-shell
-cabal --enable-nix test
+Check the sha1 of gitlib like this
+
+```sh
+nix-shell -p nix-prefetch-git
+nix-prefetch-git https://github.com/jwiegley/gitlib.git bf256617179d853bdbc12e9283b3f570ebb9d9d7 --fetch-submodules
```
-## Install from other machines
+Output is like
-```shell
-TAR="https://github.com/ingun37/answers-script/archive/refs/tags/1.0.1.tar.gz"
-# sandboxing
-nix-shell -p "with import {}; let f = import (fetchTarball $TAR); in haskellPackages.callPackage f {}"
-# no sandboxing
-nix-env --install -E "with import {}; let f = import (fetchTarball $TAR); in _: (haskellPackages.callPackage f {})"
```
+git revision is bf256617179d853bdbc12e9283b3f570ebb9d9d7
+path is /nix/store/63bx4k5nwjqwk7gv0a0k8adq796bjbpr-gitlib-bf25661
+git human-readable version is -- none --
+Commit date is 2025-09-04 11:17:27 -0700
+hash is 13k3aymqwzpcijnjjka820nv6rkgakzbvh13glw98p1c4yhqwcbf
+{
+ "url": "https://github.com/jwiegley/gitlib.git",
+ "rev": "bf256617179d853bdbc12e9283b3f570ebb9d9d7",
+ "date": "2025-09-04T11:17:27-07:00",
+ "path": "/nix/store/63bx4k5nwjqwk7gv0a0k8adq796bjbpr-gitlib-bf25661",
+ "sha256": "13k3aymqwzpcijnjjka820nv6rkgakzbvh13glw98p1c4yhqwcbf",
+ "hash": "sha256-bjGOoScsXJQ4fSPAvf5Ub2azLRBITSmtjOx+jqtXY44=",
+ "fetchLFS": false,
+ "fetchSubmodules": true,
+ "deepClone": false,
+ "fetchTags": false,
+ "leaveDotGit": false,
+ "rootDir": ""
+}
+```
+
+Use the "sha256" for the `--sha256` field in the `cabal.project`.
\ No newline at end of file
diff --git a/answers-script.cabal b/answers-script.cabal
index 2817267..fdd1c02 100644
--- a/answers-script.cabal
+++ b/answers-script.cabal
@@ -1,6 +1,26 @@
-cabal-version: 3.0
+cabal-version: 3.4
+-- The cabal-version field refers to the version of the .cabal specification,
+-- and can be different from the cabal-install (the tool) version and the
+-- Cabal (the library) version you are using. As such, the Cabal (the library)
+-- version used must be equal or greater than the version stated in this field.
+-- Starting from the specification version 2.2, the cabal-version field must be
+-- the first thing in the cabal file.
+
+-- Initial package description 'answers-script' generated by
+-- 'cabal init'. For further documentation, see:
+-- http://haskell.org/cabal/users-guide/
+--
+-- The name of the package.
name: answers-script
-version: 0.1.0.0
+
+-- The package version.
+-- See the Haskell package versioning policy (PVP) for standards
+-- guiding when and how versions should be incremented.
+-- https://pvp.haskell.org
+-- PVP summary: +-+------- breaking API changes
+-- | | +----- non-breaking API additions
+-- | | | +--- code changes with no API change
+version: 1.0.0.0
-- A short (one-line) description of the package.
-- synopsis:
@@ -8,48 +28,76 @@ version: 0.1.0.0
-- A longer description of the package.
-- description:
--- A URL where users can report bugs.
--- bug-reports:
-
-- The license under which the package is released.
--- license:
+license: BSD-3-Clause
+
+-- The file containing the license text.
+license-file: LICENSE
+
+-- The package author(s).
author: Ingun Jon
+
+-- An email address to which users can send suggestions, bug reports, and patches.
maintainer: ingun37@gmail.com
-- A copyright notice.
-- copyright:
--- category:
-extra-source-files: CHANGELOG.md
+build-type: Simple
+
+-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README.
+extra-doc-files: CHANGELOG.md
+
+-- Extra source files to be distributed with the package, such as examples, or a tutorial module.
+-- extra-source-files:
+
+common warnings
+ ghc-options: -Wall
library
- exposed-modules: Lib, MyGit
+ -- Import common warning flags.
+ import: warnings
+
+ -- Modules exported by the library.
+ exposed-modules: MyLib, MyGit, MyMark
-- Modules included in this library but not exported.
- -- other-modules:
+ -- other-modules: MyGit
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
- build-depends:
- base ^>=4.16.4.0,
- aeson,
- hlibgit2,
- containers,
- lens,
- cryptohash-sha1,
- directory,
+
+ -- Other library packages from which modules are imported.
+ build-depends:
+ base ^>=4.21.0.0,
directory-tree,
- pandoc,
- pcre-heavy,
- pcre-light,
filepath,
+ lens,
text,
+ cmark,
+ containers,
+ directory,
+ cryptohash-sha1,
bytestring,
base16-bytestring,
- aeson-pretty
+ gitlib,
+ gitlib-libgit2,
+ time,
+ tagged,
+ transformers,
+ monad-loops,
+ aeson
+
+ -- Directories containing source files.
hs-source-dirs: src
- default-language: Haskell2010
-
+
+ -- Base language which the package is written in.
+ default-language: GHC2024
+
executable answers-script
+ -- Import common warning flags.
+ import: warnings
+
+ -- .hs or .lhs file containing the Main module.
main-is: Main.hs
-- Modules included in this executable, other than Main.
@@ -57,25 +105,44 @@ executable answers-script
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
+
+ -- Other library packages from which modules are imported.
build-depends:
- base ^>=4.16.4.0,
+ base ^>=4.21.0.0,
answers-script,
optparse-applicative
+ -- Directories containing source files.
hs-source-dirs: app
- default-language: Haskell2010
-Test-Suite test
+ -- Base language which the package is written in.
+ default-language: GHC2024
+
+test-suite answers-script-test
+ -- Import common warning flags.
+ import: warnings
+
+ -- Base language which the package is written in.
+ default-language: GHC2024
+
+ -- Modules included in this executable, other than Main.
+ -- other-modules:
+
+ -- LANGUAGE extensions used by modules in this package.
+ -- other-extensions:
+
+ -- The interface type and version of the test suite.
type: exitcode-stdio-1.0
- main-is: Spec.hs
+
+ -- Directories containing source files.
hs-source-dirs: test
- default-language: Haskell2010
+
+ -- The entrypoint to the test suite.
+ main-is: Main.hs
+
+ -- Test dependencies.
build-depends:
- base ^>=4.16.4.0,
+ base ^>=4.21.0.0,
answers-script,
- hspec,
- QuickCheck,
- directory,
filepath,
- bytestring,
- directory-tree
\ No newline at end of file
+ containers
\ No newline at end of file
diff --git a/app/Main.hs b/app/Main.hs
index 50bbe09..3242a69 100644
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -1,10 +1,10 @@
module Main where
-import Lib (someFunc)
+import MyLib (someFunc)
import Options.Applicative
data Sample = Sample
- { prefixPath :: String,
+ { prefix :: String,
src :: String,
dst :: String
}
@@ -13,7 +13,7 @@ sample :: Parser Sample
sample =
Sample
<$> strOption
- ( long "prefixpath"
+ ( long "prefix"
<> value ""
<> help "prefix path of webserver"
)
@@ -27,7 +27,9 @@ sample =
)
greet :: Sample -> IO ()
-greet (Sample prefixPath src dst) = someFunc prefixPath src dst
+greet (Sample prefix src dst) = do
+ _ <- someFunc prefix src dst
+ return ()
main :: IO ()
main = greet =<< execParser opts
@@ -38,4 +40,4 @@ main = greet =<< execParser opts
( fullDesc
<> progDesc "import answers-db into answers static asset"
<> header "what is header?"
- )
+ )
\ No newline at end of file
diff --git a/cabal.project b/cabal.project
new file mode 100644
index 0000000..d0964f4
--- /dev/null
+++ b/cabal.project
@@ -0,0 +1,22 @@
+packages: .
+
+source-repository-package
+ type: git
+ location: https://github.com/jwiegley/gitlib.git
+ subdir: gitlib
+ tag: bf256617179d853bdbc12e9283b3f570ebb9d9d7
+ --sha256: 13k3aymqwzpcijnjjka820nv6rkgakzbvh13glw98p1c4yhqwcbf
+
+source-repository-package
+ type: git
+ location: https://github.com/jwiegley/gitlib.git
+ subdir: gitlib-libgit2
+ tag: bf256617179d853bdbc12e9283b3f570ebb9d9d7
+ --sha256: 13k3aymqwzpcijnjjka820nv6rkgakzbvh13glw98p1c4yhqwcbf
+
+source-repository-package
+ type: git
+ location: https://github.com/jwiegley/gitlib.git
+ subdir: hlibgit2
+ tag: bf256617179d853bdbc12e9283b3f570ebb9d9d7
+ --sha256: 13k3aymqwzpcijnjjka820nv6rkgakzbvh13glw98p1c4yhqwcbf
\ No newline at end of file
diff --git a/default.nix b/default.nix
deleted file mode 100644
index b248047..0000000
--- a/default.nix
+++ /dev/null
@@ -1,25 +0,0 @@
-{ mkDerivation, aeson, aeson-pretty, base, base16-bytestring
-, bytestring, containers, cryptohash-sha1, directory
-, directory-tree, filepath, hlibgit2, hspec, lens, lib
-, optparse-applicative, pandoc, pcre-heavy, pcre-light, QuickCheck
-, text
-}:
-mkDerivation {
- pname = "answers-script";
- version = "0.1.0.0";
- src = ./.;
- isLibrary = true;
- isExecutable = true;
- libraryHaskellDepends = [
- aeson aeson-pretty base base16-bytestring bytestring containers
- cryptohash-sha1 directory directory-tree filepath hlibgit2 lens
- pandoc pcre-heavy pcre-light text
- ];
- executableHaskellDepends = [ base optparse-applicative ];
- testHaskellDepends = [
- base bytestring directory directory-tree filepath hspec QuickCheck
- ];
- doCheck = false;
- license = "unknown";
- mainProgram = "answers-script";
-}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..e046ecd
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,41 @@
+{
+ # This is a template created by `hix init`
+ inputs.haskellNix.url = "github:input-output-hk/haskell.nix";
+ inputs.nixpkgs.follows = "haskellNix/nixpkgs-unstable";
+ inputs.flake-utils.url = "github:numtide/flake-utils";
+ outputs = { self, nixpkgs, flake-utils, haskellNix }:
+ let
+ supportedSystems = [
+ "x86_64-linux"
+ # "x86_64-darwin"
+ # "aarch64-linux"
+ # "aarch64-darwin"
+ ];
+ in
+ flake-utils.lib.eachSystem supportedSystems (system:
+ let
+ overlays = [ haskellNix.overlay
+ (final: _prev: {
+ hixProject =
+ final.haskell-nix.hix.project {
+ src = ./.;
+ # uncomment with your current system for `nix flake show` to work:
+ #evalSystem = "x86_64-linux";
+ };
+ })
+ ];
+ pkgs = import nixpkgs { inherit system overlays; inherit (haskellNix) config; };
+ flake = pkgs.hixProject.flake {};
+ in flake // {
+ legacyPackages = pkgs;
+ });
+ # --- Flake Local Nix Configuration ----------------------------
+ nixConfig = {
+ # This sets the flake to use the IOG nix cache.
+ # Nix should ask for permission before using it,
+ # but remove it here if you do not want it to.
+ extra-substituters = ["https://cache.iog.io"];
+ extra-trusted-public-keys = ["hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="];
+ allow-import-from-derivation = "true";
+ };
+}
\ No newline at end of file
diff --git a/nix/hix.nix b/nix/hix.nix
new file mode 100644
index 0000000..bf6637e
--- /dev/null
+++ b/nix/hix.nix
@@ -0,0 +1,15 @@
+{pkgs, ...}: {
+ # name = "project-name";
+ compiler-nix-name = "ghc912"; # Version of GHC to use
+ # Cross compilation support:
+ # crossPlatforms = p: pkgs.lib.optionals pkgs.stdenv.hostPlatform.isx86_64 ([
+ # p.mingwW64
+ # p.ghcjs
+ # ] ++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux [
+ # p.musl64
+ # ]);
+ # Tools to include in the development shell
+ shell.tools.cabal = "latest";
+ # shell.tools.hlint = "latest";
+ # shell.tools.haskell-language-server = "latest";
+}
\ No newline at end of file
diff --git a/shell.nix b/shell.nix
deleted file mode 100644
index 0ee0bd5..0000000
--- a/shell.nix
+++ /dev/null
@@ -1,43 +0,0 @@
-{ nixpkgs ? import {}, compiler ? "default", doBenchmark ? false }:
-
-let
-
- inherit (nixpkgs) pkgs;
-
- f = { mkDerivation, aeson, aeson-pretty, base, base16-bytestring
- , bytestring, containers, cryptohash-sha1, directory
- , directory-tree, filepath, hlibgit2, hspec, lens, lib
- , optparse-applicative, pandoc, pcre-heavy, pcre-light, QuickCheck
- , text
- }:
- mkDerivation {
- pname = "answers-script";
- version = "0.1.0.0";
- src = ./.;
- isLibrary = true;
- isExecutable = true;
- libraryHaskellDepends = [
- aeson aeson-pretty base base16-bytestring bytestring containers
- cryptohash-sha1 directory directory-tree filepath hlibgit2 lens
- pandoc pcre-heavy pcre-light text
- ];
- executableHaskellDepends = [ base optparse-applicative ];
- testHaskellDepends = [
- base bytestring directory directory-tree filepath hspec QuickCheck
- ];
- doCheck = false;
- license = "unknown";
- mainProgram = "answers-script";
- };
-
- haskellPackages = if compiler == "default"
- then pkgs.haskellPackages
- else pkgs.haskell.packages.${compiler};
-
- variant = if doBenchmark then pkgs.haskell.lib.doBenchmark else pkgs.lib.id;
-
- drv = variant (haskellPackages.callPackage f {});
-
-in
-
- if pkgs.lib.inNixShell then drv.env else drv
diff --git a/src/Lib.hs b/src/Lib.hs
deleted file mode 100644
index 781b259..0000000
--- a/src/Lib.hs
+++ /dev/null
@@ -1,198 +0,0 @@
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedStrings #-}
-
-module Lib
- ( someFunc,
- )
-where
-
-import Control.Lens (over, (^.))
-import Control.Monad (forM, when, (<=<))
-import qualified Crypto.Hash.SHA1 as SHA (hash)
-import Data.Aeson (ToJSON)
-import Data.Aeson.Encode.Pretty (encodePretty)
-import Data.ByteString (readFile)
-import qualified Data.ByteString.Base16 as B16 (encode)
-import qualified Data.ByteString.Char8 as C8 (pack, unpack)
-import Data.ByteString.Lazy (writeFile)
-import Data.Map (Map, fromList, keys, lookup, mapKeys)
-import Data.Monoid (Sum (Sum), getSum)
-import Data.Text (Text, pack, strip, unpack)
-import Data.Text.Encoding (decodeUtf8)
-import Data.Tree (Tree (Node), flatten, rootLabel)
-import GHC.Generics (Generic)
-import MyGit (creationTime)
-import System.Directory
- ( canonicalizePath,
- copyFile,
- createDirectoryIfMissing,
- removePathForcibly,
- )
-import System.Directory.Tree
- ( DirTree (Dir, File),
- filterDir,
- readDirectoryWithL,
- _dirTree,
- )
-import System.FilePath
- ( FilePath,
- dropFileName,
- isExtensionOf,
- joinPath,
- makeRelative,
- splitDirectories,
- takeBaseName,
- takeExtension,
- takeFileName,
- (-<.>),
- (>),
- )
-import qualified System.FilePath as FilePath
-import System.FilePath.Posix (joinPath, (>))
-import Text.Pandoc
- ( Extension (Ext_pipe_tables, Ext_tex_math_double_backslash),
- HTMLMathMethod (KaTeX),
- def,
- defaultKaTeXURL,
- extensionsFromList,
- githubMarkdownExtensions,
- readMarkdown,
- readerExtensions,
- runIOorExplode,
- writeHtml5String,
- writerHTMLMathMethod,
- )
-import Text.Regex.PCRE.Heavy (gsub, scan)
-import Text.Regex.PCRE.Light (compile, dotall, multiline)
-import Prelude hiding (lookup, readFile, writeFile)
-
-data AttributeFile = AttributeFile
- { posixTime :: Integer,
- content :: Text
- }
- deriving (Generic, Show)
-
-instance ToJSON AttributeFile
-
-data Item = Item
- { title :: String,
- sha1 :: String,
- attr :: Map String AttributeFile,
- numAnswer :: Int
- }
- deriving (Generic, Show)
-
-data TemTree = TemTree
- { path :: String,
- item :: Item,
- kids :: [Item],
- parentSha1 :: String
- }
- deriving (Generic, Show)
-
-instance ToJSON Item
-
-instance ToJSON TemTree
-
-sha1InHex :: String -> [Char]
-sha1InHex = C8.unpack . B16.encode . SHA.hash . C8.pack
-
-theReader :: String -> String -> String -> FilePath -> IO Text
-theReader sitePrefix srcDir dstDir fp =
- let ext = takeExtension fp
- readWorth = ext == ".md" || ext == ".txt"
- content = if readWorth then decodeUtf8 <$> readFile fp else return ""
- func =
- if ext == ".md"
- then mdToHTML <=< copyMDMedia sitePrefix srcDir (dropFileName fp) dstDir . subInlineMathBlock . subDisplayMathBlock
- else return
- in content >>= func
-
-theFilter :: DirTree a -> DirTree a
-theFilter =
- filterDir
- ( \case
- Dir name _ -> head name /= '.'
- File name _ -> ".md" `isExtensionOf` name || ".txt" `isExtensionOf` name
- _ -> False
- )
-
-writeJson :: String -> TemTree -> IO ()
-writeJson dst tt = writeFile (dst > (sha1 (item tt) -<.> ".json")) (encodePretty tt)
-
-someFunc :: String -> FilePath -> FilePath -> IO ()
-someFunc prefixPath src'' dst = do
- src <- canonicalizePath src''
- anchored <- readDirectoryWithL (theReader prefixPath src dst) src
- let anchored' = over _dirTree theFilter anchored
- let dbDst = dst > "db"
- removePathForcibly dbDst >> createDirectoryIfMissing True dbDst
- let (Dir name entries) = anchored' ^. _dirTree
- putStrLn "==============================="
- putStrLn $ "canonical src :" ++ src
- putStrLn $ "dst :" ++ dst
- putStrLn $ "Prefix :" ++ prefixPath
- putStrLn $ "Name of src dir :" ++ name
- putStrLn "==============================="
- creationTimeForFiles <- mapKeys (name >) <$> creationTime src
- mapM_ (writeJson dbDst) (flatten (makeTr creationTimeForFiles name "" entries))
-
-subInlineMathBlock :: Text -> Text
-subInlineMathBlock =
- let imgRegex = compile "\\$`(.+?)`\\$" []
- in gsub imgRegex (\(d : _) -> "\\\\(" ++ d ++ "\\\\)" :: String)
-
-stripString :: String -> String
-stripString = unpack . strip . pack
-
-subDisplayMathBlock :: Text -> Text
-subDisplayMathBlock =
- let imgRegex = compile "^```math$(.+?)^```$" [multiline, dotall]
- in gsub imgRegex (\(d : _) -> "\\\\[" ++ stripString d ++ "\\\\]" :: String)
-
-copyMDMedia :: String -> FilePath -> FilePath -> FilePath -> Text -> IO Text
-copyMDMedia sitePrefix srcDir mdDir dstDir content = do
- let imgRegex = compile "!\\[\\]\\((?!http)(.+?)\\)" []
- let imgNames = map (unpack . head . snd) $ scan imgRegex content
- if null imgNames
- then return content
- else do
- let rel = makeRelative srcDir mdDir
- let relToDst = dstDir > rel
- createDirectoryIfMissing True relToDst
- mapM_ (\x -> copyFile (mdDir > x) (relToDst > x)) imgNames
- let g x = joinPath $ filter (/= "/") ([sitePrefix, rel, x] >>= splitDirectories)
- let f x = mconcat [""]
- return $ gsub imgRegex (f . head) content
-
-mdToHTML :: Text -> IO Text
-mdToHTML txt =
- runIOorExplode $
- readMarkdown
- def
- { readerExtensions = githubMarkdownExtensions <> extensionsFromList [Ext_tex_math_double_backslash, Ext_pipe_tables]
- }
- txt
- >>= writeHtml5String
- def
- { writerHTMLMathMethod = KaTeX defaultKaTeXURL
- }
-
-countAnswer :: Item -> Sum Int
-countAnswer = maybe 0 (const 1) . lookup "a" . attr
-
-makeTr :: Map FilePath Integer -> String -> String -> [DirTree Text] -> Tree TemTree
-makeTr time path parentSha1 entries =
- let sha1 = sha1InHex path
- kidTrs = [makeTr time (path > title) sha1 entries' | Dir title entries' <- entries]
- kidItems = map (item . rootLabel) kidTrs
- answerNumber = getSum $ countAnswer thisItem <> foldMap (Sum . numAnswer) kidItems
- f filename =
- let entry = path > filename
- in case lookup entry time of
- Just x -> x
- Nothing -> error $ "Failed to read entry : " ++ entry
- thisItem = Item (takeFileName path) sha1 (fromList [(takeBaseName name', AttributeFile (f name') file) | File name' file <- entries]) answerNumber
- thisNode = TemTree path thisItem kidItems parentSha1
- in Node thisNode kidTrs
diff --git a/src/MyGit.hs b/src/MyGit.hs
index b446c15..8bc9653 100644
--- a/src/MyGit.hs
+++ b/src/MyGit.hs
@@ -1,119 +1,59 @@
-{-# LANGUAGE TemplateHaskell #-}
-
-module MyGit
- ( creationTime,
- )
-where
-
-import Bindings.Libgit2
-import Control.Lens (makeLenses, (<&>), (^.))
-import Control.Monad (when, (>=>))
-import Data.List (isPrefixOf)
-import Data.Map (Map)
-import qualified Data.Map as Map
-import Foreign (Ptr, Storable (peek), alloca)
-import Foreign.C.String (CString, peekCString, withCString)
-import System.Directory (canonicalizePath, doesDirectoryExist)
-import System.FilePath (makeRelative, takeDirectory, (>))
-import System.FilePath.Posix (takeDirectory, (>))
-
--- import Data.Set (Set)
--- import qualified Data.Set as Set
-data Pointers = Pointers
- { _repoP :: Ptr (Ptr C'git_repository),
- _headP :: Ptr (Ptr C'git_reference),
- _commitP :: Ptr (Ptr C'git_commit)
- }
-
-makeLenses ''Pointers
-
-withPointers f = alloca $ \x -> do
- alloca $ \y -> do
- alloca $ \z -> do
- f (Pointers x y z)
-
-creationTime :: FilePath -> IO (Map FilePath Integer)
-creationTime src = withLibGitDo $ do
- dotGitPath <- canonicalizePath =<< getDotGitPath src
- repoPath <- canonicalizePath $ takeDirectory dotGitPath
- let require = if repoPath == src then "" else makeRelative repoPath src
- withCString dotGitPath $ \csrc -> do
- putStrLn "============================"
- putStrLn $ "src :" ++ src
- putStrLn $ "require :" ++ require
- putStrLn $ "repoPath :" ++ repoPath
- putStrLn "============================"
- withPointers (func require csrc)
-
-getDotGitPath :: FilePath -> IO FilePath
-getDotGitPath fp = do
- putStrLn $ "testing .git: " ++ fp
- e <- doesDirectoryExist (fp > ".git")
- let dirName = takeDirectory fp
- when (dirName == fp) $ error "Failed to find .git"
- if e
- then return $ fp > ".git"
- else getDotGitPath dirName
-
-func :: String -> CString -> Pointers -> IO (Map FilePath Integer)
-func matchPrefix repoPath pointers = do
- c'git_repository_open (pointers ^. repoP) repoPath >>= errorCheck
- repo <- peek $ pointers ^. repoP
- c'git_repository_head (pointers ^. headP) repo >>= errorCheck
- headOid <- peek (pointers ^. headP) >>= c'git_reference_target
- c'git_commit_lookup (pointers ^. commitP) repo headOid >>= errorCheck
- headCommit <- peek (pointers ^. commitP)
- lineage <- unfoldCommits headCommit
- let constructEntryMap' = constructEntrymap matchPrefix repo
- maps <- mapM (getRootAndTime >=> uncurry constructEntryMap') lineage
- c'git_repository_free repo
- return $ Map.unionsWith min maps
-
-getRootAndTime :: Ptr C'git_commit -> IO (Ptr C'git_tree, Integer)
-getRootAndTime commit = do
- alloca $ \rootP -> do
- c'git_commit_tree rootP commit >>= errorCheck
- root <- peek rootP
- time <- c'git_commit_time commit
- return (root, toInteger time)
-
-unfoldCommits :: Ptr C'git_commit -> IO [Ptr C'git_commit]
-unfoldCommits commit = alloca $ \parentP -> do
- result <- c'git_commit_parent parentP commit 0
- if result == 0
- then peek parentP >>= unfoldCommits <&> (++ [commit])
- else return [commit]
-
-constructEntrymap :: String -> Ptr C'git_repository -> Ptr C'git_tree -> Integer -> IO (Map FilePath Integer)
-constructEntrymap matchPrefix repo root time =
- let makeEntryMap'' :: String -> Ptr C'git_tree_entry -> IO (Map FilePath Integer)
- makeEntryMap'' parentDir entry = do
- entryType <- c'git_tree_entry_type entry
- name <- c'git_tree_entry_name entry >>= peekCString
- let next = parentDir ++ name ++ "/"
- if entryType == c'GIT_OBJ_TREE
- then do
- eoid <- c'git_tree_entry_id entry
- alloca $ \subTreeP -> do
- c'git_tree_lookup subTreeP repo eoid >>= errorCheck
- subTree <- peek subTreeP
- if next `isPrefixOf` matchPrefix || matchPrefix `isPrefixOf` next
- then makeEntryMap' (parentDir ++ name ++ "/") subTree
- else return Map.empty
- else do
- if matchPrefix `isPrefixOf` parentDir
- then do
- let relPath = makeRelative matchPrefix (parentDir ++ name)
- return $ Map.singleton relPath time
- else return Map.empty
-
- makeEntryMap' :: String -> Ptr C'git_tree -> IO (Map FilePath Integer)
- makeEntryMap' parentDir tree = do
- entryCountC <- c'git_tree_entrycount tree
- let f = c'git_tree_entry_byindex tree
- foldMap (f >=> makeEntryMap'' parentDir) [0 .. (entryCountC - 1)]
- in makeEntryMap' "" root
-
-errorCheck r = when (r /= 0) $ error "fail"
-
--- printMessage = c'git_commit_message >=> peekCString >=> print
\ No newline at end of file
+{-# LANGUAGE OverloadedStrings #-}
+
+module MyGit (myGit) where
+
+import Control.Monad qualified as Monad
+import Control.Monad.IO.Class qualified as MonadIOClass
+import Control.Monad.Loops qualified as MonadLoops
+import Control.Monad.Trans.Reader qualified as Reader
+import Data.ByteString.Char8 qualified as C8
+import Data.Map qualified as Map
+import Data.Maybe qualified as Maybe
+import Data.Tagged qualified as Tagged
+import Data.Time qualified as Time
+import Git qualified
+import Git.Libgit2 qualified as LG
+
+myGit :: FilePath -> IO (Map.Map FilePath Time.ZonedTime)
+myGit repoPath = do
+ let repoOpts =
+ Git.RepositoryOptions
+ { repoPath,
+ repoWorkingDir = Nothing,
+ repoIsBare = False,
+ repoAutoCreate = False
+ }
+ Git.withRepository' LG.lgFactory repoOpts myGit_
+
+myGit_ :: Reader.ReaderT LG.LgRepo IO (Map.Map FilePath Time.ZonedTime)
+myGit_ = do
+ maybeObjID <- Git.resolveReference "HEAD"
+ let commitID = Maybe.fromJust maybeObjID
+ headCommit <- Git.lookupCommit (Tagged.Tagged commitID)
+ let clone x = (x, x)
+ tailCommits <-
+ MonadLoops.unfoldrM
+ (fmap (fmap clone . Maybe.listToMaybe) . Git.lookupCommitParents)
+ headCommit
+
+ MonadIOClass.liftIO $ putStrLn $ "Total commits : " ++ show (length tailCommits)
+ MonadIOClass.liftIO $ putStrLn $ "Last commit : " ++ show commitID
+ MonadIOClass.liftIO $ putStrLn $ "First commit : " ++ show (Git.commitOid $ last tailCommits)
+
+ seed <- constructEntryTimeMap headCommit
+
+ timeTable' <-
+ Monad.foldM
+ ( \xMap y -> do
+ yMap <- constructEntryTimeMap y
+ return $ Map.differenceWith (\_ b -> Just b) xMap yMap
+ )
+ seed
+ tailCommits
+ return $ Map.mapKeys (C8.unpack . fst) timeTable'
+
+constructEntryTimeMap :: Git.Commit LG.LgRepo -> Reader.ReaderT LG.LgRepo IO (Map.Map (Git.TreeFilePath, Git.Oid LG.LgRepo) Time.ZonedTime)
+constructEntryTimeMap commit = do
+ let time = Git.signatureWhen (Git.commitAuthor commit)
+ entries <- Git.listTreeEntries True =<< Git.lookupTree (Git.commitTree commit)
+ return $ Map.fromList [((x, Tagged.untag oid), time) | (x, Git.BlobEntry oid _) <- entries]
diff --git a/src/MyLib.hs b/src/MyLib.hs
new file mode 100644
index 0000000..38b5895
--- /dev/null
+++ b/src/MyLib.hs
@@ -0,0 +1,214 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module MyLib (someFunc) where
+
+import CMark qualified
+import Control.Lens
+import Control.Monad qualified as Monad
+import Crypto.Hash.SHA1 qualified as SHA (hash)
+import Data.Aeson as Json
+import Data.ByteString.Base16 qualified as B16 (encode)
+import Data.ByteString.Char8 qualified as C8
+import Data.Foldable qualified as Foldable
+import Data.List qualified as List
+import Data.List.NonEmpty qualified as NE
+import Data.Map qualified as Map
+import Data.Maybe qualified as Maybe
+import Data.Text qualified as T
+import Data.Text.IO qualified as TIO
+import Data.Time qualified
+import Data.Tree qualified as Tree
+import Data.Tree.Lens qualified as TreeLens
+import GHC.Generics qualified as Generics
+import MyGit qualified
+import MyMark qualified
+import System.Directory qualified as Dir
+import System.Directory.Tree qualified as DirTree
+import System.FilePath qualified as File
+
+sha1InHex :: String -> [Char]
+sha1InHex = C8.unpack . B16.encode . SHA.hash . C8.pack
+
+data AttributeFile = AttributeFile
+ { _content :: T.Text
+ }
+ deriving (Generics.Generic)
+
+makeLenses ''AttributeFile
+
+instance Show AttributeFile where
+ show (AttributeFile c) = "(" ++ show (T.length c) ++ " long text)"
+
+instance Json.ToJSON AttributeFile where
+ toEncoding = Json.genericToEncoding Json.defaultOptions
+
+data PageAttribute = PageAttribute {_time :: Data.Time.ZonedTime, _attributeFile :: AttributeFile} deriving (Generics.Generic)
+
+instance Show PageAttribute where
+ show (PageAttribute t af) = show af ++ ", time : " ++ show t
+
+instance Json.ToJSON PageAttribute where
+ toEncoding = Json.genericToEncoding Json.defaultOptions
+
+data PageContent = PageContent
+ { _pageTitle :: String,
+ _hash :: String,
+ _attributes :: Map.Map FilePath PageAttribute,
+ _answers :: Word
+ }
+ deriving (Generics.Generic)
+
+makeLenses ''PageContent
+
+instance Json.ToJSON PageContent where
+ toEncoding = Json.genericToEncoding Json.defaultOptions
+
+data PageData = PageData
+ { _pageContent :: PageContent,
+ _parentHash :: String,
+ _childPageContents :: [PageContent]
+ }
+ deriving (Generics.Generic)
+
+makeLenses ''PageData
+
+instance Json.ToJSON PageData where
+ toEncoding = Json.genericToEncoding Json.defaultOptions
+
+instance Show PageData where
+ show pg =
+ let printEntry (k, v) = k ++ ": " ++ show v
+ attribs = NE.nonEmpty (map printEntry (Map.toList (pg ^. pageContent . attributes)))
+ attribsStr =
+ Maybe.maybe
+ ""
+ ( \(x NE.:| xs) ->
+ "\nattributes : "
+ ++ x
+ ++ Monad.join (map ("\n " ++) xs)
+ )
+ attribs
+ in "page title : "
+ ++ pg ^. pageContent . pageTitle
+ ++ "\nhash : "
+ ++ pg ^. pageContent . hash
+ ++ "\nparent hash : "
+ ++ pg ^. parentHash
+ ++ "\nanswers : "
+ ++ show (pg ^. pageContent . answers)
+ ++ "\nchildren : "
+ ++ show (length (pg ^. childPageContents))
+ ++ attribsStr
+
+data FileType = Resource | Attribute AttributeFile deriving (Generics.Generic, Show)
+
+data Item = Item
+ { _title :: String,
+ _files :: Map.Map String FileType
+ }
+ deriving (Generics.Generic, Show)
+
+makeLenses ''Item
+
+myUnfolder :: DirTree.DirTree FileType -> (Item, [DirTree.DirTree FileType])
+myUnfolder dt =
+ let _title = dt ^. DirTree._name
+ contents = dt ^. DirTree._contents
+ in ( Item
+ { _title,
+ _files = Map.fromList [(x, y) | DirTree.File x y <- contents]
+ },
+ [DirTree.Dir x y | DirTree.Dir x y <- contents]
+ )
+
+zipPath :: Tree.Tree Item -> Tree.Tree ([FilePath], Item)
+zipPath t =
+ let item = t ^. TreeLens.root
+ branches = t ^. TreeLens.branches
+ prepend = (item ^. title :)
+ mapPrepend = over (mapped . _1) prepend
+ branches' = over mapped (mapPrepend . zipPath) branches
+ in Tree.Node ([], item) branches'
+
+theWriter :: FilePath -> FilePath -> String -> ([FilePath], Item) -> IO ()
+theWriter source destination prefix (parentPathComponents, item) = do
+ let _pathComponents = drop 1 parentPathComponents ++ [item ^. title]
+ putStrLn $ "Creating hash with " ++ List.intercalate "/" _pathComponents ++ " ..."
+ let _hash = sha1InHex $ List.intercalate "/" _pathComponents
+
+ putStrLn $ "Processing: " ++ take 7 _hash ++ "... " ++ File.joinPath _pathComponents
+
+ let hashDir = destination File.> "resources" File.> _hash
+
+ let copyResource key = do
+ let src = File.joinPath $ [source] ++ _pathComponents ++ [key]
+ let dst = hashDir File.> key
+ putStrLn $ " Copying " ++ src ++ " -> " ++ dst
+ Dir.copyFile src dst
+
+ let compileMarkdown key _content = do
+ let src = File.joinPath $ [source] ++ _pathComponents ++ [key]
+ let dst = hashDir File.> key ++ ".html"
+ putStrLn $ " Compiling " ++ src ++ " -> " ++ dst
+ let safePrefix = List.dropWhileEnd (== '/') $ dropWhile (== '/') prefix
+ let finalPrefix = List.intercalate "/" (filter (not . null) [safePrefix, "resources", _hash])
+ TIO.writeFile dst (CMark.nodeToHtml [] $ MyMark.prefixImageUrl finalPrefix $ CMark.commonmarkToNode [] _content)
+
+ let writeFileType key =
+ \case
+ Resource -> copyResource key
+ Attribute (AttributeFile {_content}) ->
+ Monad.when (File.takeExtension key == ".md") $ compileMarkdown key _content
+ _ <- Dir.createDirectoryIfMissing True hashDir
+ _ <- Map.traverseWithKey writeFileType (item ^. files)
+ return ()
+
+someFunc :: String -> FilePath -> FilePath -> IO [PageData]
+someFunc prefixPath source destination = do
+ root <- DirTree.readDirectoryWithL myReader source
+
+ let tree = zipPath $ Tree.unfoldTree myUnfolder (DirTree.filterDir myFilter $ root ^. DirTree._dirTree)
+
+ Foldable.traverse_ (theWriter source destination prefixPath) tree
+ timeTable <- MyGit.myGit source
+
+ let folder (parentPathComponents, item) children =
+ let parentPath = List.intercalate "/" (drop 1 parentPathComponents)
+ path = if null parentPath then item ^. title else parentPath ++ "/" ++ item ^. title
+ getTime k = Maybe.fromJust $ Map.lookup (path ++ "/" ++ k) timeTable
+ _attributes = Map.fromList [(key, PageAttribute {_time = getTime key, _attributeFile}) | (key, Attribute _attributeFile) <- Map.toList (item ^. files)]
+ in PageData
+ { _pageContent =
+ PageContent
+ { _pageTitle = item ^. title,
+ _hash = sha1InHex path,
+ _attributes,
+ _answers = maybe 0 (const 1) (item ^. files . at "a.md") + sumOf (folded . _head . pageContent . answers) children
+ },
+ _parentHash = sha1InHex parentPath,
+ _childPageContents = children ^.. folded . _head . pageContent
+ }
+ : Monad.join children
+ let pageDatas = Tree.foldTree folder tree
+ let pagesDir = destination File.> "pages"
+ Dir.createDirectoryIfMissing True pagesDir
+ let writePageData pg = Json.encodeFile (pagesDir File.> (pg ^. pageContent . hash) ++ ".json") pg
+ Monad.forM_ pageDatas writePageData
+ return pageDatas
+
+myReader :: FilePath -> IO FileType
+myReader path = do
+ let ext = File.takeExtension path
+ if ext `elem` [".md", ".txt"]
+ then do
+ _content <- TIO.readFile path
+ return $ Attribute $ AttributeFile {_content}
+ else return Resource
+
+myFilter :: DirTree.DirTree a -> Bool
+myFilter =
+ \case
+ DirTree.Dir name _ -> head name /= '.'
+ DirTree.File name _ -> head name /= '.'
+ _ -> False
diff --git a/src/MyMark.hs b/src/MyMark.hs
new file mode 100644
index 0000000..fe21117
--- /dev/null
+++ b/src/MyMark.hs
@@ -0,0 +1,32 @@
+module MyMark (prefixImageUrl) where
+
+import CMark
+import Data.Text qualified as T
+
+prefixImageUrl :: String -> Node -> Node
+prefixImageUrl prefix node =
+ let safePrefix = T.pack ("/" ++ prefix ++ "/")
+ replaceUrl url = if T.isPrefixOf (T.pack "http") url then url else safePrefix <> T.dropWhile (== '/') url
+ recurse (Node posInfo nodeType nodes) =
+ case nodeType of
+ IMAGE url title -> Node Nothing (IMAGE (replaceUrl url) title) nodes
+ PARAGRAPH -> Node Nothing PARAGRAPH $ workOnInlineMath (map (prefixImageUrl prefix) nodes)
+ CODE_BLOCK info text -> if info == T.pack "math" then mathBlock text else Node Nothing nodeType nodes
+ _ -> Node Nothing nodeType (recurse <$> nodes)
+ in recurse node
+
+workOnInlineMath :: [Node] -> [Node]
+workOnInlineMath (x : y : z : tail) =
+ case (x, y, z) of
+ (Node _ (TEXT l) [], Node _ (CODE m) [], Node _ (TEXT r) []) ->
+ if T.isSuffixOf (T.pack "$") l && T.isPrefixOf (T.pack "$") r
+ then x : Node Nothing (TEXT (T.pack "`" <> m <> T.pack "`")) [] : workOnInlineMath (z : tail)
+ else x : y : workOnInlineMath (z : tail)
+ _ -> x : workOnInlineMath (y : z : tail)
+workOnInlineMath (x : xs) = x : workOnInlineMath xs
+workOnInlineMath [] = []
+
+mathBlock :: T.Text -> Node
+mathBlock text =
+ let t = T.pack "\\\\(\n" <> text <> T.pack "\n)\\\\"
+ in Node Nothing PARAGRAPH [Node Nothing (TEXT t) []]
\ No newline at end of file
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
index 0000000..09b3e15
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,20 @@
+module Main (main) where
+import MyLib qualified
+import MyGit qualified
+import System.FilePath
+import Control.Monad
+import Data.Map
+
+src :: FilePath
+src = "test" > "answers-db"
+
+dst :: FilePath
+dst = "test" > "dst"
+
+prefix :: String
+prefix = "prefix"
+
+main :: IO ()
+main = do
+ pageDatas <- MyLib.someFunc prefix src dst
+ forM_ pageDatas print
\ No newline at end of file
diff --git a/test/Spec.hs b/test/Spec.hs
deleted file mode 100644
index ff9900b..0000000
--- a/test/Spec.hs
+++ /dev/null
@@ -1,38 +0,0 @@
-import Lib
-
-import System.Directory (removeDirectoryRecursive, createDirectoryIfMissing, doesDirectoryExist)
-import Test.Hspec
-import Test.QuickCheck
-import qualified System.Directory.Tree as DT
-import qualified Data.ByteString as B
-import Control.Monad (when)
-import System.FilePath (takeExtension, takeFileName)
-src :: FilePath
-src = "test/src"
-
-dst :: FilePath
-dst = "test/dst"
-
-prefix :: String
-prefix = "prefix"
-
-expect :: FilePath
-expect = "test/expect"
-
-simpleReader :: FilePath -> IO String
-simpleReader fp = case takeExtension fp of
- ".json" -> readFile fp
- _ -> return (takeFileName fp)
-
-main :: IO ()
-main = hspec $ do
- describe "Prelude.head" $ do
- it "returns the first element of a list" $ do
- dstExist <- doesDirectoryExist dst
- when dstExist (removeDirectoryRecursive dst)
- createDirectoryIfMissing True dst
- someFunc prefix src dst
- -- Top directory names are different, hence (_ :/ (Dir _ toDT))
- (_ DT.:/ (DT.Dir _ toDT)) <- DT.readDirectoryWith simpleReader dst
- (_ DT.:/ (DT.Dir _ beDT)) <- DT.readDirectoryWith simpleReader expect
- toDT `shouldBe` beDT
\ No newline at end of file
diff --git a/test/answers-db b/test/answers-db
new file mode 160000
index 0000000..ba858fd
--- /dev/null
+++ b/test/answers-db
@@ -0,0 +1 @@
+Subproject commit ba858fd9a8f220d6e4001a7c37a3895cefb4b66a
diff --git a/test/expect/db/22c0547dca2710d76a885c5ee667ad981dbe31db.json b/test/expect/db/22c0547dca2710d76a885c5ee667ad981dbe31db.json
deleted file mode 100644
index 96d4d77..0000000
--- a/test/expect/db/22c0547dca2710d76a885c5ee667ad981dbe31db.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- "item": {
- "attr": {
- "author": {
- "content": "Ingun Jon",
- "posixTime": 1668403786
- },
- "q": {
- "content": "some book's q.md
\n\\cdots
\n
",
- "posixTime": 1668403786
- }
- },
- "numAnswer": 2,
- "sha1": "22c0547dca2710d76a885c5ee667ad981dbe31db",
- "title": "some book"
- },
- "kids": [
- {
- "attr": {
- "a": {
- "content": "it's 3
",
- "posixTime": 1668403786
- },
- "q": {
- "content": "What is 1 + 1?
",
- "posixTime": 1668403786
- }
- },
- "numAnswer": 1,
- "sha1": "262d26ddc4b6cf08bd3542ce232fc43acf6004d7",
- "title": "chapter 1"
- },
- {
- "attr": {
- "a": {
- "content": "it's 5
",
- "posixTime": 1674358503
- },
- "q": {
- "content": "What is 2+2?
",
- "posixTime": 1674358503
- }
- },
- "numAnswer": 1,
- "sha1": "d2786f0cdc45005f4cd9e4c3bd67870fa7c4c3a1",
- "title": "chapter2"
- }
- ],
- "parentSha1": "f27fede2220bcd326aee3e86ddfd4ebd0fe58cb9",
- "path": "src/some book"
-}
\ No newline at end of file
diff --git a/test/expect/db/262d26ddc4b6cf08bd3542ce232fc43acf6004d7.json b/test/expect/db/262d26ddc4b6cf08bd3542ce232fc43acf6004d7.json
deleted file mode 100644
index 1b3d7d2..0000000
--- a/test/expect/db/262d26ddc4b6cf08bd3542ce232fc43acf6004d7.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "item": {
- "attr": {
- "a": {
- "content": "it's 3
",
- "posixTime": 1668403786
- },
- "q": {
- "content": "What is 1 + 1?
",
- "posixTime": 1668403786
- }
- },
- "numAnswer": 1,
- "sha1": "262d26ddc4b6cf08bd3542ce232fc43acf6004d7",
- "title": "chapter 1"
- },
- "kids": [],
- "parentSha1": "22c0547dca2710d76a885c5ee667ad981dbe31db",
- "path": "src/some book/chapter 1"
-}
\ No newline at end of file
diff --git a/test/expect/db/d2786f0cdc45005f4cd9e4c3bd67870fa7c4c3a1.json b/test/expect/db/d2786f0cdc45005f4cd9e4c3bd67870fa7c4c3a1.json
deleted file mode 100644
index ec0bc31..0000000
--- a/test/expect/db/d2786f0cdc45005f4cd9e4c3bd67870fa7c4c3a1.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "item": {
- "attr": {
- "a": {
- "content": "it's 5
",
- "posixTime": 1674358503
- },
- "q": {
- "content": "What is 2+2?
",
- "posixTime": 1674358503
- }
- },
- "numAnswer": 1,
- "sha1": "d2786f0cdc45005f4cd9e4c3bd67870fa7c4c3a1",
- "title": "chapter2"
- },
- "kids": [],
- "parentSha1": "22c0547dca2710d76a885c5ee667ad981dbe31db",
- "path": "src/some book/chapter2"
-}
\ No newline at end of file
diff --git a/test/expect/db/f27fede2220bcd326aee3e86ddfd4ebd0fe58cb9.json b/test/expect/db/f27fede2220bcd326aee3e86ddfd4ebd0fe58cb9.json
deleted file mode 100644
index 6ff4d52..0000000
--- a/test/expect/db/f27fede2220bcd326aee3e86ddfd4ebd0fe58cb9.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
- "item": {
- "attr": {
- "q": {
- "content": "top q.md
",
- "posixTime": 1668403786
- }
- },
- "numAnswer": 2,
- "sha1": "f27fede2220bcd326aee3e86ddfd4ebd0fe58cb9",
- "title": "src"
- },
- "kids": [
- {
- "attr": {
- "author": {
- "content": "Ingun Jon",
- "posixTime": 1668403786
- },
- "q": {
- "content": "some book's q.md
\n\\cdots
\n
",
- "posixTime": 1668403786
- }
- },
- "numAnswer": 2,
- "sha1": "22c0547dca2710d76a885c5ee667ad981dbe31db",
- "title": "some book"
- }
- ],
- "parentSha1": "",
- "path": "src"
-}
\ No newline at end of file
diff --git a/test/expect/some book/img.png b/test/expect/some book/img.png
deleted file mode 100644
index bb5ea35..0000000
Binary files a/test/expect/some book/img.png and /dev/null differ