Skip to content

Commit

Permalink
new version
Browse files Browse the repository at this point in the history
  • Loading branch information
Athan Clark committed Nov 27, 2018
1 parent 590135f commit 0349891
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 112 deletions.
15 changes: 10 additions & 5 deletions app/Main.hs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
module Main where

import Path
import qualified Data.ByteString.Lazy as BS
import qualified Data.Text as T
import Data.Attoparsec.Text (parseOnly, endOfInput, eitherP)
import Data.Attoparsec.Path (absFilePath, relFilePath, absDirPath)
import Data.Conduit ((=$=), runConduit)
import Data.Conduit ((.|), runConduit)
import Data.Conduit.Combinators (stdout)
import System.INotify (initINotify, removeWatch, killINotify)
import System.File.Follow (follow)
Expand All @@ -14,9 +15,10 @@ import Control.Exception (bracket)
import Control.Concurrent (threadDelay)


main :: IO ()
main = do
[f] <- getArgs
f <- case parseOnly (eitherP absFilePath relFilePath <* endOfInput) (T.pack f) of
[filePath] <- getArgs
f <- case parseOnly (eitherP absFilePath relFilePath <* endOfInput) (T.pack filePath) of
Left e -> error e
Right eAR -> case eAR of
Left a -> pure a
Expand All @@ -27,4 +29,7 @@ main = do
Right d' ->
pure (d' </> r)
i <- initINotify
bracket (follow i f (\source -> runConduit $ source =$= stdout)) (\watch -> removeWatch watch >> killINotify i) $ \_ -> forever $ threadDelay 50000
let obtain = follow i f $ \source -> runConduit (source .| stdout)
release watch = removeWatch watch >> killINotify i
bracket obtain release $ \_ ->
forever (threadDelay 50000) -- run forever
118 changes: 69 additions & 49 deletions follow-file.cabal
Original file line number Diff line number Diff line change
@@ -1,53 +1,73 @@
Name: follow-file
Version: 0.0.2
Author: Athan Clark <athan.clark@gmail.com>
Maintainer: Athan Clark <athan.clark@gmail.com>
License: BSD3
License-File: LICENSE
Synopsis: Be notified when a file gets appended, solely with what was added.
Description:
See module for docs
Cabal-Version: >= 1.10
Build-Type: Simple
Category: Filesystem
-- This file has been generated from package.yaml by hpack version 0.21.2.
--
-- see: https://github.com/sol/hpack
--
-- hash: 2237e4036e920a20255f1af7b7256b4210fd90eddba2b0938e9df217cf29ccb7

name: follow-file
version: 0.0.3
synopsis: Be notified when a file gets appended, solely with what was added. Warning - only works on linux and for files that are strictly appended, like log files.
description: Please see the README on Github at <https://github.com/athanclark/follow-file#readme>
category: Filesystem
homepage: https://github.com/athanclark/follow-file#readme
bug-reports: https://github.com/athanclark/follow-file/issues
maintainer: Athan Clark <athan.clark@gmail.com>
license: BSD3
license-file: LICENSE
build-type: Simple
cabal-version: >= 1.10

Library
Default-Language: Haskell2010
HS-Source-Dirs: src
GHC-Options: -Wall
Exposed-Modules: System.File.Follow
Build-Depends: base >= 4.8 && < 5
, attoparsec
, attoparsec-path
, bytestring
, conduit
, directory
, exceptions
, hinotify
, monad-control
, mtl
, path
, text
, unix
, utf8-string
source-repository head
type: git
location: https://github.com/athanclark/follow-file

Executable follow-file
Default-Language: Haskell2010
Hs-Source-Dirs: app
Main-is: Main.hs
Build-Depends: base
, bytestring
, follow-file
, path
, text
, attoparsec
, attoparsec-path
, hinotify
, conduit
, conduit-combinators
, directory
library
exposed-modules:
System.File.Follow
other-modules:
Paths_follow_file
hs-source-dirs:
src
ghc-options: -Wall
build-depends:
attoparsec
, attoparsec-path
, base >=4.11 && <5
, bytestring
, conduit
, directory
, exceptions
, hinotify >=0.4
, monad-control
, mtl
, path
, text
, unix
, utf8-string
default-language: Haskell2010

Source-Repository head
Type: git
Location: https://github.com/athanclark/follow-file
executable follow-file
main-is: Main.hs
other-modules:
Paths_follow_file
hs-source-dirs:
app
ghc-options: -Wall -threaded -rtsopts -Wall -with-rtsopts=-N
build-depends:
attoparsec
, attoparsec-path
, base
, bytestring
, conduit
, conduit-combinators
, directory
, exceptions
, follow-file
, hinotify
, monad-control
, mtl
, path
, text
, unix
, utf8-string
default-language: Haskell2010
41 changes: 0 additions & 41 deletions foo

This file was deleted.

52 changes: 52 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: follow-file
version: 0.0.3
synopsis: Be notified when a file gets appended, solely with what was added. Warning - only works on linux and for files that are strictly appended, like log files.
description: Please see the README on Github at <https://github.com/athanclark/follow-file#readme>
maintainer: Athan Clark <athan.clark@gmail.com>
license: BSD3
github: athanclark/follow-file
category: Filesystem

ghc-options: -Wall

dependencies:
- base >= 4.11 && < 5
- attoparsec
- attoparsec-path
- bytestring
- conduit
- directory
- exceptions
- hinotify >= 0.4
- monad-control
- mtl
- path
- text
- unix
- utf8-string

library:
source-dirs: src

executables:
follow-file:
ghc-options:
- -threaded
- -rtsopts
- -Wall
- -with-rtsopts=-N
main: Main.hs
source-dirs:
- app
dependencies:
- base
- follow-file
- bytestring
- path
- text
- attoparsec
- attoparsec-path
- hinotify
- conduit
- conduit-combinators
- directory
37 changes: 22 additions & 15 deletions src/System/File/Follow.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import qualified Data.ByteString.Lazy.Internal as LBS
import qualified Data.ByteString.Internal as BS
import qualified Data.ByteString.UTF8 as BS8
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Data.Attoparsec.Text (parseOnly, endOfInput)
import Data.Attoparsec.Path (relFilePath)
import Data.Conduit (Producer, yield)
import Data.Conduit (ConduitT, yield)
import Control.Monad (void)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.Catch (MonadMask, bracket)
import Control.Monad.Trans.Control (MonadBaseControl (liftBaseWith))
import Path (Path, Abs, File, filename, parent, toFilePath)
import System.Posix.IO.ByteString (fdReadBuf, openFd, OpenMode (ReadOnly), defaultFileFlags, closeFd, fdSeek)
import System.Posix.Types (FileOffset)
import System.Posix.ByteString.FilePath (RawFilePath)
import System.Posix.Files.ByteString (fileSize, getFileStatus)
import System.Directory (doesFileExist)
import System.INotify (INotify, addWatch, Event (..), EventVariety (..), WatchDescriptor)
Expand All @@ -38,7 +39,7 @@ follow :: ( MonadIO m
)
=> INotify
-> Path Abs File
-> (Producer m BS.ByteString -> m ()) -- ^ Monadic state of @m@ is thrown away for each invocation, not synchronously interleaved.
-> (ConduitT i BS.ByteString m () -> m ()) -- ^ Monadic state of @m@ is thrown away for each invocation, not synchronously interleaved.
-> m WatchDescriptor
follow inotify file f = do
let file' = toFilePath file
Expand Down Expand Up @@ -67,19 +68,25 @@ follow inotify file f = do
stop = do
liftIO (writeIORef positionRef 0)
f (yield mempty)
liftBaseWith $ \runInBase -> addWatch inotify [Modify, Create, Delete] (toFilePath $ parent file) $ \e ->
let isFile filePath = parseOnly (relFilePath <* endOfInput) (T.pack filePath) == Right (filename file)
liftBaseWith $ \runInBase -> addWatch inotify [Modify, Create, Delete] (BS8.fromString $ toFilePath $ parent file) $ \e ->
let isFile :: RawFilePath -> Bool
isFile filePath = parseOnly (relFilePath <* endOfInput) (T.decodeUtf8 filePath) == Right (filename file)
in case e of
Created {filePath} | isFile filePath -> void $ runInBase go
| otherwise -> pure ()
Deleted {filePath} | isFile filePath -> void $ runInBase stop
| otherwise -> pure ()
Modified {maybeFilePath} | (isFile <$> maybeFilePath) == Just True -> void $ runInBase go
| otherwise -> pure ()
MovedIn {filePath} | isFile filePath -> void $ runInBase go
| otherwise -> pure ()
MovedOut {filePath} | isFile filePath -> void $ runInBase go
| otherwise -> pure ()
Created {filePath}
| isFile filePath -> void $ runInBase go
| otherwise -> pure ()
Deleted {filePath}
| isFile filePath -> void $ runInBase stop
| otherwise -> pure ()
Modified {maybeFilePath}
| (isFile <$> maybeFilePath) == Just True -> void $ runInBase go
| otherwise -> pure ()
MovedIn {filePath}
| isFile filePath -> void $ runInBase go
| otherwise -> pure ()
MovedOut {filePath}
| isFile filePath -> void $ runInBase go
| otherwise -> pure ()
DeletedSelf -> error "containing folder deleted"
Unmounted -> error "containing folder unmounted"
QOverflow -> error "queue overflow"
Expand Down
4 changes: 2 additions & 2 deletions stack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# resolver:
# name: custom-snapshot
# location: "./custom-snapshot.yaml"
resolver: nightly-2016-06-30
resolver: lts-12.0

# User packages to be built.
# Various formats can be used as shown in the example below.
Expand All @@ -40,7 +40,7 @@ packages:
# Dependency packages to be pulled from upstream that are not in the resolver
# (e.g., acme-missiles-0.3)
extra-deps:
- attoparsec-path-0.0.0.1
- hinotify-0.4

# Override default flag values for local packages and extra-deps
flags: {}
Expand Down

0 comments on commit 0349891

Please sign in to comment.