Skip to content

Commit 5a4ffda

Browse files
authored
Rust: Match lex return type with other signatures (#783)
This pull request refactors the `lex` return type in the public Rust API. Additionally, the `LexResult` and `ParseResult` types now implement `Display` for easier printing. ```rust use herb::{lex, parse}; fn main() { let template = "<h1><%= title %></h1>"; match lex(template) { Ok(result) => { println!("{}", result); } Err(error) => { eprintln!("Lex error: {}", error); } } match parse(template) { Ok(result) => { println!("{}", result); } Err(error) => { eprintln!("Parse error: {}", error); } } } ```
1 parent 4066284 commit 5a4ffda

File tree

14 files changed

+124
-61
lines changed

14 files changed

+124
-61
lines changed

docs/docs/bindings/rust/index.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,18 @@ use herb::lex;
7575

7676
fn main() {
7777
let source = "<h1><%= user.name %></h1>";
78-
let result = lex(source);
7978

80-
for token in result.tokens() {
81-
println!("{}", token.inspect());
79+
match lex(source) {
80+
Ok(result) => {
81+
println!("{}", result);
82+
83+
for token in result.tokens() {
84+
// do something with each token
85+
}
86+
}
87+
Err(e) => {
88+
eprintln!("Lex error: {}", e);
89+
}
8290
}
8391
}
8492
```

docs/docs/bindings/rust/reference.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,27 @@ The `herb` crate exposes functions for lexing, parsing, and extracting Ruby and
2020

2121
## Lexing
2222

23-
The `herb::lex` function tokenizes an HTML document with embedded Ruby and returns a `LexResult` containing all tokens.
23+
The `herb::lex` function tokenizes an HTML document with embedded Ruby and returns a `Result<LexResult, String>` containing all tokens.
2424

25-
### `herb::lex(source: &str) -> LexResult`
25+
### `herb::lex(source: &str) -> Result<LexResult, String>`
2626

2727
:::code-group
2828
```rust
2929
use herb::lex;
3030

3131
let source = "<p>Hello <%= user.name %></p>";
32-
let result = lex(source);
3332

34-
for token in result.tokens() {
35-
println!("{}", token.inspect());
33+
match lex(source) {
34+
Ok(result) => {
35+
println!("{}", result);
36+
37+
for token in result.tokens() {
38+
// do something with each token
39+
}
40+
}
41+
Err(e) => {
42+
eprintln!("Lex error: {}", e);
43+
}
3644
}
3745
// Output:
3846
// #<Herb::Token type="TOKEN_HTML_TAG_START" value="<" range=[0, 1] start=(1:0) end=(1:1)>

rust/README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,19 @@ make all # Generate templates and build
3232
### As a Library
3333

3434
```rust
35-
use herb::{lex, Token};
35+
use herb::{lex, parse};
3636

3737
fn main() {
38-
let source = r#"<div><%= name %></div>"#;
39-
let result = lex(source);
38+
let template = "<h1><%= title %></h1>";
4039

41-
for token in result.tokens() {
42-
println!("{}", token.inspect());
40+
match lex(template) {
41+
Ok(result) => { println!("{}", result); }
42+
Err(error) => { eprintln!("Lex error: {}", error); }
43+
}
44+
45+
match parse(template) {
46+
Ok(result) => { println!("{}", result); }
47+
Err(error) => { eprintln!("Parse error: {}", error); }
4348
}
4449
}
4550
```

rust/src/herb.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ use crate::convert::token_from_c;
33
use crate::{LexResult, ParseResult};
44
use std::ffi::CString;
55

6-
pub fn lex(source: &str) -> LexResult {
6+
pub fn lex(source: &str) -> Result<LexResult, String> {
77
unsafe {
8-
let c_source = CString::new(source).expect("Failed to create CString");
8+
let c_source = CString::new(source).map_err(|e| e.to_string())?;
99
let c_tokens = crate::ffi::herb_lex(c_source.as_ptr());
1010

1111
if c_tokens.is_null() {
12-
return LexResult::new(Vec::new());
12+
return Err("Failed to lex source".to_string());
1313
}
1414

1515
let array_size = crate::ffi::hb_array_size(c_tokens);
@@ -26,7 +26,7 @@ pub fn lex(source: &str) -> LexResult {
2626
let mut c_tokens_ptr = c_tokens;
2727
crate::ffi::herb_free_tokens(&mut c_tokens_ptr as *mut *mut hb_array_T);
2828

29-
LexResult::new(tokens)
29+
Ok(LexResult::new(tokens))
3030
}
3131
}
3232

rust/src/lex_result.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::Token;
2+
use std::fmt;
23

34
pub struct LexResult {
45
pub tokens: Vec<Token>,
@@ -12,4 +13,19 @@ impl LexResult {
1213
pub fn tokens(&self) -> &[Token] {
1314
&self.tokens
1415
}
16+
17+
pub fn inspect(&self) -> String {
18+
self
19+
.tokens
20+
.iter()
21+
.map(|token| token.inspect())
22+
.collect::<Vec<_>>()
23+
.join("\n")
24+
}
25+
}
26+
27+
impl fmt::Display for LexResult {
28+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29+
write!(f, "{}", self.inspect())
30+
}
1531
}

rust/src/lib.rs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,35 +23,3 @@ pub use range::Range;
2323
pub use token::Token;
2424

2525
pub const VERSION: &str = "0.7.5";
26-
27-
#[cfg(test)]
28-
mod tests {
29-
use super::*;
30-
use crate::nodes::{DocumentNode, HTMLTextNode};
31-
32-
#[test]
33-
fn test_tree_inspect() {
34-
let loc = Location::new(Position::new(1, 0), Position::new(1, 5));
35-
36-
let text_node = HTMLTextNode {
37-
node_type: "HTMLTextNode".to_string(),
38-
location: loc,
39-
errors: vec![],
40-
content: "Hello".to_string(),
41-
};
42-
43-
let doc_node = DocumentNode {
44-
node_type: "DocumentNode".to_string(),
45-
location: Location::new(Position::new(1, 0), Position::new(2, 0)),
46-
errors: vec![],
47-
children: vec![AnyNode::HTMLTextNode(text_node)],
48-
};
49-
50-
let output = doc_node.tree_inspect();
51-
52-
assert!(output.contains("@ DocumentNode"));
53-
assert!(output.contains("children: (1 item)"));
54-
assert!(output.contains("@ HTMLTextNode"));
55-
assert!(output.contains("Hello"));
56-
}
57-
}

rust/src/main.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,14 @@ fn lex_command(file_path: &str) {
6565
}
6666
};
6767

68-
let result = herb::lex(&source);
69-
for token in result.tokens() {
70-
println!("{}", token.inspect());
68+
match herb::lex(&source) {
69+
Ok(result) => {
70+
println!("{}", result);
71+
}
72+
Err(e) => {
73+
eprintln!("Lex error: {}", e);
74+
std::process::exit(1);
75+
}
7176
}
7277
}
7378

@@ -82,7 +87,7 @@ fn parse_command(file_path: &str) {
8287

8388
match herb::parse(&source) {
8489
Ok(result) => {
85-
println!("{}", result.tree_inspect());
90+
println!("{}", result.inspect());
8691
}
8792
Err(e) => {
8893
eprintln!("Parse error: {}", e);

rust/src/parse_result.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::errors::{AnyError, ErrorNode};
22
use crate::nodes::{DocumentNode, Node};
3+
use std::fmt;
34

45
pub struct ParseResult {
56
pub value: DocumentNode,
@@ -16,7 +17,7 @@ impl ParseResult {
1617
}
1718
}
1819

19-
pub fn tree_inspect(&self) -> String {
20+
pub fn inspect(&self) -> String {
2021
self.value.tree_inspect()
2122
}
2223

@@ -39,3 +40,9 @@ impl ParseResult {
3940
self.recursive_errors().is_empty()
4041
}
4142
}
43+
44+
impl fmt::Display for ParseResult {
45+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46+
write!(f, "{}", self.inspect())
47+
}
48+
}

rust/tests/common/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Common test utilities
2+
3+
pub fn no_color() {
4+
colored::control::set_override(false);
5+
}

rust/tests/error_handling_test.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
mod common;
2+
13
use herb::parse;
24

35
#[test]
46
fn test_unclosed_element_error() {
7+
common::no_color();
8+
59
let source = "<div class=\"test\">";
610
let result = parse(source).unwrap();
711

8-
let tree_inspect = result.tree_inspect();
12+
let tree_inspect = result.inspect();
913
assert!(tree_inspect.contains("UnclosedElementError"));
1014
assert!(tree_inspect.contains("Tag `<div>` opened at (1:1) was never closed"));
1115
assert!(tree_inspect.contains("MissingClosingTagError"));
@@ -14,20 +18,24 @@ fn test_unclosed_element_error() {
1418

1519
#[test]
1620
fn test_tag_names_mismatch_error() {
21+
common::no_color();
22+
1723
let source = "<div></span>";
1824
let result = parse(source).unwrap();
1925

20-
let tree_inspect = result.tree_inspect();
26+
let tree_inspect = result.inspect();
2127
assert!(tree_inspect.contains("TagNamesMismatchError"));
2228
assert!(tree_inspect.contains("Opening tag `<div>` at (1:1) closed with `</span>`"));
2329
}
2430

2531
#[test]
2632
fn test_no_errors_with_valid_html() {
33+
common::no_color();
34+
2735
let source = "<div>Hello</div>";
2836
let result = parse(source).unwrap();
2937

30-
let tree_inspect = result.tree_inspect();
38+
let tree_inspect = result.inspect();
3139
assert!(!tree_inspect.contains("error"));
3240
assert!(!tree_inspect.contains("ERROR"));
3341
}

0 commit comments

Comments
 (0)