save
Keep small named pieces of text, such as settings and scores, between runs of a program.
import "std:save"std:save gives a program a folder of its own where it can keep named items between runs: settings, a high score, a token, the last thing the user opened. Each item is a file, written safely so a crash can't leave half of one. Use it for small per-user data. For anything you need to search or update in parts, use db, and for files you choose the location of, use fs.
Items are text. To store a number, a list or a record, turn it into text with json on the way in and back on the way out.
Open a store and keep an item#
save.open(appName) returns the store for an app, creating its folder under ~/.osl/apps when needed. set stores text under a name and get reads it back:
import "std:save"
auto store = save.open("docs-save-basics").unwrap()
// Start clean, because this example runs more than once.
for key in store.keys().unwrap() (
store.remove(key).unwrap()
)
store.set("name", "Ada").unwrap()
log store.get("name").unwrap()
store.set("name", "Grace").unwrap()
log store.get("name").unwrap()
log store.dir().endsWith("docs-save-basics")Run it twice with the cleanup loop deleted: the old value is still there on the second run, because it lives on disk.
Setting a name again replaces the old value. The write goes to a temporary file first and is renamed into place, so a reader sees the old text or the new text and never a mixture.
Pick an app name that is yours alone. Two programs that open the same name share a store.
Names and files#
A name without an extension is stored as name.txt, so "score" and "score.txt" are the same item. Give a name an extension when the content has one, such as settings.json. keys() lists what is stored, sorted, and path(name) tells you which file holds an item, so other tools can open it:
import "std:save"
import "std:fs"
auto store = save.open("docs-save-keys").unwrap()
for key in store.keys().unwrap() (
store.remove(key).unwrap()
)
store.set("note", "buy milk").unwrap()
store.set("theme.json", `{"dark": true}`).unwrap()
log store.keys().unwrap()
log store.has("note") store.has("missing")
log fs.readFile(store.path("note")).unwrap()Missing items#
get and remove fail with a NotFoundError for an item that was never stored. That is a normal first-run situation, so give it a default with unwrapOr, or branch on has:
import "std:save"
auto store = save.open("docs-save-results").unwrap()
for key in store.keys().unwrap() (
store.remove(key).unwrap()
)
string name = store.get("name").unwrapOr("stranger")
log "hello," name
log store.get("name").unwrapErr().kind
store.set("name", "Ada").unwrap()
store.remove("name").unwrap()
log store.has("name")
log store.remove("name").isErr()Saving structured data#
Combine the store with std:json and a struct to keep a settings record. Parsing checks the stored text against the struct, so a hand-edited file with a wrong type is an error you can handle:
import "std:save"
import "std:json"
struct Settings (
string theme = "light"
int fontSize = 14
)
auto store = save.open("docs-save-json").unwrap()
for key in store.keys().unwrap() (
store.remove(key).unwrap()
)
def load(*save.Store store) Settings (
string text = store.get("settings.json").unwrapOr("{}")
return json.parse<Settings>(text).unwrapOr(Settings())
)
Settings s = load(store)
log s.theme s.fontSize
s.theme = "dark"
s.fontSize = 16
store.set("settings.json", json.stringify(s)).unwrap()
Settings again = load(store)
log again.theme again.fontSize
log store.get("settings.json").unwrap()The loader falls back to a default Settings() in two cases: nothing is stored yet, or what is stored can't be parsed. A program that must not lose bad data should keep a copy before overwriting it.
You call store.get("score") on a first run, before anything was saved. What do you get?
Visit counter
Count visits across runs. visit() reads visits (0 when missing), adds one, saves it and returns the new count. Call it three times.
1 2 3
API reference#
20 members, generated from the std:save source of OSL 9.3.0.
Functions
save.all() arrayDeprecated. Deprecated: use save.open(appName) and store.keys() instead.
all lists the shared store's file names.
save.create() *save.SaveDeprecated. Deprecated: use save.open(appName) instead.
create returns a new, uninitialised store.
save.exists(string filename) booleanDeprecated. Deprecated: use save.open(appName) and store.has(name) instead.
exists reports whether the shared store has an item.
save.getItem(string filename) objectDeprecated. Deprecated: use save.open(appName) and store.get(name) instead.
getItem returns {app, filename, exists, data, size} from the shared store.
save.init(string appName) booleanDeprecated. Deprecated: use save.open(appName) instead.
init points the package's shared store at an app and reports whether it worked.
save.open(string appName) result<*save.Store, error>open returns the store for an app, creating its directory ~/.osl/apps/<appName> if needed. Throws ValueError when appName isn't a plain file name; fails with NotFoundError when the home directory isn't known, PermissionError or IOError.
save.setItem(string filename, any value) stringDeprecated. Deprecated: use save.open(appName) and store.set(name, text) instead.
setItem stores a value in the shared store and returns its path, or "".
*save.Store
store.dir() stringdir returns the store's directory.
store.get(string name) result<string, error>get returns the text stored under name. Fails with NotFoundError when there is no such item. Throws ValueError for an invalid name.
store.has(string name) booleanhas reports whether an item is stored under name. Invalid names have no item.
store.keys() result<string[], error>keys lists the stored items' file names (such as "score.txt"), sorted. Fails with NotFoundError when the store's directory has been deleted.
store.path(string name) stringpath returns the file an item is stored in. A name without an extension is stored as name.txt, so "score" and "score.txt" are the same item. Throws ValueError for a name with slashes or a trailing ".".
store.remove(string name) result<unit, error>remove deletes an item. Fails with NotFoundError when there is no such item. Throws ValueError for an invalid name.
store.set(string name, string text) result<unit, error>set stores text under name, replacing any earlier value atomically. Throws ValueError for an invalid name; fails with PermissionError or IOError.
*save.Save
save.all() arrayDeprecated. Deprecated: use store.keys() instead.
all lists stored file names, or [] when they can't be read.
save.create() *save.SaveDeprecated. Deprecated: use save.open(appName) instead.
create returns a new, uninitialised store.
save.exists(string filename) booleanDeprecated. Deprecated: use store.has(name) instead.
exists reports whether an item is stored.
save.getItem(string filename) objectDeprecated. Deprecated: use store.get(name) and store.has(name) instead.
getItem returns {app, filename, exists, data, size} for an item.
save.init(string appName) booleanDeprecated. Deprecated: use save.open(appName) instead.
init points the store at an app's directory and reports whether it worked.
save.setItem(string filename, any value) stringDeprecated. Deprecated: use store.set(name, text) instead.
setItem stores a value and returns its path, or "".