-- | Conductor is responsible to control the command-line listener, -- the listener manager and the action to executemoduleShaker.Conductor(initThread,executeCommand)whereimportShaker.TypeimportControl.MonadimportControl.ConcurrentimportShaker.ListenerimportShaker.CliimportqualifiedData.MapasMimportControl.Monad.ReaderimportData.MaybeimportqualifiedControl.ExceptionasC-- | Initialize the master thread -- Once quit is called, all threads are killedinitThread::ShakerIO()initThread=doshIn<-askinput_action<-getInputlift(forkIO(foreverinput_action))>>=addThreadIdToQuitMVarletmain_loop=runReaderTmainThreadshInlift(forkIO(forevermain_loop))>>=addThreadIdToQuitMVarquit_token<-asks(quitToken.threadData)_<-lift$takeMVarquit_tokencleanAllThreads-- | The main thread. mainThread::ShakerIO()mainThread=do(InputStateinputMvtokenMv)<-asksinputState_<-lift$tryPutMVartokenMv42maybe_cmd<-lift$takeMVarinputMvexecuteCommandmaybe_cmddataConductorData=ConductorDataListenState([FileInfo]->IO())initializeConductorData::ShakerIO()->ShakerIOConductorDatainitializeConductorDatafun=doshIn<-asklstState<-initializeListenermapM_addThreadIdToListenMVar$threadIdslstStatelettheFun=\a->runReaderTfunshIn{modifiedInfoFiles=a}return$ConductorDatalstStatetheFuncleanAllThreads::ShakerIO()cleanAllThreads=doasks(threadIdListenList.threadData)>>=cleanThreadsasks(threadIdQuitList.threadData)>>=cleanThreadscleanThreads::ThreadIdList->ShakerIO()cleanThreadsthrdList=lift(readMVarthrdList)>>=lift.mapM_killThread-- | Execute the given action when the modified MVar is filledthreadExecutor::ConductorData->ShakerIO()threadExecutorconductorData=doshIn<-askres<-lift$handleContinuousInterrupt$runReaderT(threadExecutor'conductorData)shInwhenres$threadExecutorconductorDatathreadExecutor'::ConductorData->ShakerIOBoolthreadExecutor'(ConductorDatalistenStatefun)=lift$takeMVar(mvModifiedFileslistenState)>>=fun>>returnTrue-- | Execute Given Command in a new threadexecuteCommand::MaybeCommand->ShakerIO()executeCommandNothing=executeAction[ActionInvalidAction]executeCommand(Just(CommandOneShotact_list))=executeActionact_listexecuteCommand(Just(CommandContinuousact))=initializeConductorData(executeActionact)>>=threadExecutor-- | Execute given actionexecuteAction::[Action]->ShakerIO()executeActionacts=doshIn<-askletallActs=runReaderT(mapM_executeAction'acts)shInlift$handleActionInterruptallActsreturn()-- | Execute a single action with argumentexecuteAction'::Action->ShakerIO()executeAction'(ActionWithArgactKeyarg)=doplMap<-askspluginMaplocal(\shIn->shIn{argument=Justarg})$fromJust$actKey`M.lookup`plMap-- | Execute a single action without argumentexecuteAction'(ActionactKey)=doplMap<-askspluginMapfromJust$actKey`M.lookup`plMap-- * Handlers handleContinuousInterrupt::IOBool->IOBoolhandleContinuousInterrupt=C.handlecatchAllwherecatchAll::C.SomeException->IOBoolcatchAlle=putStrLn("Shaker caught "++showe)>>returnFalsehandleActionInterrupt::IO()->IO()handleActionInterrupt=C.handlecatchAllwherecatchAll::C.SomeException->IO()catchAlle=putStrLn("Shaker caught "++showe)>>return()-- * Mvar with threadId list management-- | Add the given threadId to the listener thread listaddThreadIdToListenMVar::ThreadId->ShakerIO()addThreadIdToListenMVarthrdId=asks(threadIdListenList.threadData)>>=flipaddThreadIdToMVarthrdId-- | Add the given threadId to the quit thread listaddThreadIdToQuitMVar::ThreadId->ShakerIO()addThreadIdToQuitMVarthrdId=asks(threadIdQuitList.threadData)>>=flipaddThreadIdToMVarthrdId-- | Add the given threadId to the mvar listaddThreadIdToMVar::ThreadIdList->ThreadId->ShakerIO()addThreadIdToMVarthrdListthrId=lift$modifyMVar_thrdList(\b->return$thrId:b)