cynthia
2 years ago
commit
05821b77a4
4 changed files with 166 additions and 0 deletions
-
1.gitignore
-
7Cargo.lock
-
8Cargo.toml
-
150src/main.rs
@ -0,0 +1 @@ |
|||
/target |
@ -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" |
@ -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] |
@ -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<String, String>,
|
|||
node_graph: Vec<Vec<bool>>,
|
|||
}
|
|||
|
|||
impl Detail {
|
|||
fn new(filename: &str) -> Result<Self, DetailError> {
|
|||
let contents = fs::read_to_string(filename).map_err(|err| DetailError::Io(err))?;
|
|||
|
|||
let mut passages = BTreeMap::new();
|
|||
let mut cur_passage: Option<String> = 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<String> {
|
|||
self.flatten(&"start")
|
|||
}
|
|||
|
|||
fn flatten(&self, rule: &str) -> Option<String> {
|
|||
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 += "<details><summary>";
|
|||
output += display;
|
|||
output += "</summary>";
|
|||
output += &expand;
|
|||
output += "</details>";
|
|||
}
|
|||
}
|
|||
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,
|
|||
}
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue