Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error reporting mechanism for os.watch #71

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions os/watch/src/ErrorResponse.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package os.watch

// How to react to service errors
sealed trait ErrorResponse

object ErrorResponse {
// close this instnace of the watch service
case object Close extends ErrorResponse

// Other possibilities
//case object Continue extends ErrorResponse // the caller fixed the state
//case class Restart(paths: Seq[os.Path]) extends ErrorResponse // the caller knows

def defaultHandler(e: WatchError): ErrorResponse = {
println(e)
Close
}
}
6 changes: 6 additions & 0 deletions os/watch/src/WatchError.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package os.watch

sealed trait WatchError

case object Overflow extends WatchError
case class InternalError(t: Throwable) extends WatchError
10 changes: 9 additions & 1 deletion os/watch/src/WatchServiceWatcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ import com.sun.nio.file.SensitivityWatchEventModifier
import scala.collection.mutable
import collection.JavaConverters._

import ErrorResponse.{Close,defaultHandler}

class WatchServiceWatcher(roots: Seq[os.Path],
onEvent: Set[os.Path] => Unit,
logger: (String, Any) => Unit = (_, _) => ()) extends Watcher{
logger: (String, Any) => Unit = (_, _) => (),
onError: WatchError => ErrorResponse = defaultHandler
) extends Watcher{

val nioWatchService = FileSystems.getDefault.newWatchService()
val currentlyWatchedPaths = mutable.Map.empty[os.Path, WatchKey]
Expand Down Expand Up @@ -111,6 +115,10 @@ class WatchServiceWatcher(roots: Seq[os.Path],
case e: ClosedWatchServiceException =>
println("Watcher closed, exiting: " + e)
isRunning.set(false)
case e: Throwable =>
onError(InternalError(e)) match {
case Close => isRunning.set(false)
}
}
}

Expand Down
7 changes: 5 additions & 2 deletions os/watch/src/package.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package os

package object watch{

/**
* Efficiently watches the given `roots` folders for changes. Any time the
* filesystem is modified within those folders, the `onEvent` callback is
Expand Down Expand Up @@ -29,9 +30,11 @@ package object watch{
*/
def watch(roots: Seq[os.Path],
onEvent: Set[os.Path] => Unit,
logger: (String, Any) => Unit = (_, _) => ()): AutoCloseable = {
logger: (String, Any) => Unit = (_, _) => (),
onError: WatchError => ErrorResponse = ErrorResponse.defaultHandler
): AutoCloseable = {
val watcher = System.getProperty("os.name") match{
case "Linux" => new os.watch.WatchServiceWatcher(roots, onEvent, logger)
case "Linux" => new os.watch.WatchServiceWatcher(roots, onEvent, logger, onError)
case "Mac OS X" => new os.watch.FSEventsWatcher(roots, onEvent, logger, 0.05)
case osName => throw new Exception(s"watch not supported on operating system: $osName")
}
Expand Down
19 changes: 18 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -1603,7 +1603,10 @@ sha.stdout.trim ==> "acc142175fa520a1cb2be5b97cbbe9bea092e8bba3fe2e95afa64561590
#### os.watch.watch

```scala
os.watch.watch(roots: Seq[os.Path], onEvent: Set[os.Path] => Unit): Unit
os.watch.watch(roots: Seq[os.Path],
onEvent: Set[os.Path] => Unit,
onError: os.watch.WatchError => os.watch.ErrorResponse = os.watch.ErrorResponse.defaultHandler
): Unit
```
```scala
// SBT
Expand Down Expand Up @@ -1637,6 +1640,20 @@ changes happening within the watched roots folder, apart from the path
at which the change happened. It is up to the `onEvent` handler to query
the filesystem and figure out what happened, and what it wants to do.

Possible errors (passed to the `onError` callback)

- `WatchError.Overflow` The system is producing events faster
than the application is able to handle them.

- `WatchError.InternalError(e: Throwable)` For example: removing a drive
with watched paths.

Error responses:

- `ErrorResponse.Close`. Terminate this instance of the watch service

The default error handler prints an error message and returns `ErrorResponse.Close`

Here is an example of use from the Ammonite REPL:

```scala
Expand Down