-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
331 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,4 @@ node_modules | |
.tests | ||
.vscode | ||
|
||
.repl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
this is a fixture! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import Dict from "Dictionary" | ||
import { fromMaybe } from "Maybe" | ||
import Process from "Process" | ||
import String from "String" | ||
|
||
|
||
|
||
normalize :: String -> String | ||
export normalize = (input) => pipe( | ||
Dict.get("HOME"), | ||
map(String.replace("~", $, input)), | ||
fromMaybe(input), | ||
)(Process.Env) | ||
|
||
// TODO: check behavior for non-OSX files | ||
tempdir :: String -> String | ||
export tempdir = (file) => pipe( | ||
Dict.get("TMPDIR"), | ||
fromMaybe("/private/var/tmp/"), | ||
mappend(file), | ||
)(Process.Env) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,185 @@ | ||
import type { Error } from "IO" | ||
|
||
import File from "File" | ||
import { always } from "Function" | ||
import { always, complement, ifElse, when } from "Function" | ||
import {} from "Monad" | ||
import String from "String" | ||
import Wish from "Wish" | ||
|
||
import FS from "@/FS" | ||
|
||
|
||
// Preserve(dirty, file) | ||
export type Preserve = Preserve(Boolean, String) | ||
export alias Memory a = { get :: {} -> a, isDirty :: {} -> Boolean, set :: a -> a } | ||
|
||
// accessors | ||
// Preserve | ||
// - clean - has the value been written to source? | ||
// - sourceFile - where does the value get saved? | ||
// - value - the preserved value | ||
export type Preserve = Preserve(Boolean, String, String) | ||
export alias Memory = Wish Error Preserve | ||
|
||
isDirty :: Preserve -> Boolean | ||
export isDirty = where { | ||
Preserve(d, _) => | ||
|
||
//## ACCESSORS | ||
|
||
/** | ||
* Has the value been saved? | ||
* @since 0.0.1 | ||
*/ | ||
isClean :: Preserve -> Boolean | ||
export isClean = where { | ||
Preserve(d, _, _) => | ||
d | ||
} | ||
|
||
isDirty :: Preserve -> Boolean | ||
export isDirty = complement(isClean) | ||
|
||
/** | ||
* Where is the value saved? | ||
* @since 0.0.1 | ||
*/ | ||
source :: Preserve -> String | ||
export source = where { | ||
Preserve(_, f) => | ||
Preserve(_, f, _) => | ||
f | ||
} | ||
|
||
// constructors | ||
/** | ||
* What is the saved value? | ||
* @since 0.0.1 | ||
*/ | ||
value :: Preserve -> String | ||
export value = where { | ||
Preserve(_, _, v) => | ||
v | ||
} | ||
|
||
//## CONSTRUCTORS | ||
|
||
export create = Preserve(false) | ||
/** | ||
* Create a Preserved value in memory | ||
* @since 0.0.1 | ||
*/ | ||
export create = (file, v) => pipe( | ||
FS.normalize, | ||
Preserve(true, $, v), | ||
)(file) | ||
|
||
// methods | ||
/** | ||
* Load a Preserved value from source | ||
* @since 0.0.1 | ||
*/ | ||
load :: String -> Memory | ||
export load = (file) => pipe( | ||
FS.normalize, | ||
File.read, | ||
map(Preserve(true, file)), | ||
)(file) | ||
|
||
save :: Preserve -> String -> Wish Error {} | ||
export save = (src, data) => where(src) { | ||
Preserve(dirty, file) => | ||
dirty ? File.write(file, data) : Wish.good({}) | ||
//## METHODS | ||
|
||
/** | ||
* Set the preserved value and set clean to false | ||
*/ | ||
set :: String -> Preserve -> Preserve | ||
export set = (x, p) => where(p) { | ||
Preserve(d, s, _) => | ||
Preserve(false, s, x) | ||
} | ||
|
||
forceSave :: Preserve -> String -> Wish Error {} | ||
export forceSave = (src, data) => where(src) { | ||
Preserve(dirty, file) => | ||
pipe( | ||
File.write(file), | ||
map(always({})), | ||
)(data) | ||
/** | ||
* Transform the preserved value and set clean to false | ||
* @since 0.0.1 | ||
*/ | ||
update :: (String -> String) -> Preserve -> Preserve | ||
export update = (fn, p) => where(p) { | ||
Preserve(d, s, v) => | ||
Preserve(false, s, fn(v)) | ||
} | ||
|
||
load :: Preserve -> Wish Error String | ||
export load = where { | ||
Preserve(_, file) => | ||
File.read(file) | ||
/** | ||
* Set dirtiness | ||
* @since 0.0.1 | ||
*/ | ||
setClean :: Preserve -> Preserve | ||
export setClean = where { | ||
Preserve(_, s, v) => | ||
Preserve(true, s, v) | ||
} | ||
|
||
flush :: Preserve -> String -> Wish Error {} | ||
export flush = (src, data) => where(src) { | ||
Preserve(dirty, file) => | ||
if (dirty) { | ||
/** | ||
* Set cleanliness | ||
* @since 0.0.1 | ||
*/ | ||
setDirty :: Preserve -> Preserve | ||
export setDirty = where { | ||
Preserve(_, s, v) => | ||
Preserve(false, s, v) | ||
} | ||
|
||
isEmpty :: Preserve -> Boolean | ||
export isEmpty = where { | ||
Preserve(_, _, v) => | ||
v == "" | ||
} | ||
|
||
/** | ||
* Save the file to source only if it is dirty | ||
* @since 0.0.1 | ||
*/ | ||
save :: Preserve -> Memory | ||
export save = (pres) => ifElse( | ||
isDirty, | ||
pipe( | ||
setClean, | ||
flush, | ||
), | ||
Wish.good, | ||
)(pres) | ||
|
||
/** | ||
* Mount the preserved value from source (or write a default value if no value is present) | ||
* @since 0.0.1 | ||
*/ | ||
ensure :: String -> String -> Memory | ||
export ensure = (src, defaultValue) => pipe( | ||
load, | ||
chain( | ||
(loaded) => if (isEmpty(loaded)) { | ||
pipe( | ||
File.write(file), | ||
map(always({})), | ||
)(data) | ||
set(defaultValue), | ||
flush, | ||
)(loaded) | ||
} else { | ||
Wish.good({}) | ||
} | ||
Wish.good(loaded) | ||
}, | ||
), | ||
)(src) | ||
|
||
/** | ||
* Write the value to source and set clean to true | ||
* @since 0.0.1 | ||
*/ | ||
flush :: Preserve -> Memory | ||
export flush = (pres) => { | ||
v = value(pres) | ||
f = source(pres) | ||
return pipe( | ||
File.write(f), | ||
map(always(Preserve(true, f, v))), | ||
)(v) | ||
} | ||
|
||
remember :: Preserve -> String -> Wish Error (Memory String) | ||
export remember = (src, initialValue) => { | ||
x = initialValue | ||
changed = false | ||
controller = { | ||
get: () => x, | ||
set: (y) => if (!changed) { | ||
y | ||
} else do { | ||
x := y | ||
changed := true | ||
return y | ||
}, | ||
isDirty: () => changed, | ||
} | ||
/** | ||
* Write the value to a temp file and set clean to true | ||
* @since 0.0.1 | ||
*/ | ||
backup :: Preserve -> Memory | ||
export backup = (pres) => { | ||
v = value(pres) | ||
raw = source(pres) | ||
tmp = FS.tempdir(raw) | ||
return pipe( | ||
forceSave(src), | ||
map(always(controller)), | ||
)(x) | ||
File.write(tmp), | ||
map(always(Preserve(true, raw, v))), | ||
)(v) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { Error, assertEquals, test } from "Test" | ||
import Wish from "Wish" | ||
|
||
import FS from "@/FS" | ||
import { | ||
Preserve, | ||
create, | ||
ensure, | ||
isClean, | ||
isDirty, | ||
isEmpty, | ||
load, | ||
set, | ||
setClean, | ||
setDirty, | ||
source, | ||
update, | ||
value, | ||
} from "@/Jam" | ||
import { asyncReport, report } from "@/test" | ||
|
||
|
||
|
||
CLEAN = Preserve(true, "src", "raw") | ||
DIRTY = Preserve(false, "src", "raw") | ||
|
||
report(isClean, "isClean", [#[CLEAN, true], #[DIRTY, false]]) | ||
report(isDirty, "isDirty", [#[CLEAN, false], #[DIRTY, true]]) | ||
report(source, "source", [#[DIRTY, "src"]]) | ||
report(value, "value", [#[DIRTY, "raw"]]) | ||
|
||
report( | ||
create("~/whatever"), | ||
"create", | ||
[#["blah", Preserve(true, FS.normalize("~/whatever"), "blah")]], | ||
) | ||
|
||
report( | ||
set("test value!"), | ||
"set", | ||
[#[Preserve(true, "./test-location", "yyy"), Preserve(false, "./test-location", "test value!")]], | ||
) | ||
|
||
report( | ||
update((raw) => "hello " ++ raw), | ||
"update", | ||
[ | ||
#[ | ||
Preserve(true, "./test-location", "there"), | ||
Preserve(false, "./test-location", "hello there"), | ||
], | ||
], | ||
) | ||
|
||
report( | ||
setClean, | ||
"setClean", | ||
[#[Preserve(false, "./clean-me", "value"), Preserve(true, "./clean-me", "value")]], | ||
) | ||
|
||
report( | ||
setDirty, | ||
"setDirty", | ||
[#[Preserve(true, "./me-dirty", "value"), Preserve(false, "./me-dirty", "value")]], | ||
) | ||
|
||
report( | ||
isEmpty, | ||
"isEmpty", | ||
[ | ||
#[Preserve(true, "./some-place", ""), true], | ||
#[Preserve(true, "./some-place", "not empty"), false], | ||
], | ||
) | ||
|
||
asyncReport( | ||
pipe( | ||
load, | ||
Wish.mapRej((_) => Error("Broken!")), | ||
), | ||
"load", | ||
[#["./fixture-file", Preserve(true, "./fixture-file", "this is a fixture!\n")]], | ||
) | ||
|
||
asyncReport( | ||
pipe( | ||
ensure("./fixture-file"), | ||
Wish.mapRej((_) => Error("Broken!")), | ||
), | ||
"ensure", | ||
[#["default value", Preserve(true, "./fixture-file", "this is a fixture!\n")]], | ||
) |
Oops, something went wrong.