import filepath import gladvent/internal/cmd import gladvent/internal/file import gladvent/internal/parse.{type Day} import gleam/int import gleam/list import gleam/pair import gleam/result import gleam/string import glint import simplifile as efile type Context { Context(year: Int, day: Day, add_parse: Bool) } fn create_src_dir(ctx: Context) { ctx.year |> cmd.src_dir() |> create_dir } fn create_src_file(ctx: Context) { let gleam_src_path = gleam_src_path(ctx.year, ctx.day) let file_data = case ctx.add_parse { True -> parse_starter <> "\n" <> gleam_starter False -> gleam_starter } gleam_src_path |> file.do_with_file(file.write(_, file_data)) |> result.flatten |> result.map_error(handle_file_open_failure(_, gleam_src_path)) |> result.replace(gleam_src_path) } fn create_input_root(_ctx: Context) { cmd.input_root() |> create_dir } fn create_input_dir(ctx: Context) { ctx.year |> cmd.input_dir |> create_dir } fn create_input_file(ctx: Context) { let input_path = input_path(ctx.year, ctx.day) file.do_with_file(input_path, fn(_) { Nil }) |> result.map_error(handle_file_open_failure(_, input_path)) |> result.replace(input_path) } type Err { FailedToCreateDir(String) FailedToCreateFile(String) FileAlreadyExists(String) } fn err_to_string(e: Err) -> String { case e { FailedToCreateDir(d) -> "failed to create dir: " <> d FailedToCreateFile(f) -> "failed to create file: " <> f FileAlreadyExists(f) -> "file already exists: " <> f } } fn input_path(year: Int, day: Day) -> String { filepath.join(cmd.input_dir(year), int.to_string(day) <> ".txt") } fn gleam_src_path(year: Int, day: Day) -> String { filepath.join(cmd.src_dir(year), "day_" <> int.to_string(day) <> ".gleam") } fn create_dir(dir: String) -> Result(String, Err) { efile.create_directory(dir) |> handle_dir_open_res(dir) } fn handle_dir_open_res( res: Result(_, efile.FileError), filename: String, ) -> Result(String, Err) { case res { Ok(_) -> Ok(filename) Error(efile.Eexist) -> Ok("") _ -> filename |> FailedToCreateDir |> Error } } fn handle_file_open_failure(reason: efile.FileError, filename: String) -> Err { case reason { efile.Eexist -> FileAlreadyExists(filename) _ -> FailedToCreateFile(filename) } } fn do(ctx: Context) -> String { let seq = [ create_input_root, create_input_dir, create_input_file, create_src_dir, create_src_file, ] let successes = fn(good) { case good { "" -> "" _ -> "created:" <> good } } let errors = fn(errs) { case errs { "" -> "" _ -> "errors:" <> errs } } let newline_tab = fn(a, b) { a <> "\n\t" <> b } let #(good, bad) = { use acc, f <- list.fold(seq, #("", "")) case f(ctx) { Ok("") -> acc Ok(o) -> pair.map_first(acc, newline_tab(_, o)) Error(err) -> pair.map_second(acc, newline_tab(_, err_to_string(err))) } } |> pair.map_first(successes) |> pair.map_second(errors) [good, bad] |> list.filter(fn(s) { s != "" }) |> string.join("\n\n") } const gleam_starter = "pub fn pt_1(input: String) { todo as \"part 1 not implemented\" } pub fn pt_2(input: String) { todo as \"part 2 not implemented\" } " const parse_starter = "pub fn parse(input: String) -> String { todo as \"parse not implemented\" } " fn collect_async(year: Int, x: #(Day, Result(String, String))) -> String { fn(res) { case res { Ok(res) -> res Error(err) -> err } } |> pair.map_second(x, _) |> collect(year, _) } fn collect(year: Int, x: #(Day, String)) -> String { let day = int.to_string(x.0) let year = int.to_string(year) "initialized " <> year <> " day " <> day <> "\n" <> x.1 } pub fn new_command() { use <- glint.command_help("Create .gleam and input files") use <- glint.unnamed_args(glint.MinArgs(1)) use parse <- glint.flag( glint.bool_flag("parse") |> glint.flag_default(False) |> glint.flag_help("Generate day runners with a parse function"), ) use _, args, flags <- glint.command() use days <- result.map(parse.days(args)) let assert Ok(year) = glint.get_flag(flags, cmd.year_flag()) let assert Ok(parse) = parse(flags) cmd.exec( days, cmd.Endless, fn(day) { do(Context(year, day, parse)) }, collect_async(year, _), ) }