Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
67b87f8
Update the README file
harendra-kumar Feb 20, 2026
16ff997
Update cabal file, hide String, Uniq modules
harendra-kumar Feb 20, 2026
504fd7d
Add flake.nix
harendra-kumar Feb 20, 2026
85de64c
Replace Switch with Bool
harendra-kumar Feb 20, 2026
9a7eb5a
Break FileTest module and overhaul it
harendra-kumar Feb 20, 2026
4293a45
Add the ability to pass FilePath in predicates
harendra-kumar Mar 4, 2026
e614996
Rename apply to testGeneral
harendra-kumar Mar 4, 2026
03db365
Rename predicate etc to withStatus etc.
harendra-kumar Mar 4, 2026
a0fef08
Fix doesExist not checking the status
harendra-kumar Apr 5, 2026
8549c2d
Swap the order of arguments in ComparedTo preds
harendra-kumar Mar 5, 2026
f892a58
Fix posix isReadable, isWritable, isExecutable
harendra-kumar Mar 4, 2026
4f750fe
Fix the predicates in the Windows module
harendra-kumar Mar 4, 2026
202c0d3
Do not use the deprecated "isExisting" function
harendra-kumar Mar 5, 2026
f5432d4
Expose mode based access checks
harendra-kumar Mar 5, 2026
dfc8372
Expose some covenient size, time combinators
harendra-kumar Mar 6, 2026
bf8cbdd
Make small documentation updates
harendra-kumar Mar 6, 2026
1d4049e
Rename modifiedAfter/before to newerThan/olderThan
harendra-kumar Mar 6, 2026
e88c2fd
Add modifiedSinceLastAccess
harendra-kumar Mar 6, 2026
63fd54e
Add accessTime and metadataChangeTime
harendra-kumar Mar 6, 2026
8c431db
Implement sameFileAs
harendra-kumar Mar 6, 2026
1926893
Add isTerminalFd
harendra-kumar Mar 6, 2026
edc5e9a
Use isWritableByMode in Rm implementation
harendra-kumar Mar 6, 2026
6a55406
Fix rm module: update docs, API compatibility with GNU rm
harendra-kumar Apr 5, 2026
0859d0a
Add a test suite for Rm
harendra-kumar Apr 5, 2026
d0db3ef
Add hie.yaml
harendra-kumar Apr 6, 2026
4507261
Disable streamly master branch build
harendra-kumar Mar 6, 2026
b90ecf0
Update github CI config
harendra-kumar Mar 6, 2026
9f22aa2
Update appveyor.yaml
harendra-kumar Mar 6, 2026
1afde11
Update .packcheck.ignore, add flake, remove stale entries
harendra-kumar Mar 6, 2026
849e603
Remove the use of list "head" partial function
harendra-kumar Apr 6, 2026
35dee67
Fix unused function warning
harendra-kumar Apr 6, 2026
ab7cc98
Add version bounds on Win32
harendra-kumar Apr 6, 2026
c874917
Move docs to extra-doc-files section
harendra-kumar Apr 6, 2026
1340989
Fix doctests in FileTest module
harendra-kumar Apr 7, 2026
f2b31e8
Rename testGeneral to testWithStatus
harendra-kumar Apr 7, 2026
229ff10
Fix the Windows module for FileTest
harendra-kumar Apr 6, 2026
d6b5423
Remove windows unsupported APIs from common module
harendra-kumar Apr 7, 2026
b892495
Shorten the paths on windows
harendra-kumar Apr 7, 2026
48c03cb
Use getTemporaryDirectory for Windows/Posix portability
harendra-kumar Apr 8, 2026
dccf4f4
Use split caching to cache deps even if the later build fails
harendra-kumar Apr 8, 2026
52583f0
Fix removal of dir symlinks on windows
harendra-kumar Apr 10, 2026
bb7e54f
Add some rm idioms
harendra-kumar Apr 11, 2026
06a3c25
Fix removing write-protected files on Windows
harendra-kumar Apr 11, 2026
2d00774
Fix force removal on Windows
harendra-kumar Apr 11, 2026
adbd430
Disable checking of parent dir perms on Windows
harendra-kumar Apr 11, 2026
f8a2f03
Rename doesExist to doesItExist
harendra-kumar Apr 11, 2026
35d0302
Fix the docspec setup in the Sh module
harendra-kumar Apr 12, 2026
6c769d1
Update the tested-with field
harendra-kumar Apr 12, 2026
6c0885e
Fix warnings and enable Werror on Windows
harendra-kumar Apr 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
569 changes: 461 additions & 108 deletions .github/workflows/haskell.yml

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions .packcheck.ignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
.cirrus.yml
.packcheck.ignore
stack.yaml
.github/workflows/haskell.yml
.gitignore
default.nix
appveyor.yml
benchmark/Main.hs
hie.yaml
cabal.project.d/master
cabal.project.d/master-Werror
cabal.project.d/streamly-0.9.0
cabal.project.d/streamly-0.10.0
flake.lock
flake.nix
packages.nix
stack.yaml
test/Main.hs
32 changes: 29 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
# Shell commands using streams
# Streamly Coreutils

