import filepath import gleam/io import gleam/list import gleam/result import gleam/string import proute/config import proute/discover import proute/exit import proute/generate/page_input import proute/generate/pages import proute/generate/routes import proute/validate/pages as page_validation import simplifile type GeneratedMount { GeneratedMount(files: List(GeneratedFile)) } pub type GeneratedFile { GeneratedFile(path: String, contents: String) } pub type ProuteError { ConfigError(error: config.ConfigError) DiscoveryError(error: discover.DiscoverError) ValidationErrors(errors: List(page_validation.ValidationError)) WriteError(path: String) } pub fn main() { case run() { Ok(files) -> files |> list.each(fn(file) { let GeneratedFile(path:, ..) = file io.println("Generated " <> path) }) Error(error) -> { io.println_error(describe_error(error)) exit.failure() } } } fn run() -> Result(List(GeneratedFile), ProuteError) { use config <- result.try(load() |> result.map_error(ConfigError)) run_config(config) } pub fn run_config( config: config.Config, ) -> Result(List(GeneratedFile), ProuteError) { use generated <- result.try(generate_all(config)) use files <- result.try(write_all(generated)) Ok(files) } fn load() { case find_config_path() { Ok(path) -> case simplifile.read(path) { Ok(source) -> config.parse(source) Error(_) -> Error(config.MissingConfig(path)) } Error(_) -> Error(config.MissingConfig("proute.toml")) } } fn generate_all( config: config.Config, ) -> Result(List(GeneratedFile), ProuteError) { use generated <- result.try( config.mounts |> list.try_map(generate_mount), ) Ok(generated |> list.flat_map(fn(mount) { mount.files })) } fn generate_mount(mount: config.Mount) -> Result(GeneratedMount, ProuteError) { use mount_routes <- result.try( discover.discover_mount(mount) |> result.map_error(DiscoveryError), ) use page_modules <- result.try( page_validation.validate_mount(mount_routes) |> result.map_error(ValidationErrors), ) Ok( GeneratedMount(files: [ GeneratedFile( path: mount.routes_output, contents: routes.generate(mount_routes), ), GeneratedFile( path: mount.page_input_output, contents: page_input.generate(mount_routes), ), GeneratedFile( path: mount.pages_output, contents: pages.generate( mount_routes, page_modules, mount.page_shared_state_type, ), ), ]), ) } fn write_all( files: List(GeneratedFile), ) -> Result(List(GeneratedFile), ProuteError) { use _ <- result.try( files |> list.try_map(fn(file) { let GeneratedFile(path:, contents:) = file write_file(path, contents) }), ) Ok(files) } fn write_file(path: String, contents: String) -> Result(Nil, ProuteError) { use _ <- result.try( simplifile.create_directory_all(filepath.directory_name(path)) |> result.map_error(fn(_) { WriteError(path) }), ) simplifile.write(to: path, contents: contents) |> result.map_error(fn(_) { WriteError(path) }) } fn describe_error(error: ProuteError) -> String { case error { ConfigError(error) -> config.describe_error(error) DiscoveryError(error) -> discover.describe_error(error) ValidationErrors(errors) -> errors |> list.map(page_validation.describe_error) |> string.join("\n") WriteError(path) -> "Could not write generated file " <> string.inspect(path) <> "." } } fn find_config_path() -> Result(String, Nil) { case simplifile.current_directory() { Ok(directory) -> find_config_path_from(directory) Error(_) -> Error(Nil) } } fn find_config_path_from(directory: String) -> Result(String, Nil) { let path = directory <> "/proute.toml" case simplifile.is_file(path) { Ok(True) -> Ok(path) _ -> { let parent = filepath.directory_name(directory) case parent == directory { True -> Error(Nil) False -> find_config_path_from(parent) } } } }