{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ceca4e9d",
   "metadata": {},
   "source": [
    "---\n",
    "title: \"SideQuest 1:\"\n",
    "author: The MidWit\n",
    "date: 2026-05-31\n",
    "description: \"It's dangerous to go alone, take this...\"\n",
    "categories: [student-log, rust, advent-of-code, Errors, crates]\n",
    "draft: false\n",
    "execute:\n",
    "  eval: false\n",
    "---\n",
    "\n",
    "::: {.callout-note}\n",
    "If you just want to jump straight to the whole implementation you can skip [here](#full-implementation)\n",
    ":::\n",
    "\n",
    "# Problem {#problem}\n",
    "\n",
    "So far, all of the [AOC](https://adventofcode.com/) puzzles have given me long strings of text as input, and the easiest way for me to use them has been to put them into a file called \"./input.txt\" and then read that file in. Now, obviously `Rust` has standard implementations for that but, in the process of [solving the day 1 puzzle](./day_1.qmd) it occurred to me that it would be a good idea for me to practice things like writing my own `types`, `errors`, and `functions`, *and* setting up my own modules/crates to use, as is my prerogative. \n",
    "\n",
    "Also, it seemed like an opportunity to practice a little [Test Driven Development](https://en.wikipedia.org/wiki/Test-driven_development) (TDD) which comes recommended in [The Book](https://doc.rust-lang.org/book/). It would cost me nothing to try it out right?\n",
    "\n",
    "**Right!?** \n",
    "\n",
    "<details>\n",
    "<summary>Spoiler: was TDD fine?</summary>\n",
    "Yes. It was fine. Calm down.\n",
    "</details>\n",
    "\n",
    "## Aims\n",
    "\n",
    " - [ ] Create a `lib` crate to hold the functionality\n",
    " - [ ] Define my own Error `Enum`\n",
    " - [ ] Implement the `Display Trait` on it (and others that I may need that I don't know about yet). \n",
    " - [ ] Write my own version of `std::fs::read_to_string` that use that custom Error\n",
    "\n",
    "### Limitations (cause like, I know where I'm at)\n",
    "\n",
    " - I'm just going to use `read_to_string` as the core of my new function, I ain't got the skills to roll my own from scratch... yet. \n",
    " - The crate must be useable in future problem sets \n",
    " - Claude is allowed to help, but can't generate any code. \n",
    " - I'm going to really try TDD, but I give myself an eject button if I find that doesn't help. \n",
    "\n",
    "# Phase one - The Setupening\n",
    "\n",
    "This one is easy, and I'm taking the win early. Rust's package manager `Cargo` has one-liner for getting this up and running.\n",
    "\n",
    "```{bash}\n",
    "$Cargo new aoc_common --lib\n",
    "```\n",
    "This creates a new directory containing everything needed for a library crate, the main difference being that instead of `./src/main.rs` we get `./src/lib.rs`. The code below is the boilerplate that `Cargo` produces. \n",
    "\n",
    "```{rust}\n",
    "#| label: lib-1\n",
    "\n",
    "pub fn add(left: u64, right: u64) -> u64 {\n",
    "    left + right\n",
    "}\n",
    "\n",
    "#[cfg(test)]\n",
    "mod tests {\n",
    "    use super::*;\n",
    "\n",
    "    #[test]\n",
    "    fn it_works() {\n",
    "        let result = add(2, 2);\n",
    "        assert_eq!(result, 4);\n",
    "    }\n",
    "}\n",
    "\n",
    "```\n",
    "Honestly, this is great because it gives me a lot of what I need regarding the tests. So yeah, easy win on step one. **I feel like a genius!!**\n",
    "\n",
    "\n",
    " - [x] Create a `lib` crate to hold the functionality\n",
    "\n",
    "# Phase two - ComfortablEnum[^1]\n",
    "\n",
    "[^1]: You better believe I'm delighted with that!\n",
    "\n",
    "Without blinding myself with science an [\\`Enum\\`](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html) is a particular kind of `Struct` that allows us to specify `variants` inside of them. They are a *relatively* simple kind of object that allows us to hold much more complex data. `Results` and `Option` are both kinds of `Enums` and so they're great for matching. \n",
    "\n",
    "So the first thing to do is just define the `Enum` which will carry my specific expected cases: \n",
    "\n",
    " - Where we can't find the file\n",
    " - Where we don't have permission to read the file\n",
    " - Where the file already exist (although that not realy relevant for this case)\n",
    " - And a general case to capture whole universe of other BS that I don't know I don't know. \n",
    "\n",
    "\n",
    "```{rust}\n",
    "#| label: enum-1\n",
    "enum FileIoError{ // defining the enum \n",
    "    FileNotFound(String),// note that each variant can carry a type within it\n",
    "    NoPermission(String), \n",
    "    FileExists(String),\n",
    "    Unknown(String),\n",
    "}\n",
    "```\n",
    "\n",
    "And there we go, we have an `enum` for us to do with as we please[^2]. Now we just need to think of things to do with it. \n",
    "\n",
    "[^2]: The power!\n",
    "\n",
    "## Aims of the `enum`\n",
    "\n",
    "At the end of the day this `enum` is intended to form the return type in my own `read_challenge_input(path: &str) -> Result<String, FileIoError>` function and I want to work up to that. Maybe the best place to start is with a `FileIoError::new()` implementation, and so I'll write a test and try to get it to pass. \n",
    "\n",
    "```{rust}\n",
    "\n",
    "enum FileIoError{\n",
    "    FileNotFound(String),\n",
    "    NoPermission(String), \n",
    "    FileExists(String),\n",
    "    Unknown(String),\n",
    "}\n",
    "\n",
    "#[cfg(test)]\n",
    "mod tests {\n",
    "    use super::*;\n",
    "\n",
    "    #[test]\n",
    "    fn test_new() {\n",
    "        let test_error = FileIoError::new();\n",
    "        assert_eq!(test_error, FileIoError::FileNotFound);\n",
    "    }\n",
    "}\n",
    "```\n",
    "\n",
    "and let's run `Cargo test` and see wha'happen\n",
    "\n",
    "```{ansi}\n",
    "#| label: test-1\n",
    "warning: enum `FileIoError` is never used\n",
    " --> src/lib.rs:1:6\n",
    "  |\n",
    "1 | enum FileIoError{\n",
    "  |      ^^^^^^^^^^^\n",
    "  |\n",
    "  = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n",
    "\n",
    "warning: `aoc_common` (lib) generated 1 warning\n",
    "error[E0599]: no variant or associated item named `new` found for enum `FileIoError` in the current scope\n",
    "  --> src/lib.rs:14:39\n",
    "   |\n",
    " 1 | enum FileIoError{\n",
    "   | ---------------- variant or associated item `new` not found for this enum\n",
    "...\n",
    "14 |         let test_error = FileIoError::new();\n",
    "   |                                       ^^^ variant or associated item not found in `FileIoError`\n",
    "\n",
    "For more information about this error, try `rustc --explain E0599`.\n",
    "error: could not compile `aoc_common` (lib test) due to 1 previous error\n",
    "\n",
    "shell returned 101\n",
    "```\n",
    "Unsurprisingly... ya can't test something you don't have. \n",
    "\n",
    "## Implementing `new()`\n",
    "\n",
    "The point of the `new()` function is to just give me back a `FileIoError` and so let's wire that up first. \n",
    "\n",
    "```{rust}\n",
    "#| label: impl-new-1\n",
    "\n",
    "impl FileIoError {\n",
    "    fn new() -> FileIoError {\n",
    "        FileIoError::FileNotFound(String::from(\"testing\"))\n",
    "    }\n",
    "}\n",
    "\n",
    "#[cfg(test)]\n",
    "mod tests {\n",
    "    use super::*;\n",
    "\n",
    "    #[test]\n",
    "    fn test_new() {\n",
    "        let test_error = FileIoError::new();\n",
    "        assert_eq!(test_error, FileIoError::FileNotFound(String::from(\"testing\")));\n",
    "    }\n",
    "}\n",
    "```\n",
    "```{ansi}\n",
    "#| label: test-2\n",
    "  Compiling aoc_common v0.1.0 (/home/sp1d3r-z3r0/MyProjects-tmp/midwitsanonymous/scratch/aoc_common)\n",
    "warning: enum `FileIoError` is never used\n",
    " --> src/lib.rs:1:6\n",
    "  |\n",
    "1 | enum FileIoError{\n",
    "  |      ^^^^^^^^^^^\n",
    "  |\n",
    "  = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n",
    "\n",
    "warning: associated function `new` is never used\n",
    " --> src/lib.rs:9:8\n",
    "  |\n",
    "8 | impl FileIoError {\n",
    "  | ---------------- associated function in this implementation\n",
    "9 |     fn new() -> FileIoError {\n",
    "  |        ^^^\n",
    "\n",
    "warning: `aoc_common` (lib) generated 2 warnings\n",
    "error[E0369]: binary operation `==` cannot be applied to type `FileIoError`\n",
    "  --> src/lib.rs:21:9\n",
    "   |\n",
    "21 |         assert_eq!(test_error, FileIoError::FileNotFound(String::from(\"testing\")));\n",
    "   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
    "   |         |\n",
    "   |         FileIoError\n",
    "   |         FileIoError\n",
    "   |\n",
    "note: an implementation of `PartialEq` might be missing for `FileIoError`\n",
    "  --> src/lib.rs:1:1\n",
    "  |\n",
    "  |\n",
    "note: an implementation of `PartialEq` might be missing for `FileIoError`\n",
    "  --> src/lib.rs:1:1\n",
    "   |\n",
    " 1 | enum FileIoError{\n",
    "   | ^^^^^^^^^^^^^^^^ must implement `PartialEq`\n",
    "help: consider annotating `FileIoError` with `#[derive(PartialEq)]`\n",
    "   |\n",
    " 1 + #[derive(PartialEq)]\n",
    " 2 | enum FileIoError{\n",
    "   |\n",
    "\n",
    "error[E0277]: `FileIoError` doesn't implement `Debug`\n",
    "  --> src/lib.rs:21:9\n",
    "   |\n",
    "21 |         assert_eq!(test_error, FileIoError::FileNotFound(String::from(\"testing\")));\n",
    "   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for\n",
    "`FileIoError`\n",
    "   |\n",
    "   = note: add `#[derive(Debug)]` to `FileIoError` or manually `impl Debug for FileIoError`\n",
    "help: consider annotating `FileIoError` with `#[derive(Debug)]`\n",
    "   |\n",
    " 1 + #[derive(Debug)]\n",
    " 2 | enum FileIoError{\n",
    "   |\n",
    "\n",
    "Some errors have detailed explanations: E0277, E0369.\n",
    "For more information about an error, try `rustc --explain E0277`.\n",
    "error: could not compile `aoc_common` (lib test) due to 3 previous error\n",
    "```\n",
    "*Sam Becket: \"Oh Boy...\"*\n",
    "\n",
    "OK so let's go through that from top to bottom:\n",
    "\n",
    "  1. `dead_code` and `never_used` warnings; ignore for now, nothing is wired up yet.\n",
    "  2. `E0369`;  `==` can't be applied to `FileIoError` because `PartialEq` isn't implemented.\n",
    "  3. `E0277`;  `FileIoError` doesn't implement `Debug`.\n",
    "\n",
    "So, there's two `Traits` (other than `Display`, which I mentioned earlier) that apparently need to be there for testing to work. The compiler (`Friend Computer`) has helpfully told us what we need and how to fix it: `derive`. Rather than needing to hand-roll my own implementation for every `Trait` we can use a `decorator` to just `derive` them (if possible) for that `Struct`\n",
    "\n",
    "```{rust}\n",
    "#| label: derive-1\n",
    "// -- snip --\n",
    "#[derive(Debug, PartialEq)]\n",
    "enum FileIoError{\n",
    "    FileNotFound(String),\n",
    "    NoPermission(String), \n",
    "    FileExists(String),\n",
    "    Unknown(String),\n",
    "}\n",
    "// -- snip --\n",
    "```\n",
    "\n",
    "```{ansi}\n",
    "#| label: derive-out-1\n",
    "// -- snip -- skipping all the unused warnings\n",
    "    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.42s\n",
    "     Running unittests src/lib.rs (target/debug/deps/aoc_common-7c1db3e116eaa741)\n",
    "\n",
    "running 1 test\n",
    "test tests::test_new ... ok\n",
    "\n",
    "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n",
    "\n",
    "   Doc-tests aoc_common\n",
    "\n",
    "running 0 tests\n",
    "\n",
    "test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n",
    "```\n",
    "Fellow Humans! We have our first *Boom!*[^boom1] We wrote a test and it passed!\n",
    "\n",
    "[^boom1]: I know that we could have called the `Cargo new` from earlier a boom, but it just didn't *feel* right y'know? You know.\n",
    "\n",
    "Now we just need to make it actually useful. \n",
    "\n",
    "## Using existing `Errors`\n",
    "\n",
    "Unsurprisingly rust has actually has all this functionality already; in the `std::io` crate. Really I'm just writing a wrapper around that. We need to bring that crate into our scope first with a `use` statement\n",
    "```{rust}\n",
    "#| label: use-io\n",
    "use std::io::ErrorKind; // This holds the std library's IO Errors\n",
    "// -- snip --\n",
    "```\n",
    "And then wire up the various `ErrorKind`s to my `FileIoError variants`\n",
    "\n",
    "```{rust}\n",
    "#| label: errorkind-fileioerr\n",
    "// -- snip --\n",
    "impl FileIoError {\n",
    "    fn new(e: ErrorKind) -> FileIoError {\n",
    "        match e {// taking the e matching on it. \n",
    "            ErrorKind::NotFound => FileIoError::FileNotFound(format!(\"No file at path specified {}\", e).to_string()), \n",
    "            ErrorKind::PermissionDenied => FileIoError::NoPermission(format!(\"Permission Denied {}\", e).to_string()), \n",
    "            ErrorKind::AlreadyExists => FileIoError::FileExists(format!(\"There is already a file at the path specified {}\", e).to_string()), \n",
    "            _ => FileIoError::Unknown(format!(\"{}\", e).to_string()),\n",
    "        }\n",
    "    }\n",
    "}\n",
    "// -- snip --\n",
    "\n",
    "    #[test]\n",
    "    fn test_new() {\n",
    "        let test_error = FileIoError::new(ErrorKind::NotFound);\n",
    "        assert_eq!(test_error, FileIoError::FileNotFound());\n",
    "    }\n",
    "// -- snip --\n",
    "```\n",
    "\n",
    "Now, I'm pretty sure this won't work properly because there's no `String` instide the `FileNotFound` in the test, but I have an idea for how I might fix that, and I'm just going to run the tests first. \n",
    "\n",
    "```{ansi}\n",
    "#| label: test-3\n",
    "// -- snip --\n",
    "warning: `aoc_common` (lib) generated 2 warnings\n",
    "   Compiling aoc_common v0.1.0 (/home/sp1d3r-z3r0/MyProjects-tmp/midwitsanonymous/scratch/aoc_common)\n",
    "error[E0061]: this enum variant takes 1 argument but 0 arguments were supplied\n",
    "  --> src/lib.rs:29:32\n",
    "   |\n",
    "29 |         assert_eq!(test_error, FileIoError::FileNotFound());\n",
    "   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^-- argument #1 of type `String` is missing\n",
    "   |\n",
    "note: tuple variant defined here\n",
    "  --> src/lib.rs:5:5\n",
    "   |\n",
    " 5 |     FileNotFound(String),\n",
    "   |     ^^^^^^^^^^^^\n",
    "help: provide the argument\n",
    "   |\n",
    "29 |         assert_eq!(test_error, FileIoError::FileNotFound(/* String */));\n",
    "   |                                                          ++++++++++++\n",
    "\n",
    "For more information about this error, try `rustc --explain E0061`.\n",
    "error: could not compile `aoc_common` (lib test) due to 1 previous error\n",
    "```\n",
    "\n",
    "Yeah, just as I thought; but it's nice to be Schrodinger's correct instead of just wrong. \n",
    "\n",
    "My idea is to use `format!` inside the test the same way I have up in the `new()` function. \n",
    "\n",
    "```{rust}\n",
    "#| label: format-test\n",
    "// -- snip --\n",
    "    #[test]\n",
    "    fn test_new() {\n",
    "        let test_error = FileIoError::new(ErrorKind::NotFound);\n",
    "        assert_eq!(\n",
    "            test_error,\n",
    "            FileIoError::FileNotFound(\n",
    "                    format!(\"No file at path specified {:?}\", ErrorKind::NotFound).to_string()\n",
    "                )\n",
    "            );\n",
    "    }\n",
    "```\n",
    "```{ansi}\n",
    "#| label: format-test1\n",
    "// -- snip --\n",
    "running 1 test\n",
    "test tests::test_new ... FAILED\n",
    "\n",
    "failures:\n",
    "\n",
    "---- tests::test_new stdout ----\n",
    "\n",
    "thread 'tests::test_new' (17597) panicked at src/lib.rs:29:9:\n",
    "assertion `left == right` failed\n",
    "  left: FileNotFound(\"No file at path specified entity not found\")\n",
    " right: FileNotFound(\"No file at path specified NotFound\")\n",
    "note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n",
    "\n",
    "\n",
    "failures:\n",
    "    tests::test_new\n",
    "\n",
    "test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n",
    "\n",
    "error: test failed, to rerun pass `--lib`\n",
    "// -- snip --\n",
    "```\n",
    "Ok! So the test is failing, but maybe it's because we're using the `debug` syntax in the `format!` call, but lets see what happens if we just remove that.\n",
    "\n",
    "```{ansi}\n",
    "#| label: format-test2\n",
    "// -- snip --\n",
    "running 1 test\n",
    "test tests::test_new ... ok\n",
    "\n",
    "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n",
    "// -- snip --\n",
    "```\n",
    "And there you have it! *Boom 2: Electric Boomaloo!*\n",
    "\n",
    "## Checkpoint 1\n",
    "\n",
    "Gotta say, I'm pretty stoked with that: \n",
    "\n",
    " - [x] Define my own Error `Enum`\n",
    " - Write a test to drive implementing `new()`\n",
    " - Implement `new()` iteratively until the test passed (while cheating a little because I didn't fully write the test at the outset)\n",
    "\n",
    "So the next thing to do I write the `Display` Trait\n",
    "\n",
    "## Implementing `Traits`\n",
    "\n",
    "We already know that some `Traits` can be derived with a `decorator` but others are more complex and require us to manually write the implementation. According to the [docs](https://doc.rust-lang.org/std/fmt/trait.Display.html) `Display` can't be derived because it's for \"user facing output\", which makes sense in the case of my `FileIoError` as I want to get this back if there's something wrong with my input reading in later AOC puzzles. Fortunately it's really easy to wire up. Just gotta write a test first, as per my own rules. \n",
    "\n",
    "```{rust}\n",
    "// -- snip --\n",
    "#| label: display-test-1\n",
    "    #[test]\n",
    "    fn test_display() {\n",
    "        let test_error = FileIoError::new(ErrorKind::NotFound);\n",
    "        assert_eq!(\n",
    "            format!(\"{}\", test_error),\n",
    "            format!(\"{}\", FileIoError::FileNotFound(\n",
    "                    format!(\"No file at path specified {}\", ErrorKind::NotFound).to_string()\n",
    "                )\n",
    "            )\n",
    "        )\n",
    "    }\n",
    "```\n",
    "\n",
    "So I don't know that the test works but hey, let's test a test!\n",
    "\n",
    "```{ansi}\n",
    "#| label: display-test-out-1\n",
    "// -- snip --\n",
    "warning: enum `FileIoError` is never used\n",
    " --> src/lib.rs:4:6\n",
    "  |\n",
    "4 | enum FileIoError{\n",
    "  |      ^^^^^^^^^^^\n",
    "  |\n",
    "  = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n",
    "\n",
    "warning: associated function `new` is never used\n",
    "  --> src/lib.rs:12:8\n",
    "   |\n",
    "11 | impl FileIoError {\n",
    "   | ---------------- associated function in this implementation\n",
    "12 |     fn new(e: ErrorKind) -> FileIoError {\n",
    "   |        ^^^\n",
    "\n",
    "warning: `aoc_common` (lib) generated 2 warnings\n",
    "error[E0277]: `FileIoError` doesn't implement `std::fmt::Display`\n",
    "  --> src/lib.rs:41:27\n",
    "   |\n",
    "41 |             format!(\"{}\", test_error),\n",
    "   |                      --   ^^^^^^^^^^ `FileIoError` cannot be formatted with the default formatter\n",
    "   |                      |\n",
    "   |                      required by this formatting parameter\n",
    "   |\n",
    "help: the trait `std::fmt::Display` is not implemented for `FileIoError`\n",
    "  --> src/lib.rs:4:1\n",
    "   |\n",
    "   |\n",
    " 4 | enum FileIoError{\n",
    "   | ^^^^^^^^^^^^^^^^\n",
    "   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead\n",
    "\n",
    "error[E0277]: `FileIoError` doesn't implement `std::fmt::Display`\n",
    "  --> src/lib.rs:42:27\n",
    "   |\n",
    "42 |               format!(\"{}\", FileIoError::FileNotFound(\n",
    "   |  ______________________--___^\n",
    "   | |                      |\n",
    "   | |                      required by this formatting parameter\n",
    "43 | |                     format!(\"No file at path specified {}\", ErrorKind::NotFound).to_string()\n",
    "44 | |                 )\n",
    "   | |_________________^ `FileIoError` cannot be formatted with the default formatter\n",
    "   |\n",
    "help: the trait `std::fmt::Display` is not implemented for `FileIoError`\n",
    "  --> src/lib.rs:4:1\n",
    "   |\n",
    " 4 | enum FileIoError{\n",
    "   | ^^^^^^^^^^^^^^^^\n",
    "   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead\n",
    "\n",
    "For more information about this error, try `rustc --explain E0277`.\n",
    "error: could not compile `aoc_common` (lib test) due to 2 previous errors\n",
    "```\n",
    "Welp! The test it telling me that it's failing because we haven't done the thing that would be needed to make it pass, which... is great(?). Also, it's telling us two ways to fix it, either use the `format` syntax or to implement `Display` so lets do that (copying directly from the docs). \n",
    "\n",
    "```{rust}\n",
    "#| label: impl-display\n",
    "use std::io::ErrorKind; // This holds the std library's IO Errors\n",
    "// -- snip --\n",
    "\n",
    "impl fmt::Display for FileIoError {\n",
    "    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n",
    "        write!(f, \"{}\", self)\n",
    "    }\n",
    "\n",
    "}\n",
    "// -- snip --\n",
    "```\n",
    "We have a fresh `Error` I've never seen before\n",
    "\n",
    "```{ansi}\n",
    "#| label: stack-overflow\n",
    "// -- snip --\n",
    "running 2 tests\n",
    "test tests::test_new ... ok\n",
    "\n",
    "thread 'tests::test_display' (20470) has overflowed its stack\n",
    "fatal runtime error: stack overflow, aborting\n",
    "error: test failed, to rerun pass `--lib`\n",
    "\n",
    "Caused by:\n",
    "  process didn't exit successfully: `/home/user/aoc_common/target/debug/deps/aoc_c\n",
    "ommon-7c1db3e116eaa741` (signal: 6, SIGABRT: process abort signal)\n",
    "```\n",
    "\n",
    "I've never actually seen a stack overflow before, and I have no idea what it means in this context so I'm going to practice a little google fu Aaaand if that doesn't work I'm going to call in Claude. \n",
    "\n",
    "## Google fu results\n",
    "\n",
    "So this [this post](https://users.rust-lang.org/t/how-to-diagnose-a-stack-overflow-issues-cause/17320/12)(skip to the end) tells me that I need to enable a debuger on my system, and right now I don't want to risk falling down a rabbit hole in the case that that doesn't work so let's ask Claude what's up. \n",
    "\n",
    "## Claude response\n",
    "\n",
    " >Claude responded: The Display implementation for FileIoError is calling itself recursively and infinitely.\n",
    ">In the fmt method, you wrote write!(f, \"{}\", self). The {} format specifier invokes Display on self — but self is a FileIoError, so it calls fmt again, which calls write!(f, \"{}\", self) again, and so on forever until the stack overflows.\n",
    "\n",
    "Huh, just copying from the docs didn't help... who'da thunk!\n",
    "\n",
    "Ok, so calling `write(f, \"{}\", self)` is causing a recursion because `Display` is getting called on `self` ad infinitum which, I believe, caused it to use all the memory and get aborted; my first Stack Overflow[^pw]. Instead of just trying to easily write the self, we need to *get the* `String` *out of the* `FileIoError` variants. \n",
    "\n",
    "[^pw]: I'm probably wrong about the explanation, call it a MidWit moment\n",
    "\n",
    "You thinkin' what I'm thinkin'? \n",
    "\n",
    "**Let's `match` this MFer**\n",
    "\n",
    "```{rust}\n",
    "#| label: impl-display-2\n",
    "// -- snip --\n",
    "impl fmt::Display for FileIoError {\n",
    "    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n",
    "        match self {\n",
    "            FileIoError::FileNotFound(s) => write!(f, \"{}\", s),\n",
    "            FileIoError::NoPermission(s) => write!(f, \"{}\", s),\n",
    "            FileIoError::FileExists(s) => write!(f, \"{}\", s),\n",
    "            FileIoError::Unknown(s) => write!(f, \"{}\", s),\n",
    "        }\n",
    "    }\n",
    "}\n",
    "// -- snip --\n",
    "```\n",
    "**Hold on to your butts**\n",
    "\n",
    "```{ansi}\n",
    "#| label: display-test-out-2\n",
    "// -- snip --\n",
    "running 2 tests\n",
    "test tests::test_display ... ok\n",
    "test tests::test_new ... ok\n",
    "\n",
    "test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n",
    "// -- snip --\n",
    "```\n",
    "There it is!! *La troisieme Boom!* as the French say. I'm going to call that...\n",
    "\n",
    "## Checkpoint 2 \n",
    "\n",
    "\n",
    " - [x] Implement the `Display Trait` and the other's that `Friend Computer` suggested. \n",
    "\n",
    "I'll admit I was absolutely winging it with `test_display` but as Napoleon once said \n",
    "\n",
    "> If yer gonna be dumb, ya gotta be tough[^nap].\n",
    "\n",
    "[^nap]: I know this isn't a Napoleon quote (don't @ me). The quote \"I would rather have a general who was lucky than one who was good\" is often attributed to him, but it's probably apocryphal. I was going to quote that here in reference to the fact that I stumbled into a working test, but then the Roger Miller quote was funnier. And nothing polishes off a joke like explaining it. \n",
    "\n",
    "So now it's time to write the `read_challenge_input` function.\n",
    "\n",
    "# Phase three - Reading input\n",
    "\n",
    "As per the rules we must (*must*) write a test before we go forward, I'm just going to batch out the three I want now. Oh, also, I've created two files in the same directory as our code:\n",
    "\n",
    " - an `./input.txt` file that just contains the text \"It works!\". \n",
    " - a `./notyoudont.txt` file that we don't have permission to read. \n",
    "\n",
    "## Three tests outside Enum Missouri\n",
    "\n",
    "```{rust}\n",
    "#| label: read-input-tests\n",
    "// -- snip --\n",
    "    #[test]\n",
    "    fn it_works() {\n",
    "        let result = read_challenge_input(\"./input.txt\");\n",
    "        assert_eq!(result.unwrap(), String::from(\"It works!\\n\"));\n",
    "    }\n",
    "    #[test]\n",
    "    fn returns_error_for_missing_file() {\n",
    "        let result = read_challenge_input(\"nonexistent.txt\");\n",
    "        assert!(matches!(result, Err(FileIoError::FileNotFound(_))));\n",
    "    }\n",
    "\n",
    "    #[test]\n",
    "    fn returns_error_for_no_permission() {\n",
    "        let result = read_challenge_input(\"./noyoudont.txt\");\n",
    "        assert!(matches!(result,Err(FileIoError::NoPermission(_))));\n",
    "    }\n",
    "```\n",
    "\n",
    "and now to write the function\n",
    "\n",
    "```{rust}\n",
    "#| label: read_input\n",
    "use std::fs::read_to_string;\n",
    "// -- snip --\n",
    "fn read_challenge_input(path:&str) -> Result<String, FileIoError> {\n",
    "    let input = read_to_string(path)?; // using the ? opporator to propagate the error back up\n",
    "    Ok(input)\n",
    "}\n",
    "// -- snip --\n",
    "```\n",
    "and run it back\n",
    "\n",
    "```{ansi}\n",
    "#| label: trait-missing\n",
    "// -- snip --\n",
    "error[E0277]: `?` couldn't convert the error to `FileIoError`\n",
    "  --> src/lib.rs:37:46\n",
    "   |\n",
    "36 | fn read_challenge_input(path:&str) -> Result<String, FileIoError> {\n",
    "   |                                       --------------------------- expected `FileIoError` because of this\n",
    "37 |     let input = read_to_string(\"./input.txt\")?;\n",
    "   |                 -----------------------------^ the trait `From<std::io::Error>` is not implemented for `FileIoError`\n",
    "   |                 |\n",
    "   |                 this can't be annotated with `?` because it has type `Result<_, std::io::Error>`\n",
    "   |\n",
    "note: `FileIoError` needs to implement `From<std::io::Error>`\n",
    "  --> src/lib.rs:6:1\n",
    "   |\n",
    " 6 | enum FileIoError{\n",
    "   | ^^^^^^^^^^^^^^^^\n",
    "   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait\n",
    "\n",
    "For more information about this error, try `rustc --explain E0277`.\n",
    "error: could not compile `aoc_common` (lib) due to 1 previous error\n",
    "warning: build failed, waiting for other jobs to finish...\n",
    "error: could not compile `aoc_common` (lib test) due to 1 previous error\n",
    "```\n",
    "Ladies, Gentlemen and pals beyond the binary, I thought I might get through this without one but we've reached a *say it with me* \n",
    "\n",
    "_**Rust Rookie Mistake!**_\n",
    "\n",
    "So, I thought I was being clever jumping straight to [propagating the Error](http://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator) with the `?` operator, and maybe if I'd not made this rookie error I would have been, but it can't work if the thing I want to use as `E` in `Result<T,E>` doesn't have the [`Error Trait`](https://doc.rust-lang.org/std/error/trait.Error.html).\n",
    "\n",
    "*Which I actually knew already!* See? Rookie move!. \n",
    "\n",
    "As well as adding the `Error Trait`, I also need to convert the `Error`s from that are produced by `read_to_string()` into `FileIoError`s before my test will pass. \n",
    "\n",
    "```{rust}\n",
    "#| label: read-input-err\n",
    "use std::error::Error;// bring Error into scope\n",
    "use std::fs::read_to_string;\n",
    "// -- snip --\n",
    "impl Error for FileIoError {}// The implementation is really simple\n",
    "// -- snip --\n",
    "fn read_challenge_input(path:&str) -> Result<String, FileIoError> {\n",
    "    let input = read_to_string(path).map_err(// map_err lets me convert errors from one kind to another\n",
    "        |e| match e.kind() {\n",
    "            ErrorKind::NotFound => FileIoError::FileNotFound(e.to_string()),\n",
    "            ErrorKind::PermissionDenied => FileIoError::NoPermission(e.to_string()),\n",
    "            ErrorKind::AlreadyExists => FileIoError::FileExists(e.to_string()),\n",
    "            _ => FileIoError::Unknown(e.to_string())\n",
    "        }\n",
    "        )?;// closures man! \n",
    "    Ok(input)\n",
    "}\n",
    "}\n",
    "// -- snip --\n",
    "```\n",
    "Ok, so we've wired up `Error` and plugged in the `map_err()` to handle conversion. Let's run it. \n",
    "\n",
    "\n",
    "```{ansi}\n",
    "#| label: final-tests\n",
    "// -- snip --\n",
    "\n",
    "running 5 tests\n",
    "test tests::returns_error_for_missing_file ... ok\n",
    "test tests::returns_error_for_no_permission ... ok\n",
    "test tests::test_display ... ok\n",
    "test tests::test_new ... ok\n",
    "test tests::it_works ... ok\n",
    "\n",
    "test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n",
    "```\n",
    "**Penultimate Boom!**\n",
    "\n",
    " - [x] Write my own version of `std::fs::read_to_string` that use my `FileIoError`\n",
    "\n",
    "There we have it, five passing tests, covering my own `enum` and file reading wrapper `function`. Pretty chuffed with that. There's one thing left to do. \n",
    "\n",
    "# Phase four - Plumbing\n",
    "\n",
    "The eagled-eyed among you will have noticed that as it stands nothing in this module is accessible to outside code. Everything in a module is `Private` by default, for...reasons. However, if we want to be able to use our code in other modules we need to mark them as `pub`; you'll see that in the [final implementation](#final-implementation) down below.\n",
    "\n",
    "In the meantime there are a few other bits of plumbing that needs doing in the various `Cargo.toml` files in the project. \n",
    "\n",
    "## Workspaces\n",
    "\n",
    "Rust gives us a way to organise sub-directories together into a cohesive crate: `workspaces`. In essence we just need to tell the system what to include in our crate, which sub-directories to group together. Thankfully, this is really easy to organise, we just need to put a `Cargo.toml` in the overall root folder and fill it with the following text:\n",
    "\n",
    "```{toml}\n",
    "#| label: workspace-toml\n",
    "// -- snip --\n",
    "[workspace]\n",
    "members = [\n",
    "  \"aoc_common\",\n",
    "  \"day_1\",\n",
    "  ]\n",
    "```\n",
    "## Dependencies\n",
    "\n",
    "Once that's done we need to add our module as a dependency in any others we want to use it in. Where would use `Cargo add \\<crate\\>` to add remote dependencies, in this case we need to manually add it to all the `sub-dir/Cargo.toml`, specifying the path like so\n",
    "\n",
    "```{toml}\n",
    "#| label: day-1-toml\n",
    "// -- snip --\n",
    "[package]\n",
    "name = \"day_1\"\n",
    "version = \"0.1.0\"\n",
    "edition = \"2024\"\n",
    "\n",
    "[dependencies]\n",
    "aoc_common = {path = \"../aoc_common/\"}\n",
    "```\n",
    "Viola[^bugs]! We have a fully functioning module that we can pull into anything else in our AOC crate and handle reading in input like a boss. \n",
    "\n",
    "[^bugs]: In a Bugs Bunny voice\n",
    "\n",
    "\n",
    "**Ultimate Boom!**\n",
    "\n",
    "# Full Implementation {#full-implementation}\n",
    "\n",
    "As promised, here's the full contents \n",
    "\n",
    "```{rust}\n",
    "#| label: lib-rs\n",
    "use std::error::Error;\n",
    "use std::fs::read_to_string;\n",
    "use std::io::ErrorKind; \n",
    "use std::fmt;\n",
    "\n",
    "#[derive(Debug, PartialEq)]\n",
    "pub enum FileIoError{\n",
    "    FileNotFound(String),\n",
    "    NoPermission(String), \n",
    "    FileExists(String),\n",
    "    Unknown(String),\n",
    "}\n",
    "\n",
    "impl FileIoError {\n",
    "    pub fn new(e: ErrorKind) -> FileIoError {\n",
    "        match e {\n",
    "            ErrorKind::NotFound => FileIoError::FileNotFound(format!(\"No file at path specified {}\", e).to_string()), \n",
    "            ErrorKind::PermissionDenied => FileIoError::NoPermission(format!(\"Permission Denied {}\", e).to_string()), \n",
    "            ErrorKind::AlreadyExists => FileIoError::FileExists(format!(\"There is already a file at the path specified {}\", e).to_string()), \n",
    "            _ => FileIoError::Unknown(format!(\"{}\", e).to_string()),\n",
    "        }\n",
    "    }\n",
    "}\n",
    "\n",
    "impl fmt::Display for FileIoError {\n",
    "    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n",
    "        match self {\n",
    "            FileIoError::FileNotFound(s) => write!(f, \"{}\", s),\n",
    "            FileIoError::NoPermission(s) => write!(f, \"{}\", s),\n",
    "            FileIoError::FileExists(s) => write!(f, \"{}\", s),\n",
    "            FileIoError::Unknown(s) => write!(f, \"{}\", s),\n",
    "        }\n",
    "    }\n",
    "\n",
    "}\n",
    "\n",
    "impl Error for FileIoError {}\n",
    "\n",
    "pub fn read_challenge_input(path:&str) -> Result<String, FileIoError> {\n",
    "    let input = read_to_string(path).map_err(\n",
    "        |e| match e.kind() {\n",
    "            ErrorKind::NotFound => FileIoError::FileNotFound(e.to_string()),\n",
    "            ErrorKind::PermissionDenied => FileIoError::NoPermission(e.to_string()),\n",
    "            ErrorKind::AlreadyExists => FileIoError::FileExists(e.to_string()),\n",
    "            _ => FileIoError::Unknown(e.to_string())\n",
    "        }\n",
    "        )?;\n",
    "    Ok(input)\n",
    "}\n",
    "\n",
    "#[cfg(test)]\n",
    "mod tests {\n",
    "    use super::*;\n",
    "\n",
    "    #[test]\n",
    "    fn test_new() {\n",
    "        let test_error = FileIoError::new(ErrorKind::NotFound);\n",
    "        assert_eq!(\n",
    "            test_error,\n",
    "            FileIoError::FileNotFound(\n",
    "                    format!(\"No file at path specified {}\", ErrorKind::NotFound).to_string()\n",
    "                )\n",
    "            );\n",
    "    }\n",
    "\n",
    "    #[test]\n",
    "    fn test_display() {\n",
    "        let test_error = FileIoError::new(ErrorKind::NotFound);\n",
    "        assert_eq!(\n",
    "            format!(\"{}\", test_error),\n",
    "            format!(\"{}\", FileIoError::FileNotFound(\n",
    "                    format!(\"No file at path specified {}\", ErrorKind::NotFound).to_string()\n",
    "                )\n",
    "            )\n",
    "\n",
    "        )\n",
    "    }\n",
    "\n",
    "    #[test]\n",
    "    fn it_works() {\n",
    "        let result = read_challenge_input(\"./input.txt\");\n",
    "        assert_eq!(result.unwrap(), String::from(\"It works!\\n\"));\n",
    "    }\n",
    "    #[test]\n",
    "    fn returns_error_for_missing_file() {\n",
    "        let result = read_challenge_input(\"nonexistent.txt\");\n",
    "        assert!(matches!(result, Err(FileIoError::FileNotFound(_))));\n",
    "    }\n",
    "\n",
    "    #[test]\n",
    "    fn returns_error_for_no_permission() {\n",
    "        let result = read_challenge_input(\"./noyoudont.txt\");\n",
    "        assert!(matches!(result,Err(FileIoError::NoPermission(_))));\n",
    "    }\n",
    "\n",
    "}\n",
    "```\n",
    "\n",
    "# What do I need to take from this? \n",
    "\n",
    " - `Friend Computer` is really helpful. This exercise really drove this home for me. \n",
    " - Recursion is something to watch out for, it's not easy for untrained eyes to spot where the overflow might be coming from and I couldn't have parsed that without Claude. It sucks to admit it but it's just true. \n",
    " - Implementing `Error` is really easy, but it has to be there to give access to the power of `Result<T, E>`\n",
    " - TDD is fine, and I can see why it would be really useful to well-heeled devs.\n",
    "   - Honestly I got lucky with at least one of the tests. Maybe, in the case where the problem was small, or where I was already pretty sure of the expected output then it would have felt better. \n",
    "   - But maybe that's how TDD is meant to feel and actually the compiler feed back from the tests is an example of the power of TDD. I ain't experienced enough to make that call from here. \n",
    "\n",
    "All in all though I do feel like I got where I was aiming with this, and I did learn a lot. I think on `day_2` I'll try to write up some `Structs` and `functions` that are relevant to the puzzle from the outset. Although I'm not committed to TDD there too. Who knows. \n",
    "\n",
    "If you've read this far, I appreciate your time and I hope it was at least a little entertaining, even if the sentiment was mainly Schadenfreude. \n",
    "\n",
    "God's Speed\n",
    "\n",
    "_**The MidWit**_"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3",
   "path": "/home/runner/.local/share/jupyter/kernels/python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
