Skip to content

Commit

Permalink
Start Haskell implementation, move Haskell code to sub-directory
Browse files Browse the repository at this point in the history
  • Loading branch information
ad-si committed Feb 3, 2024
1 parent 63d3ad3 commit a1a39ea
Show file tree
Hide file tree
Showing 21 changed files with 312 additions and 56 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/.stack-work
/*.cabal
/.nickel
/cli-spec.json
8 changes: 0 additions & 8 deletions app/Main.hs

This file was deleted.

6 changes: 3 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Haskell Template
# Oclis Changelog

- 2023-11-27 - 0.0.0.0
- Initial release
- [TBD] - 0.1.0.0
- First implementation of code generation for Haskell and PureScript
3 changes: 3 additions & 0 deletions haskell/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.stack-work
/*.cabal
/dist-newstyle
73 changes: 73 additions & 0 deletions haskell/app/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}

module Main where

import Protolude (
Bool (..),
IO,
Maybe (..),
pure,
putText,
when,
($),
(&),
(<>),
)
import Protolude qualified as P

import Oclis as O
import Oclis.App as OA
import Oclis.Option as OO
import Text.RawString.QQ (r)


myCliApp :: OA.App
myCliApp =
OA.defaultApp
{ name = "my-cli-app"
, version = Just "1.0"
, OA.description =
Just
[r|
# My CLI App

This is a CLI app that does something
|]
, options =
[ OO.Option
{ long = Just "verbose"
, short = Nothing
, OO.description = Just "Enable verbose output"
, argument = ArgNone
, defaultValue = P.undefined -- Boolean False
, required = False
}
, OO.Option
{ long = Just "log-file"
, short = Just 'l'
, OO.description = Just "Log file to write to"
, argument = ArgOne "FILE"
, defaultValue = P.undefined -- Boolean False
, required = False
}
]
, run =
\opts _args -> do
P.putText "Hello, world!"

when (opts & O.hasFlag "verbose") $ do
P.putText "Verbose output enabled"

case opts & O.getOptionVal "log-file" of
Nothing -> P.putText "No log file specified"
Just file -> P.putText $ "Writing logs to " <> file
, commands = []
}


main :: IO ()
main = do
putText "Test"
-- TODO: execute myCliApp args
pure ()
File renamed without changes.
18 changes: 18 additions & 0 deletions haskell/makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.PHONY: help
help: makefile
@tail -n +4 makefile | grep ".PHONY"


.PHONY: test
test:
stack test


.PHONY: build
build:
stack build


.PHONY: install
install:
stack install
2 changes: 2 additions & 0 deletions package.yaml → haskell/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ extra-source-files:
- readme.md

dependencies:
- aeson
- base
- protolude
- raw-strings-qq

default-extensions:
- NoImplicitPrelude
Expand Down
9 changes: 9 additions & 0 deletions haskell/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Oclis for Haskell

## Used Technologies

- [Stack](https://docs.haskellstack.org/en/stable/README/)
- [Fourmolu](https://fourmolu.github.io)
- [Haskell Language Server ](https://github.com/haskell/haskell-language-server)
- [Protolude](https://github.com/protolude/protolude)
- [Hspec](https://hspec.github.io)
21 changes: 21 additions & 0 deletions haskell/source/Oclis.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Oclis (
module Oclis.App,
module Oclis.Option,
hasFlag,
getOptionVal,
)
where

import Protolude (Bool, Maybe, Text)
import Protolude qualified as P

import Oclis.App
import Oclis.Option hiding (description)


hasFlag :: Text -> [Option] -> Bool
hasFlag = P.undefined


getOptionVal :: Text -> [Option] -> Maybe Text
getOptionVal = P.undefined
32 changes: 32 additions & 0 deletions haskell/source/Oclis/App.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module Oclis.App where

import GHC.Show (show)
import Protolude (IO, Maybe (..), Show, Text, pure)

import Oclis.Option (Option)


data App = App
{ name :: Text
, version :: Maybe Text
, description :: Maybe Text
, options :: [Option]
, run :: [Option] -> [Text] -> IO ()
, commands :: [App]
}


instance Show App where
show _ = "<app>"


defaultApp :: App
defaultApp =
App
{ name = "oclis"
, version = Nothing
, description = Nothing
, options = []
, run = \_ _ -> pure ()
, commands = []
}
39 changes: 39 additions & 0 deletions haskell/source/Oclis/Option.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Oclis.Option (
Argument (..),
Option (..),
defaultOption,
) where

import Protolude (Bool (..), Char, Eq, Maybe (..), Show, Text)

import Data.Aeson (Value)


data Argument
= ArgNone
| ArgOne Text
| ArgMany Text
deriving (Show, Eq)


data Option = Option
{ long :: Maybe Text
, short :: Maybe Char
, description :: Maybe Text
, argument :: Argument
, required :: Bool
, defaultValue :: Maybe Value
}
deriving (Show, Eq)


defaultOption :: Option
defaultOption =
Option
{ long = Nothing
, short = Nothing
, description = Nothing
, argument = ArgNone
, required = False
, defaultValue = Nothing
}
2 changes: 1 addition & 1 deletion stack.yaml → haskell/stack.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
resolver: lts-21.15
resolver: lts-22.8
packages:
- "."

Expand Down
8 changes: 4 additions & 4 deletions stack.yaml.lock → haskell/stack.yaml.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
packages: []
snapshots:
- completed:
sha256: 350737ef1c4c748f4c7ff56b6e74f2f6d15039a2f148662a8ec1aded016b80d0
size: 640033
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/15.yaml
original: lts-21.15
sha256: 56ef9e03804cb4827866e762dc9752eeb392adda8f4811690da110dd9a165b9e
size: 714105
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/8.yaml
original: lts-22.8
13 changes: 13 additions & 0 deletions haskell/tests/Spec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{-# LANGUAGE OverloadedRecordDot #-}

import Protolude (IO, ($))
import Test.Hspec (describe, hspec, it, shouldBe)

import Oclis


main :: IO ()
main = hspec $ do
describe "Oclis" $ do
it "has a default app" $ do
defaultApp.name `shouldBe` "oclis"
17 changes: 9 additions & 8 deletions makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
.PHONY: test
test:
stack test
.PHONY: help
help: makefile
@tail -n +4 makefile | grep ".PHONY"


.PHONY: build
build:
stack build
build: cli-spec.json


.PHONY: install
install:
stack install
cli-spec.json: oclis-contract.ncl oclis.ncl
echo '(import "oclis-contract.ncl") & (import "oclis.ncl")' \
| nickel export --format json > $@
11 changes: 11 additions & 0 deletions oclis-contract.ncl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Contract for Oclis specifcations
{
name
| String
| doc "The name of the command",
version
| String,
description
| String
| optional,
}
9 changes: 9 additions & 0 deletions oclis.ncl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
name = "oclis",
version = "0.1.0.0",
description = m%"
Generate a CLI parser and executor
from a given `oclis.ncl` specification file.
"%,
run = "runApp"
}
76 changes: 63 additions & 13 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,71 @@
# Haskell Template
# Oclis

Opinionated template for new Haskell projects.
CLI (Command Line Interface) app builder
based on a simple, obvious specification file.


## Used Technologies
## Motivation

- [Stack](https://docs.haskellstack.org/en/stable/README/)
- [Fourmolu](https://fourmolu.github.io)
- [Haskell Language Server ](https://github.com/haskell/haskell-language-server)
- [Protolude](https://github.com/protolude/protolude)
Building a CLI application is a repetitive task.
The same code is written over and over again.
But fear not, Oclis is here to help you out!


## Usage

1. Clone this repository
1. Rename the folder to your project name
1. Replace all occurences of `haskell-template` with your project name
1. Run `stack test` to build the project and run the tests
1. Run `stack run` to run the project
1. Run `stack install` to install the project (make it available in your PATH)
1. Write a simple specification file.
2. Run `oclis` to generate the CLI parsing code for the language of your choice.
3. Define the handler functions for your commands.
4. Done 🎉


## Related

### Tools

- [CLI Definition Language] - DSL for defining command line interfaces
of C++ programs.
- [Decli] - Declarative CLI tool builder.
- [docopt] - Command-line interface description language.
- [make-cli] - Declarative CLI framework for Node.js.

[CLI Definition Language]: https://www.codesynthesis.com/projects/cli/
[Decli]: https://github.com/woile/decli
[docopt]: http://docopt.org/
[make-cli]: https://github.com/dword-design/make-cli


### Specifications

- [clig.dev] - Command Line Interface Guidelines.
- [GNU Table of Long Options][gtolo]
- [Heroku CLI Style Guide][hcsg]
- [OpenAutoComplete] - CLI autocomplete specification.
- [POSIX Utility Conventions][puc]

[clig.dev]: https://clig.dev
[gtolo]:
https://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table
[hcsg]: https://devcenter.heroku.com/articles/cli-style-guide
[OpenAutoComplete]: https://github.com/openautocomplete/openautocomplete
[puc]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html


### Generate GUIs From CLI

- [Claui] - A GUI generator for [clap] using [egui].
- [Gooey] - Turn CLI programs into a full GUI application.
- [Klask] - Automatically create GUI applications from [clap] apps.

[clap]: https://github.com/clap-rs/clap
[Claui]: https://github.com/grantshandy/claui
[egui]: https://github.com/emilk/egui
[Gooey]: https://github.com/chriskiehl/Gooey
[Klask]: https://github.com/MichalGniadek/klask


### Generate GUIs From Simple Code

- [Streamlit] - Turns data scripts into shareable web apps.

[Streamlit]: https://github.com/streamlit/streamlit
Loading

0 comments on commit a1a39ea

Please sign in to comment.