import gleam/io import gleam/list import glam/doc.{type Document} pub type TodoList = List(Task) pub type Task { Task(status: Status, description: String, subtasks: TodoList) } pub type Status { Todo Done InProgress } /// Pretty prints a `TodoList`: /// - each task is preceded by a bullet that indicates its status /// - each list of subtasks is indented by 2 spaces more than its father /// - each task should _always_ be on its own line, no matter how wide the /// maximum line width /// /// ## Examples /// /// ```gleam /// [ /// Task(Todo, "foo", []), /// Task(InProgress, "bar", []), /// Task(Done, "baz", []), /// ] /// |> tasks_to_doc /// |> doc.to_string(10_000) /// // -> /// // - [ ] foo /// // - […] bar /// // - [X] baz /// ``` /// /// ```gleam /// [ /// Task(InProgress, "foo", [ /// Task(Done, "bar", [ /// Task(Todo, "baz", []) /// ]) /// ]), /// ] /// |> tasks_to_doc /// |> doc.to_string(10_000) /// // -> /// // - […] foo /// // - [X] bar /// // - [ ] baz /// ``` /// pub fn tasks_to_doc(tasks: TodoList) -> Document { list.map(tasks, task_to_doc) |> doc.join(with: doc.soft_break) |> doc.force_break } fn task_to_doc(task: Task) -> Document { let task_line = status_to_bullet(task.status) <> " " <> task.description let task_doc = doc.from_string(task_line) case list.is_empty(task.subtasks) { True -> task_doc False -> [task_doc, doc.soft_break, tasks_to_doc(task.subtasks)] |> doc.concat |> doc.nest(by: 2) } } fn status_to_bullet(status: Status) -> String { case status { Todo -> "- [ ]" Done -> "- [X]" InProgress -> "- […]" } } pub fn example_todo_list() -> TodoList { [ Task(InProgress, "publish Glam v1.1.0", [ Task(InProgress, "write a tutorial on todo lists", []), Task(InProgress, "add `doc.flex_break`", [ Task(Done, "add the appropriate type variant", []), Task(Done, "implement the missing cases", []), Task(Todo, "add some tests", []), ]), ]), Task(Todo, "get some sleep", []), ] } pub fn main() { example_todo_list() |> tasks_to_doc |> doc.to_string(10_000) |> io.println }