Port useful commands from the GNU `coreutils` to Haskell functions using
streamly.
This repository provides Haskell functions that reimplement common
GNU `coreutils` commands, leveraging the `streamly` library for
efficient, streaming data processing where applicable. The goal is to
offer a functional and highly performant alternative to traditional
shell commands within Haskell applications, enabling complex data
transformations and system interactions using a pure functional
paradigm. Where applicable, these implementations are designed to be
highly concurrent, for example, the `ls` equivalent can list directory
contents concurrently for improved performance.

## Implemented Commands

Currently, this library provides implementations for the
following coreutils-inspired as well as some additional commands:

* Filesystem: `cp`, `rm`, `mv`, `ln`, `readlink`, `test`, `stat`, `touch`
* Directories: `ls`, `dirname`, `mkdir`, `cd`, `pwd`, `home` and others
* Text Processing: `cut`, `tail`
* Processes: `sleep`
* Shell: `which`, executing shell commands with streaming

## Important API Notice

**Please be aware that the API of this library is subject to heavy
change in future releases.** This project is under active development,
and function signatures, module organization, and overall design may
evolve significantly. Users should expect breaking changes and plan
accordingly.
66 changes: 30 additions & 36 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# packcheck-0.4.2
# packcheck-0.7.1
# You can use any of the options supported by packcheck as environment
# variables here. See https://github.com/composewell/packcheck for all
# options and their explanation.

branches:
only:
- master
Expand All @@ -14,43 +15,34 @@ environment:
# ------------------------------------------------------------------------
# Common options
# ------------------------------------------------------------------------
# GHC_OPTIONS: "-Werror"
CABAL_REINIT_CONFIG: "y"
LC_ALL: "C.UTF-8"

# ------------------------------------------------------------------------
# How to build
# ------------------------------------------------------------------------
#
GHCUP_VERSION: "0.1.50.2"
GHCVER: "9.14.1"
#CABALVER: "3.10.3.0"

# ------------------------------------------------------------------------
# What to build
# ------------------------------------------------------------------------
# DISABLE_TEST: "y"
# DISABLE_BENCH: "y"
# DISABLE_DOCS: "y"
DISABLE_SDIST_BUILD: "y"
DISABLE_DIST_CHECKS: "y"
ENABLE_INSTALL: "y"

# ------------------------------------------------------------------------
# stack options
# ------------------------------------------------------------------------
# Note requiring a specific version of stack using STACKVER may fail due to
# github API limit while checking and upgrading/downgrading to the specific
# version.
#STACKVER: "1.6.5"
STACK_UPGRADE: "y"
RESOLVER: "lts-22.33"
STACK_ROOT: "c:\\sr"
STACK_YAML: "stack.yaml"
# DISABLE_DIST_CHECKS: "y"
# DISABLE_SDIST_BUILD: "y"
# Note: these require the "diff" utility.
# DISABLE_SDIST_GIT_CHECK: "y"
DISABLE_SDIST_PROJECT_CHECK: "y"

