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)
}
}
}
}