use cmark_writer::ast::HeadingType;
use cmark_writer::coded_error;
use cmark_writer::structure_error;
use cmark_writer::CommonMarkWriter;
use cmark_writer::Node;
use cmark_writer::WriteError;
use cmark_writer::WriteResult;
use cmark_writer::WriterOptions;
use std::fmt::{self, Display};
#[test]
fn test_invalid_heading_level() {
let mut writer = CommonMarkWriter::new();
let invalid_heading_0 = Node::Heading {
level: 0,
content: vec![Node::Text("Invalid Heading".to_string())],
heading_type: HeadingType::Atx,
};
let result = writer.write(&invalid_heading_0);
assert!(result.is_err());
if let Err(WriteError::InvalidHeadingLevel(level)) = result {
assert_eq!(level, 0);
} else {
panic!("Expected InvalidHeadingLevel error");
}
let mut writer = CommonMarkWriter::new();
let invalid_heading_7 = Node::Heading {
level: 7,
content: vec![Node::Text("Invalid Heading".to_string())],
heading_type: HeadingType::Atx,
};
let result = writer.write(&invalid_heading_7);
assert!(result.is_err());
if let Err(WriteError::InvalidHeadingLevel(level)) = result {
assert_eq!(level, 7);
} else {
panic!("Expected InvalidHeadingLevel error");
}
let mut writer = CommonMarkWriter::new();
let valid_heading = Node::Heading {
level: 6,
content: vec![Node::Text("Valid Heading".to_string())],
heading_type: HeadingType::Atx,
};
assert!(writer.write(&valid_heading).is_ok());
}
#[test]
fn test_newline_in_inline_element() {
let mut writer = CommonMarkWriter::new();
let text_with_newline = Node::Text("Line 1\nLine 2".to_string());
let result = writer.write(&text_with_newline);
assert!(result.is_err());
match result {
Err(WriteError::NewlineInInlineElement(context)) => {
assert_eq!(context, "Text");
}
_ => panic!("Expected NewlineInInlineElement error"),
}
let mut writer = CommonMarkWriter::new();
let emphasis_with_newline = Node::Emphasis(vec![Node::Text("Line 1\nLine 2".to_string())]);
let result = writer.write(&emphasis_with_newline);
assert!(result.is_err());
let mut writer = CommonMarkWriter::new();
let strong_with_newline = Node::Strong(vec![Node::Text("Line 1\nLine 2".to_string())]);
let result = writer.write(&strong_with_newline);
assert!(result.is_err());
let mut writer = CommonMarkWriter::new();
let code_with_newline = Node::InlineCode("Line 1\nLine 2".to_string());
let result = writer.write(&code_with_newline);
assert!(result.is_err());
}
#[test]
fn test_unsupported_node_type() {
struct MockUnsupportedNode;
impl Display for MockUnsupportedNode {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
Ok(())
}
}
let _mock_node = MockUnsupportedNode;
let err = WriteError::UnsupportedNodeType;
let error_message = format!("{}", err);
assert_eq!(
error_message,
"Unsupported node type encountered during writing."
);
}
#[test]
fn test_fmt_error_conversion() {
let fmt_error = fmt::Error;
let write_error = WriteError::from(fmt_error);
match write_error {
WriteError::FmtError(_) => (), _ => panic!("Expected FmtError variant"),
}
}
#[test]
fn test_error_display() {
let errors = vec![
WriteError::InvalidHeadingLevel(0),
WriteError::NewlineInInlineElement("Text".to_string()),
WriteError::FmtError("test error".to_string()),
WriteError::UnsupportedNodeType,
];
for err in errors {
let display_str = format!("{}", err);
assert!(
!display_str.is_empty(),
"Error should have non-empty display: {:?}",
err
);
}
}
#[test]
fn test_error_debug() {
let errors = vec![
WriteError::InvalidHeadingLevel(0),
WriteError::NewlineInInlineElement("Text".to_string()),
WriteError::FmtError("test error".to_string()),
WriteError::UnsupportedNodeType,
];
for err in errors {
let debug_str = format!("{:?}", err);
assert!(!debug_str.is_empty());
}
}
#[test]
fn test_write_result_alias() {
let ok_result: WriteResult<()> = Ok(());
assert!(ok_result.is_ok());
let err_result: WriteResult<()> = Err(WriteError::UnsupportedNodeType);
assert!(err_result.is_err());
}
#[test]
fn test_custom_errors() {
use cmark_writer::error::WriteError;
use std::error::Error;
let custom_err = WriteError::custom("这是一个自定义错误");
assert_eq!(custom_err.to_string(), "Custom error: 这是一个自定义错误");
let coded_err =
WriteError::custom_with_code("表格行单元格数与表头数不匹配", "TABLE_STRUCTURE_ERROR");
assert_eq!(
coded_err.to_string(),
"Custom error [TABLE_STRUCTURE_ERROR]: 表格行单元格数与表头数不匹配"
);
let structure_err = WriteError::InvalidStructure("表格结构无效".to_string());
assert_eq!(structure_err.to_string(), "Invalid structure: 表格结构无效");
fn takes_error(_: &dyn Error) {}
takes_error(&custom_err);
takes_error(&coded_err);
takes_error(&structure_err);
}
#[test]
fn test_custom_error_attribute() {
#[structure_error(format = "表格列数不匹配:{}")]
struct TableColumnMismatchError(pub &'static str);
#[structure_error(format = "表格空表头:{}")]
struct TableEmptyHeaderError(pub &'static str);
#[structure_error(format = "文档格式错误:{}")]
struct DocumentFormatError(pub &'static str);
#[coded_error]
struct MarkdownSyntaxError(pub String, pub String);
let err1 = TableColumnMismatchError("第 3 行有 4 列,但表头只有 3 列").into_error();
assert_eq!(
err1.to_string(),
"Invalid structure: 表格列数不匹配:第 3 行有 4 列,但表头只有 3 列"
);
let err2 = TableEmptyHeaderError("表格必须包含至少一个表头").into_error();
assert_eq!(
err2.to_string(),
"Invalid structure: 表格空表头:表格必须包含至少一个表头"
);
let err3 = MarkdownSyntaxError(
"缺少闭合代码块标记".to_string(),
"CODE_BLOCK_UNCLOSED".to_string(),
)
.into_error();
assert_eq!(
err3.to_string(),
"Custom error [CODE_BLOCK_UNCLOSED]: 缺少闭合代码块标记"
);
let err4 = DocumentFormatError("文档超过最大嵌套深度").into_error();
assert_eq!(
err4.to_string(),
"Invalid structure: 文档格式错误:文档超过最大嵌套深度"
);
let err5: WriteError = TableColumnMismatchError("错误示例").into();
assert!(matches!(err5, WriteError::InvalidStructure(_)));
}
#[test]
fn test_mixed_order_custom_errors() {
#[coded_error]
struct ValidationError(pub String, pub String);
#[structure_error(format = "解析错误:{}")]
struct ParseError(pub &'static str);
#[coded_error]
struct FormatError(pub String, pub String);
#[structure_error(format = "渲染错误:{}")]
struct RenderError(pub &'static str);
let err1 = ValidationError(
"数据验证失败".to_string(),
"DATA_VALIDATION_FAILED".to_string(),
)
.into_error();
assert_eq!(
err1.to_string(),
"Custom error [DATA_VALIDATION_FAILED]: 数据验证失败"
);
let err2 = ParseError("无法解析 Markdown").into_error();
assert_eq!(
err2.to_string(),
"Invalid structure: 解析错误:无法解析 Markdown"
);
let err3 = FormatError("格式化失败".to_string(), "FORMAT_FAILED".to_string()).into_error();
assert_eq!(err3.to_string(), "Custom error [FORMAT_FAILED]: 格式化失败");
let err4 = RenderError("无法渲染表格").into_error();
assert_eq!(
err4.to_string(),
"Invalid structure: 渲染错误:无法渲染表格"
);
}
fn init_logger() {
let _ = env_logger::builder().is_test(true).try_init();
}
#[test]
fn test_invalid_heading_level_strict() {
init_logger();
let options = WriterOptions {
strict: true,
..Default::default()
};
let mut writer = CommonMarkWriter::with_options(options);
let node = Node::Heading {
level: 0, content: vec![Node::Text("Test".to_string())],
heading_type: HeadingType::Atx,
};
match writer.write(&node) {
Err(WriteError::InvalidHeadingLevel(level)) => assert_eq!(level, 0),
_ => panic!("Expected InvalidHeadingLevel error"),
}
}
#[test]
fn test_invalid_heading_level_non_strict() {
init_logger();
let options = WriterOptions {
strict: false,
..Default::default()
};
let mut writer = CommonMarkWriter::with_options(options);
let node = Node::Heading {
level: 0, content: vec![Node::Text("Test".to_string())],
heading_type: HeadingType::Atx,
};
assert!(writer.write(&node).is_ok());
assert_eq!(writer.into_string(), "# Test\n");
}
#[test]
fn test_invalid_heading_level_7_non_strict() {
init_logger();
let options = WriterOptions {
strict: false,
..Default::default()
};
let mut writer = CommonMarkWriter::with_options(options);
let node = Node::Heading {
level: 7, content: vec![Node::Text("Test".to_string())],
heading_type: HeadingType::Atx,
};
assert!(writer.write(&node).is_ok());
assert_eq!(writer.into_string(), "###### Test\n");
}
#[test]
fn test_newline_in_link_text_strict() {
init_logger();
let options = WriterOptions {
strict: true,
..Default::default()
};
let mut writer = CommonMarkWriter::with_options(options);
let node = Node::Link {
url: "http://example.com".to_string(),
title: None,
content: vec![Node::Text("Link\nText".to_string())], };
match writer.write(&node) {
Err(WriteError::NewlineInInlineElement(context)) => assert_eq!(context, "Link content"),
_ => panic!("Expected NewlineInInlineElement error for link text"),
}
}
#[test]
fn test_newline_in_link_text_non_strict() {
init_logger();
let options = WriterOptions {
strict: false,
..Default::default()
};
let mut writer = CommonMarkWriter::with_options(options);
let node = Node::Link {
url: "http://example.com".to_string(),
title: None,
content: vec![Node::Text("Link\nText".to_string())], };
assert!(writer.write(&node).is_ok());
assert_eq!(writer.into_string(), "[Link\nText](http://example.com)");
}