import argv
import clip
import clip/arg
import clip/help
import clip/opt
import envoy
import filepath
import gleam/bool
import gleam/dict
import gleam/function
import gleam/http
import gleam/http/request
import gleam/httpc
import gleam/io
import gleam/json
import gleam/list
import gleam/result
import gleam/string
import simplifile
import youid/uuid
type RawArgs {
RawPushArgs(path: String, url: String)
}
type Push {
Push(path: String, url: String, user_id: uuid.Uuid, api_key: uuid.Uuid)
}
pub fn main() -> Nil {
clip()
}
fn clip() -> Nil {
let result =
clip.subcommands([#("push", push_command())])
|> clip.help(help.simple(
"gleam run -m gleeps/dev",
"Tools for developing a gleeps bot",
))
|> clip.run(argv.load().arguments)
case result {
Error(e) -> io.println_error(e)
Ok(RawPushArgs(..) as args) -> {
use user_id <- ok_or_print(
envoy.get("GLEEPS_USERID")
|> result.try(uuid.from_base64),
"The GLEEPS_USERID environment variable is missing",
)
use api_key <- ok_or_print(
envoy.get("GLEEPS_APIKEY")
|> result.try(uuid.from_base64),
"The GLEEPS_APIKEY environment variable is missing",
)
case do_push(Push(path: args.path, url: args.url, api_key:, user_id:)) {
Ok(_) -> Nil
Error(message) -> io.println_error(message)
}
}
}
}
fn push_command() -> clip.Command(RawArgs) {
clip.command({
use path <- clip.parameter
use url <- clip.parameter
RawPushArgs(path:, url:)
})
|> clip.arg(path_arg())
|> clip.opt(url_opt())
|> clip.help(help.simple("push", "Submit your code to the gleeps server"))
}
fn url_opt() -> opt.Opt(String) {
opt.new("url")
|> opt.default("http://localhost:3000")
|> opt.help("The url of the server you want to push to")
}
fn path_arg() -> arg.Arg(String) {
arg.new("path")
|> arg.default("./src")
|> arg.help("Path to your ./src directory")
}
fn ok_or_print(
result: Result(a, Nil),
error_to_print: String,
continue: fn(a) -> Nil,
) -> Nil {
case result {
Ok(value) -> continue(value)
Error(_) -> io.println_error(error_to_print)
}
}
fn do_push(args: Push) {
use files <- result.try(
recursively_read_directory(args.path)
|> result.map_error(fn(error) {
"Failed to read directory: " <> simplifile.describe_error(error)
}),
)
use code <- result.try(read_file_content(files))
use req <- result.try(
request.to(args.url)
|> result.replace_error("The url you supplied is invalid"),
)
let resp =
req
|> request.set_method(http.Post)
|> request.set_path("code/submit")
|> request.set_header("user-id", args.user_id |> uuid.to_base64())
|> request.set_header("api-key", args.api_key |> uuid.to_base64())
|> request.set_body(modules_to_json_string(code))
|> httpc.send()
use resp <- result.try(
resp
|> result.map_error(fn(error) {
"The request failed: " <> string.inspect(error)
}),
)
{ "Server responded:\n" <> resp.body }
|> io.println()
|> Ok
}
fn modules_to_json_string(code: dict.Dict(String, String)) -> String {
json.object([
#("modules", json.dict(code, function.identity, json.string)),
])
|> json.to_string
}
fn read_file_content(
files: List(String),
) -> Result(dict.Dict(String, String), String) {
files
|> list.map(fn(module) {
use content <- result.try(
module
|> simplifile.read()
|> result.map_error(fn(error) {
"Failed to read file content '"
<> module
<> "' "
<> simplifile.describe_error(error)
<> " "
}),
)
Ok(#(module, content))
})
|> result.all()
|> result.map(dict.from_list)
}
fn recursively_read_directory(path: String) -> Result(List(String), _) {
use is_dir <- result.try(simplifile.is_directory(path))
use <- bool.guard(bool.negate(is_dir), Ok([path]))
use directory <- result.try(simplifile.read_directory(path))
list.map(directory, fn(file) {
path
|> filepath.join(file)
|> recursively_read_directory
})
|> result.all()
|> result.map(list.flatten)
}