# ------------------------------------------------------------------------
# cabal options
# ------------------------------------------------------------------------
CABAL_CHECK_RELAX: "y"
CABAL_HACKAGE_MIRROR: "hackage.haskell.org:http://hackage.fpcomplete.com"

# ------------------------------------------------------------------------
# Where to find the required tools
# ------------------------------------------------------------------------
PATH: "%PATH%;%APPDATA%\\local\\bin"
LOCAL_BIN: "%APPDATA%\\local\\bin"
#CABAL_PROJECT: "cabal.project"

# ------------------------------------------------------------------------
# Location of packcheck.sh (the shell script invoked to perform CI tests ).
Expand All @@ -63,31 +55,33 @@ environment:
# If you have not committed packcheck.sh in your repo at PACKCHECK_LOCAL_PATH
# then it is automatically pulled from this URL.
PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
PACKCHECK_GITHUB_COMMIT: "a68b7b9c7c21eef8ed273e67030efb1d4fec027c"
PACKCHECK_GITHUB_COMMIT: "b91dd7daf50d91047ca8c1cce47e8634e34dfcbc"

# Override the temp directory to avoid sed escaping issues
# See https://github.com/haskell/cabal/issues/5386
TMP: "c:\\tmp"

# Bump the -> version to clear the cache
# packcheck uses "%APPDATA%\\local" to install tools like hlint etc.
# cabal may use "%APPDATA%\\cabal" or "c:\\cabal"
# ghcup may use "%APPDATA%\\ghcup" or "c:\\ghcup"
cache:
- "%STACK_ROOT%"
- "%LOCAL_BIN%"
- "%APPDATA%\\local\\bin -> v1"
- "%APPDATA%\\cabal"
- "%APPDATA%\\ghc"
# - "%LOCALAPPDATA%\\Programs\\stack"
- "%LOCALAPPDATA%\\cabal"
- "C:\\ghcup"
- "C:\\cabal"

# Folder where the repository is cloned.
clone_folder: "c:\\pkg"
build: off

before_test:
- if not exist %PACKCHECK_LOCAL_PATH% curl -sSkL -o%PACKCHECK_LOCAL_PATH% %PACKCHECK_GITHUB_URL%/%PACKCHECK_GITHUB_COMMIT%/packcheck.sh
- if not exist %LOCAL_BIN% mkdir %LOCAL_BIN%
- where stack.exe || curl -sSkL -ostack.zip http://www.stackage.org/stack/windows-x86_64 && 7z x stack.zip stack.exe && move stack.exe %LOCAL_BIN%
- if defined STACKVER (stack upgrade --binary-only --binary-version %STACKVER%) else (stack upgrade --binary-only || ver > nul)
- stack --version
- if not exist %PACKCHECK_LOCAL_PATH% curl --fail -sSL -o%PACKCHECK_LOCAL_PATH% %PACKCHECK_GITHUB_URL%/%PACKCHECK_GITHUB_COMMIT%/packcheck.sh

test_script:
- stack setup > nul
- for /f "usebackq tokens=*" %%i in (`where 7z.exe`) do set PATH7Z=%%i\..
- for /f "usebackq tokens=*" %%i in (`where git.exe`) do set PATHGIT=%%i\..
- chcp 65001 && stack exec bash -- -c "chmod +x %PACKCHECK_LOCAL_PATH%; %PACKCHECK_LOCAL_PATH% stack PATH=/usr/bin:\"%PATH7Z%\":\"%PATHGIT%\""
- for /f "usebackq tokens=*" %%i in (`where curl.exe`) do set PATHCURL=%%i\..
- chcp 65001
- bash %PACKCHECK_LOCAL_PATH% cabal PATH="/usr/bin:%PATH7Z%:%PATHGIT%:%PATHCURL%"
20 changes: 10 additions & 10 deletions cabal.project.d/master-Werror
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ packages: .
package streamly-coreutils
ghc-options: -Werror

