Skip to content

Add hooks into serializing/constructing os.Paths and launching subprocesses #365

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

Merged
merged 5 commits into from
Mar 4, 2025
Merged
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
7 changes: 7 additions & 0 deletions os/src-jvm/ResourceApi.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package os
trait ResourceApi {
def resource(implicit resRoot: ResourceRoot = Thread.currentThread().getContextClassLoader) = {
os.ResourcePath.resource(resRoot)
}

}
83 changes: 0 additions & 83 deletions os/src-jvm/package.scala

This file was deleted.

2 changes: 2 additions & 0 deletions os/src-native/ResourceApi.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
package os
trait ResourceApi
48 changes: 34 additions & 14 deletions os/src/Path.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import scala.language.implicitConversions
import acyclic.skipped
import os.PathError.{InvalidSegment, NonCanonicalLiteral}

import scala.util.Try //needed for cross-version defined macros
import scala.util.{DynamicVariable, Try} //needed for cross-version defined macros

trait PathChunk {
def segments: Seq[String]
Expand Down Expand Up @@ -438,6 +438,31 @@ object SubPath extends SubPathMacros {
}

object Path extends PathMacros {
@experimental trait Serializer {
def serializeString(p: os.Path): String
def serializeFile(p: os.Path): java.io.File
def serializePath(p: os.Path): java.nio.file.Path
def deserialize(s: String): java.nio.file.Path
def deserialize(s: java.io.File): java.nio.file.Path
def deserialize(s: java.nio.file.Path): java.nio.file.Path
def deserialize(s: java.net.URI): java.nio.file.Path
}
@experimental val pathSerializer = new DynamicVariable[Serializer](defaultPathSerializer)
@experimental object defaultPathSerializer extends Serializer {
def serializeString(p: os.Path): String = p.wrapped.toString
def serializeFile(p: os.Path): java.io.File = p.wrapped.toFile
def serializePath(p: os.Path): java.nio.file.Path = p.wrapped
def deserialize(s: String) = Paths.get(s)
def deserialize(s: java.io.File) = Paths.get(s.getPath)
def deserialize(s: java.nio.file.Path) = s
def deserialize(s: java.net.URI) = s.getScheme() match {
case "file" => Paths.get(s)
case uriType =>
throw new IllegalArgumentException(
s"""os.Path can only be created from a "file" URI scheme, but found "${uriType}""""
)
}
}
def apply(p: FilePath, base: Path) = p match {
case p: RelPath => base / p
case p: SubPath => base / p
Expand Down Expand Up @@ -562,7 +587,7 @@ class Path private[os] (val wrapped: java.nio.file.Path)
val resolved = wrapped.resolve(chunk.toString).normalize()
new Path(resolved)
}
override def toString = wrapped.toString
override def toString = Path.pathSerializer.value.serializeString(this)

override def equals(o: Any): Boolean = o match {
case p: Path => wrapped.equals(p.wrapped)
Expand Down Expand Up @@ -593,8 +618,8 @@ class Path private[os] (val wrapped: java.nio.file.Path)
new RelPath(segments.drop(nonUpIndex), nonUpIndex)
}

def toIO: java.io.File = wrapped.toFile
def toNIO: java.nio.file.Path = wrapped
def toIO: java.io.File = Path.pathSerializer.value.serializeFile(this)
def toNIO: java.nio.file.Path = Path.pathSerializer.value.serializePath(this)

def resolveFrom(base: os.Path) = this

Expand All @@ -608,23 +633,18 @@ sealed trait PathConvertible[T] {

object PathConvertible {
implicit object StringConvertible extends PathConvertible[String] {
def apply(t: String) = Paths.get(t)
def apply(t: String) = Path.pathSerializer.value.deserialize(t)
}
implicit object JavaIoFileConvertible extends PathConvertible[java.io.File] {
def apply(t: java.io.File) = Paths.get(t.getPath)
def apply(t: java.io.File) = Path.pathSerializer.value.deserialize(t)
}
implicit object NioPathConvertible extends PathConvertible[java.nio.file.Path] {
def apply(t: java.nio.file.Path) = t
def apply(t: java.nio.file.Path) = Path.pathSerializer.value.deserialize(t)
override def isCustomFs(t: java.nio.file.Path): Boolean =
t.getFileSystem() != java.nio.file.FileSystems.getDefault()
}
implicit object UriPathConvertible extends PathConvertible[URI] {
def apply(uri: URI) = uri.getScheme() match {
case "file" => Paths.get(uri)
case uriType =>
throw new IllegalArgumentException(
s"""os.Path can only be created from a "file" URI scheme, but found "${uriType}""""
)
}
def apply(uri: URI) = Path.pathSerializer.value.deserialize(uri)

}
}
8 changes: 6 additions & 2 deletions os/src/ProcessOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,9 @@ case class ProcGroup private[os] (commands: Seq[proc]) {
def pipeTo(next: proc) = ProcGroup(commands :+ next)
}

private[os] object ProcessOps {
@experimental
object ProcessOps {
val spawnHook = new scala.util.DynamicVariable[os.Path => Unit]({ p => () })
def buildProcess(
command: Seq[String],
cwd: Path = null,
Expand Down Expand Up @@ -644,7 +646,9 @@ private[os] object ProcessOps {

addToProcessEnv(env)

builder.directory(Option(cwd).getOrElse(os.pwd).toIO)
val dir = Option(cwd).getOrElse(os.pwd)
builder.directory(dir.toIO)
spawnHook.value.apply(dir)

builder
.command(command: _*)
Expand Down
5 changes: 4 additions & 1 deletion os/src-native/package.scala → os/src/package.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import scala.language.implicitConversions
import java.nio.file.FileSystem
import java.nio.file.FileSystems
import java.nio.file.Paths
import scala.util.DynamicVariable
package object os {

package object os extends ResourceApi {
type Generator[+T] = geny.Generator[T]
val Generator = geny.Generator
implicit def GlobSyntax(s: StringContext): GlobInterpolator = new GlobInterpolator(s)
Expand Down
Loading