flix

0.78.0

Env.flix

/*
 *  Copyright 2021 Ramiro Calle
 *
 * Use of this source code is governed by the Apache 2.0 license
 * that can be found in the LICENSE.md file.
 */

///
/// Effect for accessing the system environment, and its functions.
///
pub mod Sys.Env {

    use Sys.Env

    import dev.flix.runtime.Global
    import java.lang.NullPointerException
    import java.lang.Runtime
    import java.lang.SecurityException
    import java.lang.System
    import java.io.{File => JFile}
    import java.util.{Iterator => JIterator}
    import java.util.{Map => JMap}
    import java.util.{Set => JSet}
    import java.util.{Map$Entry => Entry}

    ///
    /// Effect for accessing the system environment.
    ///
    pub eff Env {

        ///
        /// Returns the arguments passed to the program via the command line.
        ///
        def getArgs(): List[String]

        ///
        /// Returns an map of the current system environment.
        ///
        def getEnv(): Map[String, String]

        ///
        /// Returns the value of the specified environment variable.
        ///
        def getVar(name: String): Option[String]

        ///
        /// Returns the system property by name.
        ///
        def getProp(name: String): Option[String]

        ///
        /// Returns the operating system name
        ///
        def getOsName(): Option[String]

        ///
        /// Returns the operating system architecture
        ///
        def getOsArch(): Option[String]

        ///
        /// Returns the operating system version
        ///
        def getOsVersion(): Option[String]

        ///
        /// Returns the file separator
        ///
        def getFileSeparator(): String

        ///
        /// Returns the path separator
        ///
        def getPathSeparator(): String

        ///
        /// Returns the system line separator
        ///
        def getLineSeparator(): String

        ///
        /// Returns the user's current working directory
        ///
        def getCurrentWorkingDirectory(): Option[String]

        ///
        /// Returns the default temp file path
        ///
        def getTemporaryDirectory(): Option[String]

        ///
        /// Returns the user's account name
        ///
        def getUserName(): Option[String]

        ///
        /// Returns the user's home directory
        ///
        def getUserHomeDirectory(): Option[String]

        ///
        /// Returns the number of virtual processors available to the JVM.
        ///
        /// The returned value is never less than one.
        ///
        def getVirtualProcessors(): Int32

    }

    ///
    /// Handles the `Env` effect of the given function `f`.
    ///
    /// In other words, re-interprets the `Env` effect using the `IO` effect.
    ///
    pub def handle(f: a -> b \ ef): a -> b \ (ef - Env) + IO = x ->
        run {
            f(x)
        } with handler Env {
            def getArgs(k) =
                k(Array.toList(Global.getArgs()))

            def getEnv(k) = region rc {
                try {
                    let _ = rc;
                    let iter: JIterator[Entry[String, String]] = unsafe IO as rc { System.getenv().entrySet().iterator() };
                    k(getEnvHelper(iter, Map.empty()))
                } catch {
                    case _: SecurityException => k(Map.empty())
                }
            }

            def getVar(name, k) =
                try {
                    let result = System.getenv(name);
                    k(Object.toOption(result))
                } catch {
                    case _: SecurityException    => k(None)
                    case _: NullPointerException => k(None)
                }

            def getProp(name, k)              = k(getPropHelper(name))

            def getOsName(k)                  = k(getPropHelper("os.name"))

            def getOsArch(k)                  = k(getPropHelper("os.arch"))

            def getOsVersion(k)               = k(getPropHelper("os.version"))

            def getFileSeparator(k)           = k(JFile.separator)

            def getPathSeparator(k)           = k(JFile.pathSeparator)

            def getLineSeparator(k)           = k(System.lineSeparator())

            def getCurrentWorkingDirectory(k) = k(getPropHelper("user.dir"))

            def getTemporaryDirectory(k)      = k(getPropHelper("java.io.tmpdir"))

            def getUserName(k)                = k(getPropHelper("user.name"))

            def getUserHomeDirectory(k)       = k(getPropHelper("user.home"))

            def getVirtualProcessors(k)       = k(Runtime.getRuntime().availableProcessors())
        }

    ///
    /// Runs the `Env` effect of the given function `f`.
    ///
    /// In other words, re-interprets the `Env` effect using the `IO` effect.
    ///
    @DefaultHandler
    pub def runWithIO(f: Unit -> a \ ef): a \ (ef - Env) + IO = handle(f)()

    def getPropHelper(name: String): Option[String] \ IO =
        try {
            let result = System.getProperty(name);
            Object.toOption(result)
        } catch {
            case _: SecurityException    => None
            case _: NullPointerException => None
        }

    def getEnvHelper(iter: JIterator[Entry[String, String]], m: Map[String, String]): Map[String, String] \ IO =
        if (iter.hasNext())
            let e: Entry[String, String] = iter.next();
            let k: String = e.getKey();
            let v: String = e.getValue();
            getEnvHelper(iter, Map.insert(k, v, m))
        else
            m

}