commit 05821b77a49ee7af3a7c3c44a1737a3f49ffdfec Author: cynthia <7065188-licynthiax@users.noreply.gitlab.com> Date: Mon Apr 11 02:13:46 2022 -0700 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..51e1bf2 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "details" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4139128 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "details" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..3be1676 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,150 @@ +#![allow(dead_code)] + +use std::collections::BTreeMap; +use std::io::{Error as IoError, Write}; +use std::{env::args, fs}; + +fn main() { + let filename = args().nth(1).expect("no file given"); + let detail = Detail::new(&filename).unwrap(); + let out = filename + .strip_suffix(".detail") + .unwrap_or(&filename) + .to_string() + + ".html"; + + let mut file = + std::fs::File::create(out.clone()).unwrap_or_else(|_| panic!("couldn't create {}", out)); + + file.write_all(detail.into_html().unwrap().as_bytes()) + .unwrap_or_else(|_| panic!("unable to write to {}", out)); +} + +// ~~~ data structures ~~~ +struct Detail { + passages: BTreeMap, + node_graph: Vec>, +} + +impl Detail { + fn new(filename: &str) -> Result { + let contents = fs::read_to_string(filename).map_err(|err| DetailError::Io(err))?; + + let mut passages = BTreeMap::new(); + let mut cur_passage: Option = None; + let mut cur_lines = String::new(); + + for line in contents.lines() { + let line = line.trim(); + + // if line begins with ~detail + if let Some(passage_name) = line.strip_prefix("~detail ") { + // if this isn't the first passage, add previous passage to map + if let Some(prev_passage) = cur_passage { + let prev_passage = prev_passage.to_string(); + if !cur_lines.is_empty() { + if passages.contains_key(&prev_passage) { + println!("WARNING: passage name \"{}\" duplicated", prev_passage); + passages.remove(&prev_passage); + } else { + // clear cur_lines and insert + passages.insert(prev_passage, std::mem::take(&mut cur_lines)); + } + } + } + + cur_passage = Some(passage_name.to_string()); + } else { + // case: regular line + cur_lines += line; + } + } + + // add last passage + if let Some(prev_passage) = cur_passage { + let prev_passage = prev_passage.to_string(); + if !cur_lines.is_empty() { + if passages.contains_key(&prev_passage) { + println!("WARNING: passage name \"{}\" duplicated", prev_passage); + passages.remove(&prev_passage); + } else { + // clear cur_lines and insert + passages.insert(prev_passage, std::mem::take(&mut cur_lines)); + } + } + } + + let mut node_graph = Vec::new(); + node_graph.resize_with(passages.len(), || { + let mut row = Vec::new(); + row.resize(passages.len(), false); + row + }); + + let mut detail = Self { + passages, + node_graph, + }; + + detail.build_graph(); + + if detail.check_valid() { + Ok(detail) + } else { + Err(DetailError::InfiniteLoop) + } + } + + fn into_html(self) -> Option { + self.flatten(&"start") + } + + fn flatten(&self, rule: &str) -> Option { + let rule = self.passages.get(rule)?; + let mut output = String::new(); + for (sep, piece) in rule.split('#').enumerate() { + // search keys + if sep % 2 == 0 { + output += piece; + } else { + let (display, detail_to) = piece.split_once('~').unwrap_or((piece, piece)); + let expand = self.flatten(&detail_to)?; + output += "
"; + output += display; + output += ""; + output += &expand; + output += "
"; + } + } + Some(output) + } + + // don't speak to me about how nested this method is. + fn build_graph(&mut self) { + for (i, content) in self.passages.values().enumerate() { + for (sep, piece) in content.split('#').enumerate() { + // search keys + if sep % 2 == 1 { + if let Some((_, detail_to)) = piece.split_once('~') { + if let Some(j) = self.passages.keys().position(|name| name == &detail_to) { + self.node_graph[i][j] = true; + } + } else if let Some(j) = self.passages.keys().position(|name| name == &piece) { + self.node_graph[i][j] = true; + } + } + } + } + } + + // TODO actually write this + fn check_valid(&self) -> bool { + true + } +} + +#[derive(Debug)] +enum DetailError { + Io(IoError), + InfiniteLoop, +}