summaryrefslogtreecommitdiffhomepage
path: root/gleam/src/ocda/load.gleam
blob: 2a82ac930906cb5f7323f873b3ead5ba46aa59ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//// What a path is, and loading it.
////
//// One place that decides, so that every reader and every writer accepts
//// the same inputs and a new input form is added once. This mirrors
//// spine's own `ocda/abstraction/load.d`, which names the sources it can
//// enter the pipeline from and tells them apart by suffix.
////
//// Deliberately *not* here: `.sst` and `.ssm` markup. Reading markup means
//// implementing the parser, which is spine's work and a far larger thing
//// than a reader or a sample writer needs. This library reads
//// abstractions.

import gleam/list
import gleam/string
import ocda/load_db
import ocda/load_pod
import ocda/load_ssp
import ocda/model.{type Document}

pub type Source {
  /// `.ssp`, the abstraction as text
  Ssp
  /// `.ocda.db`, the abstraction as sqlite
  OcdaDb
  /// `.sisupod` or `.zip`, an archive carrying a `.ssp`
  Pod
  Unknown
}

/// `.sisupod` is the archive spine writes now; `.zip` is what it wrote
/// before, and every pod already published carries that name, so both are
/// recognised. What is inside decides how it is opened, not the name.
const suffixes = [
  #(".ocda.db", OcdaDb),
  #(".ssp", Ssp),
  #(".sisupod", Pod),
  #(".zip", Pod),
  #(".db", OcdaDb),
]

pub fn source_of(path: String) -> Source {
  case list.find(suffixes, fn(s) { string.ends_with(path, s.0) }) {
    Ok(#(_, source)) -> source
    Error(_) -> Unknown
  }
}

pub fn source_name(source: Source) -> String {
  case source {
    Ssp -> ".ssp (the abstraction as text)"
    OcdaDb -> ".ocda.db (the abstraction as sqlite)"
    Pod -> "a pod archive carrying a .ssp"
    Unknown -> "not an abstraction this library reads"
  }
}

/// Load whatever the path is into the model.
///
/// `lang` names which language to take from a `.ocda.db`, which holds
/// every language of its document, and from a pod, which may carry more than
/// one `.ssp`. A `.ssp` is one language by construction and ignores it.
pub fn load(
  path: String,
  lang: String,
  any_version: Bool,
) -> Result(Document, String) {
  case source_of(path) {
    Ssp -> load_ssp.load_file(path)
    OcdaDb -> load_db.load_file(path, lang, any_version)
    Pod -> load_pod.load_file(path, lang)
    Unknown ->
      Error(
        path
        <> ": "
        <> source_name(Unknown)
        <> " (expected .ssp, .ocda.db, .sisupod or .zip)",
      )
  }
}