source-repository-package
type: git
location: https://github.com/composewell/streamly.git
tag: master

source-repository-package
type: git
location: https://github.com/composewell/streamly.git
tag: master
subdir: core
-- source-repository-package
-- type: git
-- location: https://github.com/composewell/streamly.git
-- tag: master
--
-- source-repository-package
-- type: git
-- location: https://github.com/composewell/streamly.git
-- tag: master
-- subdir: core
88 changes: 88 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
description = "Streamly Coreutils Development Environment";

inputs = {
basepkgs.url = "git+ssh://git@github.com/composewell/streamly-packages?rev=76420910d9c74e5fc1d92d680499c97e4f94e873";
nixpkgs.follows = "basepkgs/nixpkgs";
nixpkgs-darwin.follows = "basepkgs/nixpkgs-darwin";
};

outputs = { self, nixpkgs, nixpkgs-darwin, basepkgs }:
basepkgs.nixpack.mkOutputs {
inherit nixpkgs nixpkgs-darwin basepkgs;
name = "streamly-coreutils";
sources = basepkgs.nixpack.lib.localSource "streamly-coreutils" ./.;
#packages = basepkgs.nixpack.lib.devPackage "streamly-coreutils";
#sources = import ./sources.nix;
packages = import ./packages.nix;
};
}
2 changes: 2 additions & 0 deletions hie.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ cradle:
cabal:
- path: "./src"
component: "lib:streamly-coreutils"
- path: "./test"
component: "test:Streamly.Coreutils.Rm"
dependencies:
- streamly-coreutils.cabal
- hie.yaml
24 changes: 24 additions & 0 deletions packages.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{ nixpkgs }:
{
# General packages, haskell or non-haskell will be installed in the
# environment.
packages =
with nixpkgs.pkgs;
[
];

# Haskell packages available in the nix shell, the dependencies of these
# packages are also installed and exposed in ghc package database.
libraries =
with nixpkgs.haskellPackages;
[
temporary hspec
];

# Haskell dev packages. Install only dependencies of these in the shell,
# do not build the packages themselves.
dev-packages =
with nixpkgs.haskellPackages;
[streamly-coreutils];

}
33 changes: 30 additions & 3 deletions src/Streamly/Coreutils/Common.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE PatternSynonyms #-}

-- | This module is deprecated. Use 'Data.Bool' and 'Bool' instead.
module Streamly.Coreutils.Common
( Switch (..))
where
{-# DEPRECATED "This module is deprecated. Please use 'Bool' from 'Data.Bool' instead." #-}
( Switch
#if __GLASGOW_HASKELL__ >= 914
, data On
, data Off
#else
, pattern On
, pattern Off
#endif
) where

-- Define Switch as a Bool alias
type Switch = Bool
{-# DEPRECATED Switch "Use 'Bool' instead" #-}

-- Alias On with True
pattern On :: Switch
pattern On = True
{-# DEPRECATED On "Use 'True' instead" #-}

-- Alias Off with False
pattern Off :: Switch
pattern Off = False
{-# DEPRECATED Off "Use 'False' instead" #-}

data Switch = On | Off deriving (Show, Eq)
-- Ensure GHC knows these two patterns cover all Bool cases
{-# COMPLETE On, Off #-}
8 changes: 4 additions & 4 deletions src/Streamly/Coreutils/Cp.hs
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ cpShouldOverwrite :: CpOverwrite -> FilePath -> FilePath -> IO Bool
cpShouldOverwrite option src dest =
case option of
OverwriteAlways -> return True
OverwriteOnly -> test dest isExisting
OverwriteNever -> not <$> test dest isExisting
OverwriteOnly -> test dest doesItExist
OverwriteNever -> not <$> test dest doesItExist
OverwriteUpdate -> do
r <- test dest isExisting
r <- test dest doesItExist
if r
then test src $ cmpModifyTime (>) dest
then test src $ newerThanFile dest
else return True

-- | @cp option-modifier source destination@. Copy a file or directory.
Expand Down
Loading
Loading