aboutsummaryrefslogtreecommitdiff
path: root/src/lustre/cli/try.gleam
blob: 42f38547a23a93451066f95f90cf2ba30f03fe69 (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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// IMPORTS ---------------------------------------------------------------------

import gleam_community/ansi
import gleam/int
import gleam/io
import gleam/result
import glint.{type Command, CommandInput}
import glint/flag
import lustre/cli/project

// TYPES -----------------------------------------------------------------------

type Options {
  /// It's important to remember that for Erlang, Gleam records have their field
  /// names erased and they degenerate to tuples. This means that the order of
  /// the fields is important!
  Options(name: String, host: String, port: Int, no_styles: Bool)
}

type Error {
  CompileError
}

// MAIN ------------------------------------------------------------------------

pub fn run() -> Command(Nil) {
  glint.command(fn(input) {
    let CommandInput(_, flags, _) = input
    let assert Ok(port) = flag.get_int(flags, port_flag_name)
    let assert Ok(host) = flag.get_string(flags, host_flag_name)
    let assert Ok(no_styles) = flag.get_bool(flags, no_styles_flag_name)

    let result = {
      let compile = result.replace_error(project.build(), CompileError)
      use compiled <- result.try(compile)
      let configuration = project.read_configuration(compiled)
      let options = Options(configuration.name, host, port, no_styles)
      serve(options, on_start(host, _), on_port_taken)
      Ok(Nil)
    }

    case result {
      Ok(_) -> Nil
      Error(error) -> explain(error)
    }
  })
  |> glint.flag(host_flag_name, host_flag())
  |> glint.flag(port_flag_name, port_flag())
  |> glint.flag(no_styles_flag_name, no_styles_flag())
}

// UTILS -----------------------------------------------------------------------

fn explain(error: Error) -> Nil {
  case error {
    CompileError -> Nil
  }
}

// GLINT FLAGS -----------------------------------------------------------------

const host_flag_name = "host"

fn host_flag() {
  flag.string()
  |> flag.default("localhost")
  |> flag.description("The host to run the server on.")
}

const port_flag_name = "port"

fn port_flag() {
  flag.int()
  |> flag.default(1234)
  |> flag.description("The port to run the server on.")
}

const no_styles_flag_name = "no-styles"

fn no_styles_flag() {
  flag.bool()
  |> flag.default(False)
  |> flag.description("When false, lustre/ui's styles will not be included.")
}

// UTILS -----------------------------------------------------------------------

fn on_start(host: String, port: Int) -> Nil {
  let address = "http://" <> host <> ":" <> int.to_string(port)
  io.println("✨ Server has been started at " <> ansi.bold(address))
}

fn on_port_taken(port) -> Nil {
  io.println(
    "🚨 Port "
      <> ansi.bold(int.to_string(port))
      <> " already in use, using next available port",
  )
}

// EXTERNALS -------------------------------------------------------------------

@external(erlang, "lustre_try_ffi", "serve")
fn serve(
  options: Options,
  on_start: fn(Int) -> Nil,
  on_port_taken: fn(Int) -> Nil,
) -> Nil