From fbfef5a05d42db46a503e7daab0063130a9b34fc Mon Sep 17 00:00:00 2001 From: Decio Ferreira Date: Fri, 28 Feb 2025 23:43:19 +0000 Subject: [PATCH] Refactor IO functions to use Http tasks for file operations and process management --- bin/index.js | 587 ++++++++++++------------ src/Builder/File.elm | 59 ++- src/Builder/Http.elm | 171 +++++-- src/Compiler/Json/Encode.elm | 4 +- src/System/Exit.elm | 15 +- src/System/IO.elm | 860 ++++++----------------------------- src/System/Process.elm | 180 +++++--- src/Utils/Main.elm | 455 +++++++++++++++++- 8 files changed, 1161 insertions(+), 1170 deletions(-) diff --git a/bin/index.js b/bin/index.js index c15c881c0..d7fbfe6e5 100755 --- a/bin/index.js +++ b/bin/index.js @@ -3,31 +3,299 @@ const { newMockXhr } = require('mock-xmlhttprequest'); const MockXhr = newMockXhr(); -// Mock JSON response -MockXhr.onSend = (request) => { - switch (request.url) { - case "getLine": +const handlers = { + getLine: (request) => { rl.on("line", (value) => { request.respond(200, {}, value); }); - break; - case "hPutStr": + }, + hPutStr: (request) => { const { fd, content } = JSON.parse(request.body); fs.write(fd, content, (err) => { if (err) throw err; - request.respond(200, {}, ""); + request.respond(200); + }); + }, + writeString: (request) => { + let { path, content } = JSON.parse(request.body); + fs.writeFile(path, content, (err) => { + if (err) throw err; + request.respond(200); + }); + }, + read: (request) => { + fs.readFile(request.body, (err, data) => { + if (err) throw err; + request.respond(200, null, data.toString()); + }); + }, + readStdin: (request) => { + fs.readFile(0, (err, data) => { + if (err) throw err; + request.respond(200, null, data.toString()); + }); + }, + getArchive: (request) => { + download.apply({ + send: ({ sha, archive }) => { + request.respond(200, null, JSON.stringify({ sha, archive })); + } + }, + // FIXME hardcoded index 0 + [0, "GET", request.body]); + }, + httpUpload: (request) => { + const { urlStr, headers, parts } = JSON.parse(request.body); + const url = new URL(urlStr); + const client = url.protocol == "https:" ? https : http; + + const form = new FormData(); + + parts.forEach((part) => { + switch (part.type) { + case "FilePart": + form.append(part.name, fs.createReadStream(part.filePath)); + break; + + case "JsonPart": + form.append(part.name, JSON.stringify(part.value), { + contentType: "application/json", + filepath: part.filePath, + }); + break; + + case "StringPart": + form.append(part.name, part.string); + break; + } + }); + + const req = client.request(url, { + method: "POST", + headers: { ...headers, ...form.getHeaders() }, + }); + + form.pipe(req); + + req.on("response", (res) => { + res.on("end", () => { + request.respond(200); + }); + }); + + req.on("error", (err) => { + throw err; + }); + }, + withFile: (request) => { + let { filename, mode } = JSON.parse(request.body); + fs.open(filename, mode, (err, fd) => { + if (err) throw err; + request.respond(200, null, fd); + }); + }, + hFileSize: (request) => { + fs.fstat(request.body, (err, stats) => { + if (err) throw err; + request.respond(200, null, stats.size); + }); + }, + withCreateProcess: (request) => { + let { createProcess } = JSON.parse(request.body); + tmp.file((err, path, fd, cleanupCallback) => { + if (err) throw err; + + const reader = fs.createReadStream(path); + + reader.on("open", (_fd) => { + nextCounter += 1; + processes[nextCounter] = child_process.spawn( + createProcess.cmdspec.cmd, + createProcess.cmdspec.args, + { + stdio: [ + createProcess.stdin, + createProcess.stdout, + createProcess.stderr, + ], + } + ); + + request.respond(200, null, JSON.stringify({ stdinHandle: fd, ph: nextCounter })); + }); + + reader.on("data", (chunk) => { + processes[nextCounter].stdin.end(chunk); }); - break; + }); + }, + hClose: (request) => { + fs.close(request.body); + request.respond(200); + }, + waitForProcess: (request) => { + processes[request.body].on("exit", (code) => { + request.respond(200, null, code); + }); + }, + exitWith: (request) => { + rl.close(); + process.exit(request.body); + }, + dirFindExecutable: (request) => { + request.respond(200, null, which.sync(request.body, { nothrow: true })); + }, + replGetInputLine: (request) => { + rl.question(request.body, (value) => { + request.respond(200, null, value); + }); + }, + dirDoesFileExist: (request) => { + fs.stat(request.body, (err, stats) => { + request.respond(200, null, !err && stats.isFile()); + }); + }, + dirCreateDirectoryIfMissing: (request) => { + const { createParents, filename } = JSON.parse(request.body); + fs.mkdir(filename, { recursive: createParents }, (err) => { + request.respond(200); + }); + }, + lockFile: (request) => { + const path = request.body; + + if (lockedFiles[path]) { + lockedFiles[path].subscribers.push(request); + } else { + lockedFiles[path] = { subscribers: [] }; + request.respond(200); + } + }, + unlockFile: (request) => { + const path = request.body; + + if (lockedFiles[path]) { + const subscriber = lockedFiles[path].subscribers.shift(); + + if (subscriber) { + subscriber.respond(200); + } else { + delete lockedFiles[path]; + } + + request.respond(200); + } else { + console.error(`Could not find locked file "${path}"!`); + rl.close(); + process.exit(255); + } + }, + dirGetModificationTime: (request) => { + fs.stat(request.body, (err, stats) => { + if (err) throw err; + request.respond(200, null, parseInt(stats.mtimeMs, 10)); + }); + }, + + dirDoesDirectoryExist: (request) => { + fs.stat(request.body, (err, stats) => { + request.respond(200, null, !err && stats.isDirectory()); + }); + }, + dirCanonicalizePath: (request) => { + request.respond(200, null, resolve(request.body)); + }, + dirListDirectory: (request) => { + fs.readdir(request.body, { recursive: false }, (err, files) => { + if (err) throw err; + request.respond(200, null, JSON.stringify(files)); + }); + }, + binaryDecodeFileOrFail: (request) => { + fs.readFile(request.body, (err, data) => { + if (err) throw err; + request.respond(200, null, data.toString()); + }); + }, + write: (request) => { + const { fd, content } = JSON.parse(request.body); + fs.writeFile(fd, JSON.stringify(content), (err) => { + if (err) throw err; + request.respond(200); + }); + }, + dirRemoveFile: (request) => { + fs.unlink(request.body, (err) => { + if (err) throw err; + request.respond(200); + }); + }, + dirRemoveDirectoryRecursive: (request) => { + fs.rm(request.body, { recursive: true, force: true }, (err) => { + if (err) throw err; + request.respond(200); + }); + }, + dirWithCurrentDirectory: (request) => { + try { + process.chdir(request.body); + request.respond(200); + } catch (err) { + console.error(`chdir: ${err}`); + } } }; -// const savedXMLHttpRequest = globalThis.XMLHttpRequest; -// Install in the global context so "new XMLHttpRequest()" creates MockXhr instances -globalThis.XMLHttpRequest = MockXhr; +const savedXMLHttpRequest = globalThis.XMLHttpRequest; +MockXhr.onSend = (request) => { + const handler = handlers[request.url]; + + if (handler) { + handler(request); + } else { + const url = new URL(request.url); + const client = url.protocol == "https:" ? https : http; + + const req = client.request(url, { + method: request.method, + headers: request.requestHeaders + }, (res) => { + let chunks = []; + + res.on("data", (chunk) => { + chunks.push(chunk); + }); + + res.on("end", () => { + const buffer = Buffer.concat(chunks); + const encoding = res.headers["content-encoding"]; + + if (encoding == "gzip") { + zlib.gunzip(buffer, (err, decoded) => { + if (err) throw err; + request.respond(200, null, decoded && decoded.toString()); + }); + } else if (encoding == "deflate") { + zlib.inflate(buffer, (err, decoded) => { + if (err) throw err; + request.respond(200, null, decoded && decoded.toString()); + }); + } else { + request.respond(200, null, buffer.toString()); + } + }); + }); -// Do something that send()s an XMLHttpRequest to '/my/url' and returns a Promise -// that resolves to the parsed JSON response + req.on("error", (err) => { + throw err; + }); + + req.end(); + } +}; +// Install in the global context so "new XMLHttpRequest()" creates MockXhr instances +globalThis.XMLHttpRequest = MockXhr; const fs = require("node:fs"); const child_process = require("node:child_process"); @@ -75,7 +343,7 @@ const download = function (index, method, url) { }; }); - this.send({ index, value: [sha, archive] }); + this.send({ index, sha, archive }); }); } else if (res.headers.location) { download.apply(this, [index, method, res.headers.location]); @@ -97,293 +365,4 @@ const app = Elm.Terminal.Main.init({ homedir: os.homedir(), progName: "guida" } -}); - - -app.ports.sendWriteString.subscribe(function ({ index, path, content }) { - fs.writeFile(path, content, (err) => { - if (err) throw err; - app.ports.recvWriteString.send(index); - }); -}); - -app.ports.sendRead.subscribe(function ({ index, fd }) { - fs.readFile(fd, (err, data) => { - if (err) throw err; - app.ports.recvRead.send({ index, value: data.toString() }); - }); -}); - -app.ports.sendReadStdin.subscribe(function ({ index }) { - fs.readFile(0, (err, data) => { - if (err) throw err; - app.ports.recvReadStdin.send({ index, value: data.toString() }); - }); -}); - -app.ports.sendHttpFetch.subscribe(function ({ index, method, urlStr, headers }) { - const url = new URL(urlStr); - const client = url.protocol == "https:" ? https : http; - - const req = client.request(url, { - method - , headers: headers.reduce((acc, [key, value]) => { acc[key] = value; return acc }, {}) - }, (res) => { - let chunks = []; - - res.on("data", (chunk) => { - chunks.push(chunk); - }); - - res.on("end", () => { - const buffer = Buffer.concat(chunks); - const encoding = res.headers["content-encoding"]; - - if (encoding == "gzip") { - zlib.gunzip(buffer, (err, decoded) => { - if (err) throw err; - app.ports.recvHttpFetch.send({ index, value: decoded && decoded.toString() }); - }); - } else if (encoding == "deflate") { - zlib.inflate(buffer, (err, decoded) => { - if (err) throw err; - app.ports.recvHttpFetch.send({ index, value: decoded && decoded.toString() }); - }); - } else { - app.ports.recvHttpFetch.send({ index, value: buffer.toString() }); - } - }); - }); - - req.on("error", (err) => { - throw err; - }); - - req.end(); -}); - -app.ports.sendGetArchive.subscribe(function ({ index, method, url }) { - download.apply(app.ports.recvGetArchive, [index, method, url]); -}); - -app.ports.sendHttpUpload.subscribe(function ({ index, urlStr, headers, parts }) { - const url = new URL(urlStr); - const client = url.protocol == "https:" ? https : http; - - const form = new FormData(); - - parts.forEach((part) => { - switch (part.type) { - case "FilePart": - form.append(part.name, fs.createReadStream(part.filePath)); - break; - - case "JsonPart": - form.append(part.name, JSON.stringify(part.value), { - contentType: "application/json", - filepath: part.filePath, - }); - break; - - case "StringPart": - form.append(part.name, part.string); - break; - } - }); - - const req = client.request(url, { - method: "POST", - headers: { ...headers, ...form.getHeaders() }, - }); - - form.pipe(req); - - req.on("response", (res) => { - res.on("end", () => { - app.ports.recvHttpUpload.send(index); - }); - }); - - req.on("error", (err) => { - throw err; - }); -}); - -app.ports.sendHFlush.subscribe(function ({ index, fd }) { - app.ports.recvHFlush.send(index); -}); - -app.ports.sendWithFile.subscribe(function ({ index, filename, mode }) { - fs.open(filename, mode, (err, fd) => { - if (err) throw err; - app.ports.recvWithFile.send({ index, value: fd }); - }); -}); - -app.ports.sendHFileSize.subscribe(function ({ index, fd }) { - fs.fstat(fd, (err, stats) => { - if (err) throw err; - app.ports.recvHFileSize.send({ index, value: stats.size }); - }); -}); - -app.ports.sendProcWithCreateProcess.subscribe(function ({ index, createProcess }) { - tmp.file((err, path, fd, cleanupCallback) => { - if (err) throw err; - - const reader = fs.createReadStream(path); - - reader.on("open", (_fd) => { - nextCounter += 1; - processes[nextCounter] = child_process.spawn( - createProcess.cmdspec.cmd, - createProcess.cmdspec.args, - { - stdio: [ - createProcess.stdin, - createProcess.stdout, - createProcess.stderr, - ], - } - ); - - app.ports.recvProcWithCreateProcess.send({ index, value: { stdinHandle: fd, ph: nextCounter } }); - }); - - reader.on("data", (chunk) => { - processes[nextCounter].stdin.end(chunk); - }); - }); -}); - -app.ports.sendHClose.subscribe(function ({ index, fd }) { - fs.close(fd); - app.ports.recvHClose.send(index); -}); - -app.ports.sendProcWaitForProcess.subscribe(function ({ index, ph }) { - processes[ph].on("exit", (code) => { - app.ports.recvProcWaitForProcess.send({ index, value: code }); - }); -}); - -app.ports.sendExitWith.subscribe(function (code) { - rl.close(); - process.exit(code); -}); - -app.ports.sendDirFindExecutable.subscribe(function ({ index, name }) { - app.ports.recvDirFindExecutable.send({ index, value: which.sync(name, { nothrow: true }) }); -}); - -app.ports.sendReplGetInputLine.subscribe(function ({ index, prompt }) { - rl.question(prompt, (value) => { - app.ports.recvReplGetInputLine.send({ index, value }); - }); -}); - -app.ports.sendDirDoesFileExist.subscribe(function ({ index, filename }) { - fs.stat(filename, (err, stats) => { - app.ports.recvDirDoesFileExist.send({ index, value: !err && stats.isFile() }); - }); -}); - -app.ports.sendDirCreateDirectoryIfMissing.subscribe(function ({ index, createParents, filename }) { - fs.mkdir(filename, { recursive: createParents }, (err) => { - app.ports.recvDirCreateDirectoryIfMissing.send(index); - }); -}); - -app.ports.sendLockFile.subscribe(function ({ index, path }) { - if (lockedFiles[path]) { - lockedFiles[path].subscribers.push(index); - } else { - lockedFiles[path] = { subscribers: [] }; - app.ports.recvLockFile.send(index); - } -}); - -app.ports.sendUnlockFile.subscribe(function ({ index, path }) { - if (lockedFiles[path]) { - const subscriber = lockedFiles[path].subscribers.shift(); - - if (subscriber) { - app.ports.recvUnlockFile.send(subscriber); - } else { - delete lockedFiles[path]; - } - - app.ports.recvUnlockFile.send(index); - } else { - console.error(`Could not find locked file "${path}"!`); - rl.close(); - process.exit(255); - } -}); - -app.ports.sendDirGetModificationTime.subscribe(function ({ index, filename }) { - fs.stat(filename, (err, stats) => { - if (err) throw err; - app.ports.recvDirGetModificationTime.send({ index, value: parseInt(stats.mtimeMs, 10) }); - }); -}); - -app.ports.sendDirDoesDirectoryExist.subscribe(function ({ index, path }) { - fs.stat(path, (err, stats) => { - app.ports.recvDirDoesDirectoryExist.send({ index, value: !err && stats.isDirectory() }); - }); -}); - -app.ports.sendDirCanonicalizePath.subscribe(function ({ index, path }) { - app.ports.recvDirCanonicalizePath.send({ index, value: resolve(path) }); -}); - -app.ports.sendDirListDirectory.subscribe(function ({ index, path }) { - fs.readdir(path, { recursive: false }, (err, files) => { - if (err) throw err; - app.ports.recvDirListDirectory.send({ index, value: files }); - }); -}); - -app.ports.sendBinaryDecodeFileOrFail.subscribe(function ({ index, filename }) { - fs.readFile(filename, (err, data) => { - if (err) throw err; - app.ports.recvBinaryDecodeFileOrFail.send({ index, value: JSON.parse(data.toString()) }); - }); -}); - -app.ports.sendWrite.subscribe(function ({ index, fd, content }) { - fs.writeFile(fd, JSON.stringify(content), (err) => { - if (err) throw err; - app.ports.recvWrite.send(index); - }); -}); - -app.ports.sendDirRemoveFile.subscribe(function ({ index, path }) { - fs.unlink(path, (err) => { - if (err) throw err; - app.ports.recvDirRemoveFile.send(index); - }); -}); - -app.ports.sendDirRemoveDirectoryRecursive.subscribe(function ({ index, path }) { - fs.rm(path, { recursive: true, force: true }, (err) => { - if (err) throw err; - app.ports.recvDirRemoveDirectoryRecursive.send(index); - }); -}); - -app.ports.sendDirWithCurrentDirectory.subscribe(function ({ index, path }) { - try { - process.chdir(path); - app.ports.recvDirWithCurrentDirectory.send(index); - } catch (err) { - console.error(`chdir: ${err}`); - } -}); - -app.ports.sendReplGetInputLineWithInitial.subscribe(function ({ index, prompt, left, right }) { - rl.question(prompt + left + right, (value) => { - app.ports.recvReplGetInputLineWithInitial.send({ index, value }); - }); -}); +}); \ No newline at end of file diff --git a/src/Builder/File.elm b/src/Builder/File.elm index 6eb6c6857..5b27a252b 100644 --- a/src/Builder/File.elm +++ b/src/Builder/File.elm @@ -16,6 +16,7 @@ module Builder.File exposing ) import Codec.Archive.Zip as Zip +import Http import Json.Decode as Decode import Json.Encode as Encode import System.IO as IO exposing (IO(..)) @@ -95,8 +96,8 @@ readBinary decoder path = writeUtf8 : FilePath -> String -> IO () -writeUtf8 path content = - IO (\_ s -> ( s, IO.WriteString IO.pure path content )) +writeUtf8 = + IO.writeString @@ -105,12 +106,58 @@ writeUtf8 path content = readUtf8 : FilePath -> IO String readUtf8 path = - IO (\_ s -> ( s, IO.Read IO.pure path )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "read" + , body = Http.stringBody "text/plain" path + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + Ok (IO.pure body) + + _ -> + Ok (IO.pure "") + ) + , timeout = Nothing + } + ) + ) + ) readStdin : IO String readStdin = - IO (\_ s -> ( s, IO.ReadStdin IO.pure )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "readStdin" + , body = Http.emptyBody + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + Ok (IO.pure body) + + _ -> + Ok (IO.pure "") + ) + , timeout = Nothing + } + ) + ) + ) @@ -118,8 +165,8 @@ readStdin = writeBuilder : FilePath -> String -> IO () -writeBuilder path builder = - IO (\_ s -> ( s, IO.WriteString IO.pure path builder )) +writeBuilder = + IO.writeString diff --git a/src/Builder/Http.elm b/src/Builder/Http.elm index 45e85fef2..b139db184 100644 --- a/src/Builder/Http.elm +++ b/src/Builder/Http.elm @@ -24,10 +24,12 @@ module Builder.Http exposing import Basics.Extra exposing (uncurry) import Codec.Archive.Zip as Zip import Compiler.Elm.Version as V +import Http import Json.Decode as Decode import Json.Encode as Encode import System.IO as IO exposing (IO(..)) import Url.Builder +import Utils.Crash exposing (crash) import Utils.Main as Utils exposing (SomeException) @@ -94,37 +96,40 @@ type alias Header = get : Manager -> String -> List Header -> (Error -> e) -> (String -> IO (Result e a)) -> IO (Result e a) get = - fetch MethodGet + fetch "GET" post : Manager -> String -> List Header -> (Error -> e) -> (String -> IO (Result e a)) -> IO (Result e a) post = - fetch MethodPost + fetch "POST" -type Method - = MethodGet - | MethodPost - - -fetch : Method -> Manager -> String -> List Header -> (Error -> e) -> (String -> IO (Result e a)) -> IO (Result e a) -fetch methodVerb _ url headers _ onSuccess = +fetch : String -> Manager -> String -> List Header -> (Error -> e) -> (String -> IO (Result e a)) -> IO (Result e a) +fetch method _ url headers _ onSuccess = IO (\_ s -> ( s - , IO.HttpFetch IO.pure - (case methodVerb of - MethodGet -> - "GET" - - MethodPost -> - "POST" + , IO.ImpureTask + (Http.task + { method = method + , headers = List.map (\( a, b ) -> Http.header a b) (addDefaultHeaders headers) + , url = url + , body = Http.emptyBody + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + Ok (onSuccess body) + + _ -> + Ok (onSuccess "") + ) + , timeout = Nothing + } ) - url - (addDefaultHeaders headers) ) ) - |> IO.bind onSuccess addDefaultHeaders : List Header -> List Header @@ -171,8 +176,51 @@ shaToChars = getArchive : Manager -> String -> (Error -> e) -> e -> (( Sha, Zip.Archive ) -> IO (Result e a)) -> IO (Result e a) getArchive _ url _ _ onSuccess = - IO (\_ s -> ( s, IO.GetArchive IO.pure "GET" url )) - |> IO.bind (\shaAndArchive -> onSuccess shaAndArchive) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "getArchive" + , body = Http.stringBody "text/plain" url + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + let + shaAndArchiveResult = + Decode.decodeString + (Decode.map2 Tuple.pair + (Decode.field "sha" Decode.string) + (Decode.field "archive" + (Decode.list + (Decode.map2 Zip.Entry + (Decode.field "eRelativePath" Decode.string) + (Decode.field "eData" Decode.string) + ) + ) + ) + ) + body + in + case shaAndArchiveResult of + Ok shaAndArchive -> + Ok (onSuccess shaAndArchive) + + Err _ -> + crash "getArchive" + + _ -> + crash "getArchive" + ) + , timeout = Nothing + } + ) + ) + ) @@ -190,35 +238,58 @@ upload _ url parts = IO (\_ s -> ( s - , IO.HttpUpload IO.pure - url - (addDefaultHeaders []) - (List.map - (\part -> - case part of - FilePart name filePath -> - Encode.object - [ ( "type", Encode.string "FilePart" ) - , ( "name", Encode.string name ) - , ( "filePath", Encode.string filePath ) - ] - - JsonPart name filePath value -> - Encode.object - [ ( "type", Encode.string "JsonPart" ) - , ( "name", Encode.string name ) - , ( "filePath", Encode.string filePath ) - , ( "value", value ) - ] - - StringPart name string -> - Encode.object - [ ( "type", Encode.string "StringPart" ) - , ( "name", Encode.string name ) - , ( "string", Encode.string string ) - ] - ) - parts + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "httpUpload" + , body = + Http.jsonBody + (Encode.object + [ ( "urlStr", Encode.string url ) + , ( "headers", Encode.object (List.map (Tuple.mapSecond Encode.string) (addDefaultHeaders [])) ) + , ( "parts" + , Encode.list + (\part -> + case part of + FilePart name filePath -> + Encode.object + [ ( "type", Encode.string "FilePart" ) + , ( "name", Encode.string name ) + , ( "filePath", Encode.string filePath ) + ] + + JsonPart name filePath value -> + Encode.object + [ ( "type", Encode.string "JsonPart" ) + , ( "name", Encode.string name ) + , ( "filePath", Encode.string filePath ) + , ( "value", value ) + ] + + StringPart name string -> + Encode.object + [ ( "type", Encode.string "StringPart" ) + , ( "name", Encode.string name ) + , ( "string", Encode.string string ) + ] + ) + parts + ) + ] + ) + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (IO.pure ()) + + _ -> + crash "httpUpload" + ) + , timeout = Nothing + } ) ) ) diff --git a/src/Compiler/Json/Encode.elm b/src/Compiler/Json/Encode.elm index eae85f424..f79edb0c0 100644 --- a/src/Compiler/Json/Encode.elm +++ b/src/Compiler/Json/Encode.elm @@ -220,8 +220,8 @@ writeUgly path value = {-| FIXME Builder.File.writeBuilder -} fileWriteBuilder : String -> String -> IO () -fileWriteBuilder path value = - IO (\_ s -> ( s, IO.WriteString IO.pure path value )) +fileWriteBuilder = + IO.writeString diff --git a/src/System/Exit.elm b/src/System/Exit.elm index 226df0bcb..fd4a1fa30 100644 --- a/src/System/Exit.elm +++ b/src/System/Exit.elm @@ -5,7 +5,9 @@ module System.Exit exposing , exitWith ) +import Http import System.IO as IO exposing (IO(..)) +import Utils.Crash exposing (crash) type ExitCode @@ -27,7 +29,18 @@ exitWith exitCode = ExitFailure int -> int in - ( s, IO.ExitWith IO.pure code ) + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "exitWith" + , body = Http.stringBody "text/plain" (String.fromInt code) + , resolver = Http.stringResolver (\_ -> crash "exitWith") + , timeout = Nothing + } + ) + ) ) diff --git a/src/System/IO.elm b/src/System/IO.elm index 8a5bb7c4d..05a8ec1ed 100644 --- a/src/System/IO.elm +++ b/src/System/IO.elm @@ -1,4 +1,4 @@ -port module System.IO exposing +module System.IO exposing ( Program, Flags, Model, Msg, Next, run , IO(..), ION(..), RealWorld, pure, apply, fmap, bind, mapM , FilePath, Handle(..) @@ -12,6 +12,7 @@ port module System.IO exposing , putStr, putStrLn, getLine , ReplState(..), initialReplState , RealWorldMVar, MVarSubscriber(..) + , writeString ) {-| Ref.: @@ -82,9 +83,9 @@ port module System.IO exposing import Array exposing (Array) import Cmd.Extra as Cmd -import Codec.Archive.Zip as Zip import Dict exposing (Dict) import Http +import Json.Decode as Decode import Json.Encode as Encode import Task exposing (Task) import Utils.Crash exposing (crash) @@ -120,38 +121,7 @@ run app = , next = Dict.empty } , update = update - , subscriptions = - \_ -> - Sub.batch - [ recvWriteString WriteStringMsg - , recvRead (\{ index, value } -> ReadMsg index value) - , recvReadStdin (\{ index, value } -> ReadStdinMsg index value) - , recvHttpFetch (\{ index, value } -> HttpFetchMsg index value) - , recvGetArchive (\{ index, value } -> GetArchiveMsg index value) - , recvHttpUpload HttpUploadMsg - , recvHFlush HFlushMsg - , recvWithFile (\{ index, value } -> WithFileMsg index value) - , recvHFileSize (\{ index, value } -> HFileSizeMsg index value) - , recvProcWithCreateProcess (\{ index, value } -> ProcWithCreateProcessMsg index value) - , recvHClose HCloseMsg - , recvProcWaitForProcess (\{ index, value } -> ProcWaitForProcessMsg index value) - , recvDirFindExecutable (\{ index, value } -> DirFindExecutableMsg index value) - , recvReplGetInputLine (\{ index, value } -> ReplGetInputLineMsg index value) - , recvDirDoesFileExist (\{ index, value } -> DirDoesFileExistMsg index value) - , recvDirCreateDirectoryIfMissing DirCreateDirectoryIfMissingMsg - , recvLockFile LockFileMsg - , recvUnlockFile UnlockFileMsg - , recvDirGetModificationTime (\{ index, value } -> DirGetModificationTimeMsg index value) - , recvDirDoesDirectoryExist (\{ index, value } -> DirDoesDirectoryExistMsg index value) - , recvDirCanonicalizePath (\{ index, value } -> DirCanonicalizePathMsg index value) - , recvDirListDirectory (\{ index, value } -> DirListDirectoryMsg index value) - , recvBinaryDecodeFileOrFail (\{ index, value } -> BinaryDecodeFileOrFailMsg index value) - , recvWrite WriteMsg - , recvDirRemoveFile DirRemoveFileMsg - , recvDirRemoveDirectoryRecursive DirRemoveDirectoryRecursiveMsg - , recvDirWithCurrentDirectory DirWithCurrentDirectoryMsg - , recvReplGetInputLineWithInitial (\{ index, value } -> ReplGetInputLineWithInitialMsg index value) - ] + , subscriptions = \_ -> Sub.none } @@ -160,36 +130,7 @@ type alias Model = type Next - = WriteStringNext (() -> IO ()) - | ReadNext (String -> IO ()) - | ReadStdinNext (String -> IO ()) - | HttpFetchNext (String -> IO ()) - | GetArchiveNext (( String, Zip.Archive ) -> IO ()) - | HttpUploadNext (() -> IO ()) - | HFlushNext (() -> IO ()) - | WithFileNext (Int -> IO ()) - | HFileSizeNext (Int -> IO ()) - | ProcWithCreateProcessNext ({ stdinHandle : Maybe Int, ph : Int } -> IO ()) - | HCloseNext (() -> IO ()) - | ProcWaitForProcessNext (Int -> IO ()) - | ExitWithNext (() -> IO ()) - | DirFindExecutableNext (Maybe FilePath -> IO ()) - | ReplGetInputLineNext (Maybe String -> IO ()) - | DirDoesFileExistNext (Bool -> IO ()) - | DirCreateDirectoryIfMissingNext (() -> IO ()) - | LockFileNext (() -> IO ()) - | UnlockFileNext (() -> IO ()) - | DirGetModificationTimeNext (Int -> IO ()) - | DirDoesDirectoryExistNext (Bool -> IO ()) - | DirCanonicalizePathNext (String -> IO ()) - | DirListDirectoryNext (List String -> IO ()) - | BinaryDecodeFileOrFailNext (Encode.Value -> IO ()) - | WriteNext (() -> IO ()) - | DirRemoveFileNext (() -> IO ()) - | DirRemoveDirectoryRecursiveNext (() -> IO ()) - | DirWithCurrentDirectoryNext (() -> IO ()) - | ReplGetInputLineWithInitialNext (Maybe String -> IO ()) - | NewEmptyMVarNext (Int -> IO ()) + = NewEmptyMVarNext (Int -> IO ()) | ReadMVarNext (Encode.Value -> IO ()) | TakeMVarNext (Encode.Value -> IO ()) | PutMVarNext (() -> IO ()) @@ -197,34 +138,6 @@ type Next type Msg = PureMsg Int (IO ()) - | WriteStringMsg Int - | ReadMsg Int String - | ReadStdinMsg Int String - | HttpFetchMsg Int String - | GetArchiveMsg Int ( String, Zip.Archive ) - | HttpUploadMsg Int - | HFlushMsg Int - | WithFileMsg Int Int - | HFileSizeMsg Int Int - | ProcWithCreateProcessMsg Int { stdinHandle : Maybe Int, ph : Int } - | HCloseMsg Int - | ProcWaitForProcessMsg Int Int - | DirFindExecutableMsg Int (Maybe FilePath) - | ReplGetInputLineMsg Int (Maybe String) - | DirDoesFileExistMsg Int Bool - | DirCreateDirectoryIfMissingMsg Int - | LockFileMsg Int - | UnlockFileMsg Int - | DirGetModificationTimeMsg Int Int - | DirDoesDirectoryExistMsg Int Bool - | DirCanonicalizePathMsg Int FilePath - | DirListDirectoryMsg Int (List FilePath) - | BinaryDecodeFileOrFailMsg Int Encode.Value - | WriteMsg Int - | DirRemoveFileMsg Int - | DirRemoveDirectoryRecursiveMsg Int - | DirWithCurrentDirectoryMsg Int - | ReplGetInputLineWithInitialMsg Int (Maybe String) | NewEmptyMVarMsg Int Int | ReadMVarMsg Int Encode.Value | PutMVarMsg Int @@ -238,15 +151,16 @@ update msg model = ( newRealWorld, Pure () ) -> ( { newRealWorld | next = Dict.remove index newRealWorld.next } , if index == 0 then - sendExitWith 0 + Http.post + { url = "exitWith" + , body = Http.stringBody "text/plain" "0" + , expect = Http.expectString (\_ -> crash "exitWith") + } else Cmd.none ) - ( newRealWorld, ImpureCmd cmd ) -> - ( newRealWorld, Cmd.map (PureMsg index) cmd ) - ( newRealWorld, ImpureTask task ) -> ( newRealWorld, Task.perform (PureMsg index) task ) @@ -258,111 +172,6 @@ update msg model = ] ) - ( newRealWorld, WriteString next path content ) -> - ( { newRealWorld | next = Dict.insert index (WriteStringNext next) model.next }, sendWriteString { index = index, path = path, content = content } ) - - ( newRealWorld, Read next fd ) -> - ( { newRealWorld | next = Dict.insert index (ReadNext next) model.next }, sendRead { index = index, fd = fd } ) - - ( newRealWorld, ReadStdin next ) -> - ( { newRealWorld | next = Dict.insert index (ReadStdinNext next) model.next }, sendReadStdin { index = index } ) - - ( newRealWorld, HttpFetch next method urlStr headers ) -> - ( { newRealWorld | next = Dict.insert index (HttpFetchNext next) model.next }, sendHttpFetch { index = index, method = method, urlStr = urlStr, headers = headers } ) - - ( newRealWorld, GetArchive next method url ) -> - ( { newRealWorld | next = Dict.insert index (GetArchiveNext next) model.next }, sendGetArchive { index = index, method = method, url = url } ) - - ( newRealWorld, HttpUpload next urlStr headers parts ) -> - ( { newRealWorld | next = Dict.insert index (HttpUploadNext next) model.next }, sendHttpUpload { index = index, urlStr = urlStr, headers = headers, parts = parts } ) - - ( newRealWorld, HFlush next (Handle fd) ) -> - ( { newRealWorld | next = Dict.insert index (HFlushNext next) model.next }, sendHFlush { index = index, fd = fd } ) - - ( newRealWorld, WithFile next path mode ) -> - ( { newRealWorld | next = Dict.insert index (WithFileNext next) model.next } - , sendWithFile - { index = index - , path = path - , mode = - case mode of - ReadMode -> - "r" - - WriteMode -> - "w" - - AppendMode -> - "a" - - ReadWriteMode -> - "w+" - } - ) - - ( newRealWorld, HFileSize next (Handle fd) ) -> - ( { newRealWorld | next = Dict.insert index (HFileSizeNext next) model.next }, sendHFileSize { index = index, fd = fd } ) - - ( newRealWorld, ProcWithCreateProcess next createProcess ) -> - ( { newRealWorld | next = Dict.insert index (ProcWithCreateProcessNext next) model.next }, sendProcWithCreateProcess { index = index, createProcess = createProcess } ) - - ( newRealWorld, HClose next (Handle fd) ) -> - ( { newRealWorld | next = Dict.insert index (HCloseNext next) model.next }, sendHClose { index = index, fd = fd } ) - - ( newRealWorld, ProcWaitForProcess next ph ) -> - ( { newRealWorld | next = Dict.insert index (ProcWaitForProcessNext next) model.next }, sendProcWaitForProcess { index = index, ph = ph } ) - - ( newRealWorld, ExitWith next code ) -> - ( { newRealWorld | next = Dict.insert index (ExitWithNext next) model.next }, sendExitWith code ) - - ( newRealWorld, DirFindExecutable next name ) -> - ( { newRealWorld | next = Dict.insert index (DirFindExecutableNext next) model.next }, sendDirFindExecutable { index = index, name = name } ) - - ( newRealWorld, ReplGetInputLine next prompt ) -> - ( { newRealWorld | next = Dict.insert index (ReplGetInputLineNext next) model.next }, sendReplGetInputLine { index = index, prompt = prompt } ) - - ( newRealWorld, DirDoesFileExist next filename ) -> - ( { newRealWorld | next = Dict.insert index (DirDoesFileExistNext next) model.next }, sendDirDoesFileExist { index = index, filename = filename } ) - - ( newRealWorld, DirCreateDirectoryIfMissing next createParents filename ) -> - ( { newRealWorld | next = Dict.insert index (DirCreateDirectoryIfMissingNext next) model.next }, sendDirCreateDirectoryIfMissing { index = index, createParents = createParents, filename = filename } ) - - ( newRealWorld, LockFile next path ) -> - ( { newRealWorld | next = Dict.insert index (LockFileNext next) model.next }, sendLockFile { index = index, path = path } ) - - ( newRealWorld, UnlockFile next path ) -> - ( { newRealWorld | next = Dict.insert index (UnlockFileNext next) model.next }, sendUnlockFile { index = index, path = path } ) - - ( newRealWorld, DirGetModificationTime next filename ) -> - ( { newRealWorld | next = Dict.insert index (DirGetModificationTimeNext next) model.next }, sendDirGetModificationTime { index = index, filename = filename } ) - - ( newRealWorld, DirDoesDirectoryExist next path ) -> - ( { newRealWorld | next = Dict.insert index (DirDoesDirectoryExistNext next) model.next }, sendDirDoesDirectoryExist { index = index, path = path } ) - - ( newRealWorld, DirCanonicalizePath next path ) -> - ( { newRealWorld | next = Dict.insert index (DirCanonicalizePathNext next) model.next }, sendDirCanonicalizePath { index = index, path = path } ) - - ( newRealWorld, DirListDirectory next path ) -> - ( { newRealWorld | next = Dict.insert index (DirListDirectoryNext next) model.next }, sendDirListDirectory { index = index, path = path } ) - - ( newRealWorld, BinaryDecodeFileOrFail next filename ) -> - ( { newRealWorld | next = Dict.insert index (BinaryDecodeFileOrFailNext next) model.next }, sendBinaryDecodeFileOrFail { index = index, filename = filename } ) - - ( newRealWorld, Write next fd content ) -> - ( { newRealWorld | next = Dict.insert index (WriteNext next) model.next }, sendWrite { index = index, fd = fd, content = content } ) - - ( newRealWorld, DirRemoveFile next path ) -> - ( { newRealWorld | next = Dict.insert index (DirRemoveFileNext next) model.next }, sendDirRemoveFile { index = index, path = path } ) - - ( newRealWorld, DirRemoveDirectoryRecursive next path ) -> - ( { newRealWorld | next = Dict.insert index (DirRemoveDirectoryRecursiveNext next) model.next }, sendDirRemoveDirectoryRecursive { index = index, path = path } ) - - ( newRealWorld, DirWithCurrentDirectory next path ) -> - ( { newRealWorld | next = Dict.insert index (DirWithCurrentDirectoryNext next) model.next }, sendDirWithCurrentDirectory { index = index, path = path } ) - - ( newRealWorld, ReplGetInputLineWithInitial next prompt left right ) -> - ( { newRealWorld | next = Dict.insert index (ReplGetInputLineWithInitialNext next) model.next }, sendReplGetInputLineWithInitial { index = index, prompt = prompt, left = left, right = right } ) - -- MVars ( newRealWorld, NewEmptyMVar next value ) -> update (NewEmptyMVarMsg index value) { newRealWorld | next = Dict.insert index (NewEmptyMVarNext next) model.next } @@ -393,230 +202,6 @@ update msg model = ( newRealWorld, PutMVar next _ Nothing ) -> update (PutMVarMsg index) { newRealWorld | next = Dict.insert index (PutMVarNext next) model.next } - WriteStringMsg index -> - case Dict.get index model.next of - Just (WriteStringNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "WriteStringMsg" - - ReadMsg index value -> - case Dict.get index model.next of - Just (ReadNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "ReadMsg" - - ReadStdinMsg index value -> - case Dict.get index model.next of - Just (ReadStdinNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "ReadStdinMsg" - - HttpFetchMsg index value -> - case Dict.get index model.next of - Just (HttpFetchNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "HttpFetchMsg" - - GetArchiveMsg index value -> - case Dict.get index model.next of - Just (GetArchiveNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "GetArchiveMsg" - - HttpUploadMsg index -> - case Dict.get index model.next of - Just (HttpUploadNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "HttpUploadMsg" - - HFlushMsg index -> - case Dict.get index model.next of - Just (HFlushNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "HFlushMsg" - - WithFileMsg index fd -> - case Dict.get index model.next of - Just (WithFileNext fn) -> - update (PureMsg index (fn fd)) model - - _ -> - crash "WithFileMsg" - - HFileSizeMsg index size -> - case Dict.get index model.next of - Just (HFileSizeNext fn) -> - update (PureMsg index (fn size)) model - - _ -> - crash "HFileSizeMsg" - - ProcWithCreateProcessMsg index value -> - case Dict.get index model.next of - Just (ProcWithCreateProcessNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "ProcWithCreateProcessMsg" - - HCloseMsg index -> - case Dict.get index model.next of - Just (HCloseNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "HCloseMsg" - - ProcWaitForProcessMsg index code -> - case Dict.get index model.next of - Just (ProcWaitForProcessNext fn) -> - update (PureMsg index (fn code)) model - - _ -> - crash "ProcWaitForProcessMsg" - - DirFindExecutableMsg index value -> - case Dict.get index model.next of - Just (DirFindExecutableNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "DirFindExecutableMsg" - - ReplGetInputLineMsg index value -> - case Dict.get index model.next of - Just (ReplGetInputLineNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "ReplGetInputLineMsg" - - DirDoesFileExistMsg index value -> - case Dict.get index model.next of - Just (DirDoesFileExistNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "DirDoesFileExistMsg" - - DirCreateDirectoryIfMissingMsg index -> - case Dict.get index model.next of - Just (DirCreateDirectoryIfMissingNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "DirCreateDirectoryIfMissingMsg" - - LockFileMsg index -> - case Dict.get index model.next of - Just (LockFileNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "LockFileMsg" - - UnlockFileMsg index -> - case Dict.get index model.next of - Just (UnlockFileNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "UnlockFileMsg" - - DirGetModificationTimeMsg index value -> - case Dict.get index model.next of - Just (DirGetModificationTimeNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "DirGetModificationTimeMsg" - - DirDoesDirectoryExistMsg index value -> - case Dict.get index model.next of - Just (DirDoesDirectoryExistNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "DirDoesDirectoryExistMsg" - - DirCanonicalizePathMsg index value -> - case Dict.get index model.next of - Just (DirCanonicalizePathNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "DirCanonicalizePathMsg" - - DirListDirectoryMsg index value -> - case Dict.get index model.next of - Just (DirListDirectoryNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "DirListDirectoryMsg" - - BinaryDecodeFileOrFailMsg index value -> - case Dict.get index model.next of - Just (BinaryDecodeFileOrFailNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "BinaryDecodeFileOrFailMsg" - - WriteMsg index -> - case Dict.get index model.next of - Just (WriteNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "WriteMsg" - - DirRemoveFileMsg index -> - case Dict.get index model.next of - Just (DirRemoveFileNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "DirRemoveFileMsg" - - DirRemoveDirectoryRecursiveMsg index -> - case Dict.get index model.next of - Just (DirRemoveDirectoryRecursiveNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "DirRemoveDirectoryRecursiveMsg" - - DirWithCurrentDirectoryMsg index -> - case Dict.get index model.next of - Just (DirWithCurrentDirectoryNext fn) -> - update (PureMsg index (fn ())) model - - _ -> - crash "DirWithCurrentDirectoryMsg" - - ReplGetInputLineWithInitialMsg index value -> - case Dict.get index model.next of - Just (ReplGetInputLineWithInitialNext fn) -> - update (PureMsg index (fn value)) model - - _ -> - crash "ReplGetInputLineWithInitialMsg" - NewEmptyMVarMsg index value -> case Dict.get index model.next of Just (NewEmptyMVarNext fn) -> @@ -656,175 +241,33 @@ updatePutIndex maybePutIndex ( model, cmd ) = ( model, cmd ) -port sendWriteString : { index : Int, path : FilePath, content : String } -> Cmd msg - - -port recvWriteString : (Int -> msg) -> Sub msg - - -port sendRead : { index : Int, fd : String } -> Cmd msg - - -port recvRead : ({ index : Int, value : String } -> msg) -> Sub msg - - -port sendReadStdin : { index : Int } -> Cmd msg - - -port recvReadStdin : ({ index : Int, value : String } -> msg) -> Sub msg - - -port sendHttpFetch : { index : Int, method : String, urlStr : String, headers : List ( String, String ) } -> Cmd msg - - -port recvHttpFetch : ({ index : Int, value : String } -> msg) -> Sub msg - - -port sendGetArchive : { index : Int, method : String, url : String } -> Cmd msg - - -port recvGetArchive : ({ index : Int, value : ( String, Zip.Archive ) } -> msg) -> Sub msg - - -port sendHttpUpload : { index : Int, urlStr : String, headers : List ( String, String ), parts : List Encode.Value } -> Cmd msg - - -port recvHttpUpload : (Int -> msg) -> Sub msg - - -port sendHFlush : { index : Int, fd : Int } -> Cmd msg - - -port recvHFlush : (Int -> msg) -> Sub msg - -port sendWithFile : { index : Int, path : String, mode : String } -> Cmd msg +-- Interal helpers -port recvWithFile : ({ index : Int, value : Int } -> msg) -> Sub msg - - -port sendHFileSize : { index : Int, fd : Int } -> Cmd msg - - -port recvHFileSize : ({ index : Int, value : Int } -> msg) -> Sub msg - - -port sendProcWithCreateProcess : { index : Int, createProcess : Encode.Value } -> Cmd msg - - -port recvProcWithCreateProcess : ({ index : Int, value : { stdinHandle : Maybe Int, ph : Int } } -> msg) -> Sub msg - - -port sendHClose : { index : Int, fd : Int } -> Cmd msg - - -port recvHClose : (Int -> msg) -> Sub msg - - -port sendProcWaitForProcess : { index : Int, ph : Int } -> Cmd msg - - -port recvProcWaitForProcess : ({ index : Int, value : Int } -> msg) -> Sub msg - - -port sendExitWith : Int -> Cmd msg - - -port sendDirFindExecutable : { index : Int, name : FilePath } -> Cmd msg - - -port recvDirFindExecutable : ({ index : Int, value : Maybe FilePath } -> msg) -> Sub msg - - -port sendReplGetInputLine : { index : Int, prompt : String } -> Cmd msg - - -port recvReplGetInputLine : ({ index : Int, value : Maybe String } -> msg) -> Sub msg - - -port sendDirDoesFileExist : { index : Int, filename : String } -> Cmd msg - - -port recvDirDoesFileExist : ({ index : Int, value : Bool } -> msg) -> Sub msg - - -port sendDirCreateDirectoryIfMissing : { index : Int, createParents : Bool, filename : String } -> Cmd msg - - -port recvDirCreateDirectoryIfMissing : (Int -> msg) -> Sub msg - - -port sendLockFile : { index : Int, path : String } -> Cmd msg - - -port recvLockFile : (Int -> msg) -> Sub msg - - -port sendUnlockFile : { index : Int, path : String } -> Cmd msg - - -port recvUnlockFile : (Int -> msg) -> Sub msg - - -port sendDirGetModificationTime : { index : Int, filename : String } -> Cmd msg - - -port recvDirGetModificationTime : ({ index : Int, value : Int } -> msg) -> Sub msg - - -port sendDirDoesDirectoryExist : { index : Int, path : FilePath } -> Cmd msg - - -port recvDirDoesDirectoryExist : ({ index : Int, value : Bool } -> msg) -> Sub msg - - -port sendDirCanonicalizePath : { index : Int, path : FilePath } -> Cmd msg - - -port recvDirCanonicalizePath : ({ index : Int, value : FilePath } -> msg) -> Sub msg - - -port sendDirListDirectory : { index : Int, path : FilePath } -> Cmd msg - - -port recvDirListDirectory : ({ index : Int, value : List FilePath } -> msg) -> Sub msg - - -port sendBinaryDecodeFileOrFail : { index : Int, filename : FilePath } -> Cmd msg - - -port recvBinaryDecodeFileOrFail : ({ index : Int, value : Encode.Value } -> msg) -> Sub msg - - -port sendWrite : { index : Int, fd : FilePath, content : Encode.Value } -> Cmd msg - - -port recvWrite : (Int -> msg) -> Sub msg - - -port sendDirRemoveFile : { index : Int, path : FilePath } -> Cmd msg - - -port recvDirRemoveFile : (Int -> msg) -> Sub msg - - -port sendDirRemoveDirectoryRecursive : { index : Int, path : FilePath } -> Cmd msg - - -port recvDirRemoveDirectoryRecursive : (Int -> msg) -> Sub msg - - -port sendDirWithCurrentDirectory : { index : Int, path : FilePath } -> Cmd msg - - -port recvDirWithCurrentDirectory : (Int -> msg) -> Sub msg - - -port sendReplGetInputLineWithInitial : { index : Int, prompt : String, left : String, right : String } -> Cmd msg - - -port recvReplGetInputLineWithInitial : ({ index : Int, value : Maybe String } -> msg) -> Sub msg +writeString : FilePath -> String -> IO () +writeString path content = + IO + (\_ s -> + ( s + , ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "writeString" + , body = + Http.jsonBody + (Encode.object + [ ( "path", Encode.string path ) + , ( "content", Encode.string content ) + ] + ) + , resolver = Http.stringResolver (\_ -> Ok (pure ())) + , timeout = Nothing + } + ) + ) + ) @@ -837,38 +280,8 @@ type IO a type ION a = Pure a - | ImpureCmd (Cmd (IO a)) | ImpureTask (Task Never (IO a)) | ForkIO (() -> IO a) (IO ()) - | WriteString (() -> IO a) FilePath String - | Read (String -> IO a) FilePath - | ReadStdin (String -> IO a) - | HttpFetch (String -> IO a) String String (List ( String, String )) - | GetArchive (( String, Zip.Archive ) -> IO a) String String - | HttpUpload (() -> IO a) String (List ( String, String )) (List Encode.Value) - | HFlush (() -> IO a) Handle - | WithFile (Int -> IO a) String IOMode - | HFileSize (Int -> IO a) Handle - | ProcWithCreateProcess ({ stdinHandle : Maybe Int, ph : Int } -> IO a) Encode.Value - | HClose (() -> IO a) Handle - | ProcWaitForProcess (Int -> IO a) Int - | ExitWith (a -> IO a) Int - | DirFindExecutable (Maybe FilePath -> IO a) FilePath - | ReplGetInputLine (Maybe String -> IO a) String - | DirDoesFileExist (Bool -> IO a) FilePath - | DirCreateDirectoryIfMissing (() -> IO a) Bool FilePath - | LockFile (() -> IO a) FilePath - | UnlockFile (() -> IO a) FilePath - | DirGetModificationTime (Int -> IO a) FilePath - | DirDoesDirectoryExist (Bool -> IO a) FilePath - | DirCanonicalizePath (String -> IO a) FilePath - | DirListDirectory (List String -> IO a) FilePath - | BinaryDecodeFileOrFail (Encode.Value -> IO a) FilePath - | Write (() -> IO a) FilePath Encode.Value - | DirRemoveFile (() -> IO a) FilePath - | DirRemoveDirectoryRecursive (() -> IO a) FilePath - | DirWithCurrentDirectory (() -> IO a) FilePath - | ReplGetInputLineWithInitial (Maybe String -> IO a) String String String -- MVars | NewEmptyMVar (Int -> IO a) Int | ReadMVar (Encode.Value -> IO a) (Maybe Encode.Value) @@ -924,102 +337,12 @@ bind f (IO ma) = ( s1, Pure a ) -> unIO (f a) index s1 - ( s1, ImpureCmd cmd ) -> - ( s1, ImpureCmd (Cmd.map (bind f) cmd) ) - ( s1, ImpureTask task ) -> ( s1, ImpureTask (Task.map (bind f) task) ) ( s1, ForkIO next forkIO ) -> ( s1, ForkIO (\() -> bind f (next ())) forkIO ) - ( s1, WriteString next path content ) -> - ( s1, WriteString (\() -> bind f (next ())) path content ) - - ( s1, Read next fd ) -> - ( s1, Read (\input -> bind f (next input)) fd ) - - ( s1, ReadStdin next ) -> - ( s1, ReadStdin (\input -> bind f (next input)) ) - - ( s1, HttpFetch next method urlStr headers ) -> - ( s1, HttpFetch (\body -> bind f (next body)) method urlStr headers ) - - ( s1, GetArchive next method url ) -> - ( s1, GetArchive (\body -> bind f (next body)) method url ) - - ( s1, HttpUpload next urlStr headers parts ) -> - ( s1, HttpUpload (\() -> bind f (next ())) urlStr headers parts ) - - ( s1, HFlush next handle ) -> - ( s1, HFlush (\() -> bind f (next ())) handle ) - - ( s1, WithFile next path mode ) -> - ( s1, WithFile (\fd -> bind f (next fd)) path mode ) - - ( s1, HFileSize next handle ) -> - ( s1, HFileSize (\size -> bind f (next size)) handle ) - - ( s1, ProcWithCreateProcess next createProcess ) -> - ( s1, ProcWithCreateProcess (\data -> bind f (next data)) createProcess ) - - ( s1, HClose next handle ) -> - ( s1, HClose (\() -> bind f (next ())) handle ) - - ( s1, ProcWaitForProcess next ph ) -> - ( s1, ProcWaitForProcess (\code -> bind f (next code)) ph ) - - ( s1, ExitWith _ code ) -> - ( s1, ExitWith (\_ -> crash "exitWith") code ) - - ( s1, DirFindExecutable next name ) -> - ( s1, DirFindExecutable (\value -> bind f (next value)) name ) - - ( s1, ReplGetInputLine next prompt ) -> - ( s1, ReplGetInputLine (\value -> bind f (next value)) prompt ) - - ( s1, DirDoesFileExist next filename ) -> - ( s1, DirDoesFileExist (\exists -> bind f (next exists)) filename ) - - ( s1, DirCreateDirectoryIfMissing next createParents filename ) -> - ( s1, DirCreateDirectoryIfMissing (\exists -> bind f (next exists)) createParents filename ) - - ( s1, LockFile next path ) -> - ( s1, LockFile (\() -> bind f (next ())) path ) - - ( s1, UnlockFile next path ) -> - ( s1, UnlockFile (\() -> bind f (next ())) path ) - - ( s1, DirGetModificationTime next path ) -> - ( s1, DirGetModificationTime (\value -> bind f (next value)) path ) - - ( s1, DirDoesDirectoryExist next path ) -> - ( s1, DirDoesDirectoryExist (\value -> bind f (next value)) path ) - - ( s1, DirCanonicalizePath next path ) -> - ( s1, DirCanonicalizePath (\value -> bind f (next value)) path ) - - ( s1, DirListDirectory next path ) -> - ( s1, DirListDirectory (\value -> bind f (next value)) path ) - - ( s1, BinaryDecodeFileOrFail next filename ) -> - ( s1, BinaryDecodeFileOrFail (\value -> bind f (next value)) filename ) - - ( s1, Write next fd content ) -> - ( s1, Write (\() -> bind f (next ())) fd content ) - - ( s1, DirRemoveFile next path ) -> - ( s1, DirRemoveFile (\() -> bind f (next ())) path ) - - ( s1, DirRemoveDirectoryRecursive next path ) -> - ( s1, DirRemoveDirectoryRecursive (\() -> bind f (next ())) path ) - - ( s1, DirWithCurrentDirectory next path ) -> - ( s1, DirWithCurrentDirectory (\() -> bind f (next ())) path ) - - ( s1, ReplGetInputLineWithInitial next prompt left right ) -> - ( s1, ReplGetInputLineWithInitial (\value -> bind f (next value)) prompt left right ) - ( s1, NewEmptyMVar next newValue ) -> ( s1, NewEmptyMVar (\value -> bind f (next value)) newValue ) @@ -1077,8 +400,56 @@ stderr = withFile : String -> IOMode -> (Handle -> IO a) -> IO a withFile path mode callback = - IO (\_ s -> ( s, WithFile pure path mode )) - |> bind (Handle >> callback) + IO + (\_ s -> + ( s + , ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "withFile" + , body = + Http.jsonBody + (Encode.object + [ ( "path", Encode.string path ) + , ( "mode" + , Encode.string + (case mode of + ReadMode -> + "r" + + WriteMode -> + "w" + + AppendMode -> + "a" + + ReadWriteMode -> + "w+" + ) + ) + ] + ) + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString Decode.int body of + Ok fd -> + Ok (callback (Handle fd)) + + Err _ -> + crash "withFile" + + _ -> + crash "withFile" + ) + , timeout = Nothing + } + ) + ) + ) type IOMode @@ -1093,8 +464,31 @@ type IOMode hClose : Handle -> IO () -hClose handle = - IO (\_ s -> ( s, HClose pure handle )) +hClose (Handle handle) = + IO + (\_ s -> + ( s + , ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "hFileSize" + , body = Http.stringBody "text/plain" (String.fromInt handle) + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (pure ()) + + _ -> + crash "hClose" + ) + , timeout = Nothing + } + ) + ) + ) @@ -1102,8 +496,36 @@ hClose handle = hFileSize : Handle -> IO Int -hFileSize handle = - IO (\_ s -> ( s, HFileSize pure handle )) +hFileSize (Handle handle) = + IO + (\_ s -> + ( s + , ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "hFileSize" + , body = Http.stringBody "text/plain" (String.fromInt handle) + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString Decode.int body of + Ok fd -> + Ok (pure fd) + + Err _ -> + crash "hFileSize" + + _ -> + crash "hFileSize" + ) + , timeout = Nothing + } + ) + ) + ) @@ -1111,8 +533,8 @@ hFileSize handle = hFlush : Handle -> IO () -hFlush handle = - IO (\_ s -> ( s, HFlush pure handle )) +hFlush _ = + pure () diff --git a/src/System/Process.elm b/src/System/Process.elm index 330373b0b..a886ffc9a 100644 --- a/src/System/Process.elm +++ b/src/System/Process.elm @@ -8,9 +8,12 @@ module System.Process exposing , withCreateProcess ) +import Http +import Json.Decode as Decode import Json.Encode as Encode import System.Exit as Exit import System.IO as IO exposing (IO(..)) +import Utils.Crash exposing (crash) type CmdSpec @@ -50,78 +53,121 @@ withCreateProcess createProcess f = IO (\_ s -> ( s - , IO.ProcWithCreateProcess IO.pure - (Encode.object - [ ( "cmdspec" - , case createProcess.cmdspec of - RawCommand cmd args -> - Encode.object - [ ( "type", Encode.string "RawCommand" ) - , ( "cmd", Encode.string cmd ) - , ( "args", Encode.list Encode.string args ) - ] - ) - , ( "stdin" - , case createProcess.std_in of - Inherit -> - Encode.string "inherit" - - UseHandle (IO.Handle handle) -> - Encode.int handle - - CreatePipe -> - Encode.string "pipe" - - NoStream -> - Encode.string "ignore" - ) - , ( "stdout" - , case createProcess.std_out of - Inherit -> - Encode.string "inherit" - - UseHandle (IO.Handle handle) -> - Encode.int handle - - CreatePipe -> - Encode.string "pipe" - - NoStream -> - Encode.string "ignore" - ) - , ( "stderr" - , case createProcess.std_err of - Inherit -> - Encode.string "inherit" - - UseHandle (IO.Handle handle) -> - Encode.int handle - - CreatePipe -> - Encode.string "pipe" - - NoStream -> - Encode.string "ignore" - ) - ] + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "withCreateProcess" + , body = + Http.jsonBody + (Encode.object + [ ( "cmdspec" + , case createProcess.cmdspec of + RawCommand cmd args -> + Encode.object + [ ( "type", Encode.string "RawCommand" ) + , ( "cmd", Encode.string cmd ) + , ( "args", Encode.list Encode.string args ) + ] + ) + , ( "stdin" + , case createProcess.std_in of + Inherit -> + Encode.string "inherit" + + UseHandle (IO.Handle handle) -> + Encode.int handle + + CreatePipe -> + Encode.string "pipe" + + NoStream -> + Encode.string "ignore" + ) + , ( "stdout" + , case createProcess.std_out of + Inherit -> + Encode.string "inherit" + + UseHandle (IO.Handle handle) -> + Encode.int handle + + CreatePipe -> + Encode.string "pipe" + + NoStream -> + Encode.string "ignore" + ) + , ( "stderr" + , case createProcess.std_err of + Inherit -> + Encode.string "inherit" + + UseHandle (IO.Handle handle) -> + Encode.int handle + + CreatePipe -> + Encode.string "pipe" + + NoStream -> + Encode.string "ignore" + ) + ] + ) + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString (Decode.map2 Tuple.pair (Decode.field "stdinHandle" (Decode.maybe Decode.int)) (Decode.field "ph" Decode.int)) body of + Ok ( stdinHandle, ph ) -> + Ok (f (Maybe.map IO.Handle stdinHandle) Nothing Nothing (ProcessHandle ph)) + + Err _ -> + crash "withFile" + + _ -> + crash "withFile" + ) + , timeout = Nothing + } ) ) ) - |> IO.bind - (\{ stdinHandle, ph } -> - f (Maybe.map IO.Handle stdinHandle) Nothing Nothing (ProcessHandle ph) - ) waitForProcess : ProcessHandle -> IO Exit.ExitCode waitForProcess (ProcessHandle ph) = - IO (\_ s -> ( s, IO.ProcWaitForProcess IO.pure ph )) - |> IO.fmap - (\exitCode -> - case exitCode of - 0 -> - Exit.ExitSuccess - - int -> - Exit.ExitFailure int + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "waitForProcess" + , body = + Http.stringBody "text/plain" (String.fromInt ph) + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString Decode.int body of + Ok 0 -> + Ok (IO.pure Exit.ExitSuccess) + + Ok int -> + Ok (IO.pure (Exit.ExitFailure int)) + + Err _ -> + crash "waitForProcess" + + _ -> + crash "waitForProcess" + ) + , timeout = Nothing + } + ) ) + ) diff --git a/src/Utils/Main.elm b/src/Utils/Main.elm index a90cbd4b8..1f87c68b1 100644 --- a/src/Utils/Main.elm +++ b/src/Utils/Main.elm @@ -139,6 +139,7 @@ import Control.Monad.State.Strict as State import Data.Map as Map exposing (Dict) import Data.Set as EverySet exposing (EverySet) import Dict +import Http import Json.Decode as Decode import Json.Encode as Encode import Maybe.Extra as Maybe @@ -748,12 +749,58 @@ lockWithFileLock path mode ioFunc = lockFile : FilePath -> IO () lockFile path = - IO (\_ s -> ( s, IO.LockFile IO.pure path )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "lockFile" + , body = Http.stringBody "text/plain" path + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (IO.pure ()) + + _ -> + crash "lockFile" + ) + , timeout = Nothing + } + ) + ) + ) unlockFile : FilePath -> IO () unlockFile path = - IO (\_ s -> ( s, IO.UnlockFile IO.pure path )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "unlockFile" + , body = Http.stringBody "text/plain" path + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (IO.pure ()) + + _ -> + crash "lockFile" + ) + , timeout = Nothing + } + ) + ) + ) @@ -762,17 +809,102 @@ unlockFile path = dirDoesFileExist : FilePath -> IO Bool dirDoesFileExist filename = - IO (\_ s -> ( s, IO.DirDoesFileExist IO.pure filename )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirDoesFileExist" + , body = Http.stringBody "text/plain" filename + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString Decode.bool body of + Ok value -> + Ok (IO.pure value) + + Err _ -> + crash "dirDoesFileExist" + + _ -> + crash "dirDoesFileExist" + ) + , timeout = Nothing + } + ) + ) + ) dirFindExecutable : FilePath -> IO (Maybe FilePath) dirFindExecutable filename = - IO (\_ s -> ( s, IO.DirFindExecutable IO.pure filename )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirFindExecutable" + , body = Http.stringBody "text/plain" filename + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString (Decode.maybe Decode.string) body of + Ok name -> + Ok (IO.pure name) + + Err _ -> + crash "dirFindExecutable" + + _ -> + crash "dirFindExecutable" + ) + , timeout = Nothing + } + ) + ) + ) dirCreateDirectoryIfMissing : Bool -> FilePath -> IO () dirCreateDirectoryIfMissing createParents filename = - IO (\_ s -> ( s, IO.DirCreateDirectoryIfMissing IO.pure createParents filename )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirCreateDirectoryIfMissing" + , body = + Http.jsonBody + (Encode.object + [ ( "createParents", Encode.bool createParents ) + , ( "filename", Encode.string filename ) + ] + ) + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (IO.pure ()) + + _ -> + crash "dirCreateDirectoryIfMissing" + ) + , timeout = Nothing + } + ) + ) + ) dirGetCurrentDirectory : IO String @@ -787,28 +919,152 @@ dirGetAppUserDataDirectory filename = dirGetModificationTime : FilePath -> IO Time.Posix dirGetModificationTime filename = - IO (\_ s -> ( s, IO.DirGetModificationTime IO.pure filename )) - |> IO.fmap Time.millisToPosix + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirGetModificationTime" + , body = Http.stringBody "text/plain" filename + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString Decode.int body of + Ok value -> + Ok (IO.pure (Time.millisToPosix value)) + + Err _ -> + crash "dirGetModificationTime" + + _ -> + crash "dirGetModificationTime" + ) + , timeout = Nothing + } + ) + ) + ) dirRemoveFile : FilePath -> IO () dirRemoveFile path = - IO (\_ s -> ( s, IO.DirRemoveFile IO.pure path )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirRemoveFile" + , body = Http.stringBody "text/plain" path + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (IO.pure ()) + + _ -> + crash "dirRemoveFile" + ) + , timeout = Nothing + } + ) + ) + ) dirRemoveDirectoryRecursive : FilePath -> IO () dirRemoveDirectoryRecursive path = - IO (\_ s -> ( s, IO.DirRemoveDirectoryRecursive IO.pure path )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirRemoveDirectoryRecursive" + , body = Http.stringBody "text/plain" path + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (IO.pure ()) + + _ -> + crash "dirRemoveDirectoryRecursive" + ) + , timeout = Nothing + } + ) + ) + ) dirDoesDirectoryExist : FilePath -> IO Bool dirDoesDirectoryExist path = - IO (\_ s -> ( s, IO.DirDoesDirectoryExist IO.pure path )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirDoesDirectoryExist" + , body = Http.stringBody "text/plain" path + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString Decode.bool body of + Ok value -> + Ok (IO.pure value) + + Err _ -> + crash "dirDoesDirectoryExist" + + _ -> + crash "dirDoesDirectoryExist" + ) + , timeout = Nothing + } + ) + ) + ) dirCanonicalizePath : FilePath -> IO FilePath dirCanonicalizePath path = - IO (\_ s -> ( s, IO.DirCanonicalizePath IO.pure path )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirCanonicalizePath" + , body = Http.stringBody "text/plain" path + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + Ok (IO.pure body) + + _ -> + crash "dirCanonicalizePath" + ) + , timeout = Nothing + } + ) + ) + ) dirWithCurrentDirectory : FilePath -> IO a -> IO a @@ -817,15 +1073,91 @@ dirWithCurrentDirectory dir action = |> IO.bind (\currentDir -> bracket_ - (IO (\_ s -> ( s, IO.DirWithCurrentDirectory IO.pure dir ))) - (IO (\_ s -> ( s, IO.DirWithCurrentDirectory IO.pure currentDir ))) + (IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirWithCurrentDirectory" + , body = Http.stringBody "text/plain" dir + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (IO.pure ()) + + _ -> + crash "dirWithCurrentDirectory" + ) + , timeout = Nothing + } + ) + ) + ) + ) + (IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirWithCurrentDirectory" + , body = Http.stringBody "text/plain" currentDir + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (IO.pure ()) + + _ -> + crash "dirWithCurrentDirectory" + ) + , timeout = Nothing + } + ) + ) + ) + ) action ) dirListDirectory : FilePath -> IO (List FilePath) dirListDirectory path = - IO (\_ s -> ( s, IO.DirListDirectory IO.pure path )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "dirListDirectory" + , body = Http.stringBody "text/plain" path + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString (Decode.list Decode.string) body of + Ok value -> + Ok (IO.pure value) + + Err _ -> + crash "dirListDirectory" + + _ -> + crash "dirListDirectory" + ) + , timeout = Nothing + } + ) + ) + ) @@ -1177,16 +1509,69 @@ builderHPutBuilder = binaryDecodeFileOrFail : Decode.Decoder a -> FilePath -> IO (Result ( Int, String ) a) binaryDecodeFileOrFail decoder filename = - IO (\_ s -> ( s, IO.BinaryDecodeFileOrFail IO.pure filename )) - |> IO.fmap - (Decode.decodeValue decoder - >> Result.mapError (\_ -> ( 0, "Could not find file " ++ filename )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "binaryDecodeFileOrFail" + , body = Http.stringBody "text/plain" filename + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString decoder body of + Ok value -> + Ok (IO.pure (Ok value)) + + Err _ -> + Ok (IO.pure (Err ( 0, "Could not find file " ++ filename ))) + + _ -> + crash "binaryDecodeFileOrFail" + ) + , timeout = Nothing + } + ) ) + ) binaryEncodeFile : (a -> Encode.Value) -> FilePath -> a -> IO () binaryEncodeFile encoder path value = - IO (\_ s -> ( s, IO.Write IO.pure path (encoder value) )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "binaryEncodeFile" + , body = + Http.jsonBody + (Encode.object + [ ( "fd", Encode.string path ) + , ( "content", encoder value ) + ] + ) + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ _ -> + Ok (IO.pure ()) + + _ -> + crash "binaryEncodeFile" + ) + , timeout = Nothing + } + ) + ) + ) @@ -1231,12 +1616,40 @@ replCompleteWord _ _ _ = replGetInputLine : String -> ReplInputT (Maybe String) replGetInputLine prompt = - IO (\_ s -> ( s, IO.ReplGetInputLine IO.pure prompt )) + IO + (\_ s -> + ( s + , IO.ImpureTask + (Http.task + { method = "POST" + , headers = [] + , url = "replGetInputLine" + , body = Http.stringBody "text/plain" prompt + , resolver = + Http.stringResolver + (\response -> + case response of + Http.GoodStatus_ _ body -> + case Decode.decodeString (Decode.maybe Decode.string) body of + Ok value -> + Ok (IO.pure value) + + Err _ -> + crash "replGetInputLine" + + _ -> + crash "replGetInputLine" + ) + , timeout = Nothing + } + ) + ) + ) replGetInputLineWithInitial : String -> ( String, String ) -> ReplInputT (Maybe String) replGetInputLineWithInitial prompt ( left, right ) = - IO (\_ s -> ( s, IO.ReplGetInputLineWithInitial IO.pure prompt left right )) + replGetInputLine (left ++ prompt ++ right)