Compare commits
3 Commits
1aa6af3b1a
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d4f611de1 | |||
| 1c7504e3e0 | |||
| 7d602fffba |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ site/
|
||||
target/
|
||||
|
||||
result
|
||||
tarpaulin-report.html
|
||||
|
||||
@@ -16,15 +16,10 @@ pub enum Block {
|
||||
language: Option<String>,
|
||||
content: String,
|
||||
},
|
||||
List(Vec<ListItem>),
|
||||
List(Vec<Block>),
|
||||
Quote(Vec<Block>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ListItem {
|
||||
pub blocks: Vec<Block>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Inline {
|
||||
Text(String),
|
||||
|
||||
@@ -133,6 +133,20 @@ mod test {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_block_content_after_end() {
|
||||
let md = "```\necho hello\n```abc";
|
||||
let doc_res = parse(md);
|
||||
assert!(doc_res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_block_no_terminating() {
|
||||
let md = "```\nabc\n";
|
||||
let doc_res = parse(md);
|
||||
assert!(doc_res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rust_code_block() {
|
||||
let md = "```rust\nfn main() {\n\tprintln!(\"Hello world!\");\n}\n```";
|
||||
|
||||
@@ -48,6 +48,11 @@ pub fn parse_blocks(input: &str) -> Result<Vec<Block>, MdParseError> {
|
||||
}
|
||||
*/
|
||||
|
||||
// unordered list TODO
|
||||
if line_chars.parse_str("- ") {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// code
|
||||
if line_chars.parse_str("```") {
|
||||
let lang_line: String = line_chars.collect();
|
||||
|
||||
@@ -36,12 +36,18 @@ pub fn parse_inlines(input: &str) -> Result<Vec<Inline>, MdParseError> {
|
||||
_ => {
|
||||
let mut text = String::new();
|
||||
text.push(c);
|
||||
let mut escaped = false;
|
||||
while let Some(&nc) = chars.peek() {
|
||||
if matches!(nc, '*' | '_' | '`' | '[') {
|
||||
if matches!(nc, '*' | '_' | '`' | '[') && !escaped {
|
||||
break;
|
||||
}
|
||||
let c = chars.next().ok_or(MdParseError::new("a character", ""))?;
|
||||
text.push(c);
|
||||
let next_c = chars.next().ok_or(MdParseError::new("a character", ""))?;
|
||||
if next_c == '\\' && !escaped {
|
||||
escaped = true;
|
||||
} else {
|
||||
escaped = false;
|
||||
text.push(next_c);
|
||||
}
|
||||
}
|
||||
inlines.push(Inline::Text(text));
|
||||
}
|
||||
@@ -69,7 +75,14 @@ fn collect_until<I: Iterator<Item = char>>(
|
||||
mod test {
|
||||
use crate::ast::Inline;
|
||||
|
||||
use super::parse_inlines;
|
||||
use super::{collect_until, parse_inlines};
|
||||
|
||||
#[test]
|
||||
fn collect_until_without_end() {
|
||||
let mut s = "abcdef".chars().peekable();
|
||||
let res = collect_until(&mut s, '.');
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bold_text() {
|
||||
@@ -128,4 +141,43 @@ mod test {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_hyperlink() {
|
||||
let md = "a link to [my site](https://example.com)";
|
||||
let inl = parse_inlines(md).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
inl,
|
||||
vec![
|
||||
Inline::Text("a link to ".to_string()),
|
||||
Inline::Link {
|
||||
text: vec![Inline::Text("my site".to_string())],
|
||||
href: "https://example.com".to_string()
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hyperlink_without_link() {
|
||||
let md = "[abc]";
|
||||
let inl = parse_inlines(md);
|
||||
|
||||
assert!(inl.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_brackets() {
|
||||
let md = r"some \[text\]";
|
||||
let inl = parse_inlines(md).unwrap();
|
||||
assert_eq!(inl, vec![Inline::Text("some [text]".to_string())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escape_escape() {
|
||||
let md = r"backslash \\";
|
||||
let inl = parse_inlines(md).unwrap();
|
||||
assert_eq!(inl, vec![Inline::Text(r"backslash \".to_string())]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
cargo-nextest
|
||||
cargo-expand
|
||||
cargo-watch
|
||||
cargo-tarpaulin
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,14 +10,17 @@ use std::net::Ipv4Addr;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub enum Command {
|
||||
Generate { force: bool },
|
||||
Generate { force: bool, single: bool },
|
||||
Serve { addr: Ipv4Addr, port: u16 },
|
||||
Init,
|
||||
}
|
||||
|
||||
impl Default for Command {
|
||||
fn default() -> Self {
|
||||
Self::Generate { force: true }
|
||||
Self::Generate {
|
||||
force: true,
|
||||
single: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,10 +70,18 @@ impl TryFrom<Args> for Command {
|
||||
comm = Command::Init;
|
||||
}
|
||||
// `gravel` command
|
||||
else if let Some(a) = value.next() {
|
||||
Err(Error::CommandLineArgsParse(format!(
|
||||
"Unexpected argument: `{a}`"
|
||||
)))?;
|
||||
for a in value {
|
||||
match a.as_str() {
|
||||
"-s" => {
|
||||
comm = Command::Generate {
|
||||
force: true,
|
||||
single: true,
|
||||
}
|
||||
}
|
||||
_ => Err(Error::CommandLineArgsParse(format!(
|
||||
"Unknown argument: `{a}`"
|
||||
)))?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(comm)
|
||||
|
||||
@@ -15,9 +15,16 @@ fn run() -> Result<(), Error> {
|
||||
let conf = ProgramConfig::new("gravel.toml", std::env::args())?;
|
||||
|
||||
match conf.command {
|
||||
Command::Init => todo!("project init not implemented"),
|
||||
Command::Init => todo!("project init"),
|
||||
Command::Serve { addr, port } => serve(addr, port, conf.outdir)?,
|
||||
Command::Generate { force } => generate(&conf.indir, &conf.outdir, force)?,
|
||||
Command::Generate {
|
||||
force,
|
||||
single: false,
|
||||
} => generate(&conf.indir, &conf.outdir, force)?,
|
||||
Command::Generate {
|
||||
force: _f,
|
||||
single: true,
|
||||
} => todo!("single file generation"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user