Split md_parser
This commit is contained in:
parent
9b192a92f2
commit
85875abd2b
|
@ -7,10 +7,16 @@ keywords = ["qbittorrent"]
|
||||||
repository = "https://github.com/JoelWachsler/qbittorrent-web-api"
|
repository = "https://github.com/JoelWachsler/qbittorrent-web-api"
|
||||||
description = "Generated web api for qBittorrent"
|
description = "Generated web api for qBittorrent"
|
||||||
exclude = ["*.txt", "tests"]
|
exclude = ["*.txt", "tests"]
|
||||||
|
# we use trybuild instead
|
||||||
|
autotests = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "tests"
|
||||||
|
path = "tests/tests.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = { version = "1.0.98", features = ["extra-traits"]}
|
syn = { version = "1.0.98", features = ["extra-traits"]}
|
||||||
quote = "1.0.20"
|
quote = "1.0.20"
|
||||||
|
|
127
qbittorrent-web-api-gen/src/md_parser/md_token.rs
Normal file
127
qbittorrent-web-api-gen/src/md_parser/md_token.rs
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum MdContent {
|
||||||
|
Text(String),
|
||||||
|
Asterix(String),
|
||||||
|
Table(Table),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Table {
|
||||||
|
pub header: TableRow,
|
||||||
|
pub split: String,
|
||||||
|
pub rows: Vec<TableRow>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Table {
|
||||||
|
fn raw(&self) -> String {
|
||||||
|
let mut output = vec![self.header.raw.clone(), self.split.clone()];
|
||||||
|
for row in self.rows.clone() {
|
||||||
|
output.push(row.raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Header {
|
||||||
|
pub level: i32,
|
||||||
|
pub content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct TableRow {
|
||||||
|
raw: String,
|
||||||
|
pub columns: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MdContent {
|
||||||
|
pub fn inner_value_as_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
MdContent::Text(text) => text.into(),
|
||||||
|
MdContent::Asterix(text) => text.into(),
|
||||||
|
MdContent::Table(table) => table.raw(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MdToken {
|
||||||
|
Header(Header),
|
||||||
|
Content(MdContent),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MdToken {
|
||||||
|
fn parse_token(line: &str) -> MdToken {
|
||||||
|
if line.starts_with('#') {
|
||||||
|
let mut level = 0;
|
||||||
|
for char in line.chars() {
|
||||||
|
if char != '#' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
level += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MdToken::Header(Header {
|
||||||
|
level,
|
||||||
|
content: line.trim_matches('#').trim().to_string(),
|
||||||
|
})
|
||||||
|
} else if line.starts_with('*') {
|
||||||
|
MdToken::Content(MdContent::Asterix(
|
||||||
|
line.trim_matches('*').trim().to_string(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
MdToken::Content(MdContent::Text(line.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from(content: &str) -> Vec<MdToken> {
|
||||||
|
let mut output = Vec::new();
|
||||||
|
|
||||||
|
let mut iter = content.lines();
|
||||||
|
while let Some(line) = iter.next() {
|
||||||
|
// assume this is a table
|
||||||
|
if line.contains('|') {
|
||||||
|
let to_columns = |column_line: &str| {
|
||||||
|
column_line
|
||||||
|
.replace('`', "")
|
||||||
|
.split('|')
|
||||||
|
.map(|s| s.trim().to_string())
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
let table_header = TableRow {
|
||||||
|
raw: line.into(),
|
||||||
|
columns: to_columns(line),
|
||||||
|
};
|
||||||
|
let table_split = iter.next().unwrap();
|
||||||
|
let mut table_rows = Vec::new();
|
||||||
|
while let Some(row_line) = iter.next() {
|
||||||
|
if !row_line.contains('|') {
|
||||||
|
// we've reached the end of the table, let's go back one step
|
||||||
|
iter.next_back();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let table_row = TableRow {
|
||||||
|
raw: row_line.into(),
|
||||||
|
columns: to_columns(row_line),
|
||||||
|
};
|
||||||
|
|
||||||
|
table_rows.push(table_row);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push(MdToken::Content(MdContent::Table(Table {
|
||||||
|
header: table_header,
|
||||||
|
split: table_split.to_string(),
|
||||||
|
rows: table_rows,
|
||||||
|
})));
|
||||||
|
} else {
|
||||||
|
output.push(MdToken::parse_token(line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,316 +1,7 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
mod md_token;
|
||||||
|
mod token_tree;
|
||||||
|
mod token_tree_factory;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
pub use md_token::*;
|
||||||
pub enum MdContent {
|
pub use token_tree::TokenTree;
|
||||||
Text(String),
|
pub use token_tree_factory::TokenTreeFactory;
|
||||||
Asterix(String),
|
|
||||||
Table(Table),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct Table {
|
|
||||||
pub header: TableRow,
|
|
||||||
pub split: String,
|
|
||||||
pub rows: Vec<TableRow>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Table {
|
|
||||||
fn raw(&self) -> String {
|
|
||||||
let mut output = vec![self.header.raw.clone(), self.split.clone()];
|
|
||||||
for row in self.rows.clone() {
|
|
||||||
output.push(row.raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
output.join("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct TableRow {
|
|
||||||
raw: String,
|
|
||||||
pub columns: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MdContent {
|
|
||||||
pub fn inner_value_as_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
MdContent::Text(text) => text.into(),
|
|
||||||
MdContent::Asterix(text) => text.into(),
|
|
||||||
MdContent::Table(table) => table.raw(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Header {
|
|
||||||
level: i32,
|
|
||||||
content: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// These are the only relevant tokens we need for the api generation.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum MdToken {
|
|
||||||
Header(Header),
|
|
||||||
Content(MdContent),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MdToken {
|
|
||||||
fn parse_token(line: &str) -> MdToken {
|
|
||||||
if line.starts_with('#') {
|
|
||||||
let mut level = 0;
|
|
||||||
for char in line.chars() {
|
|
||||||
if char != '#' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
level += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
MdToken::Header(Header {
|
|
||||||
level,
|
|
||||||
content: line.trim_matches('#').trim().to_string(),
|
|
||||||
})
|
|
||||||
} else if line.starts_with('*') {
|
|
||||||
MdToken::Content(MdContent::Asterix(
|
|
||||||
line.trim_matches('*').trim().to_string(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
MdToken::Content(MdContent::Text(line.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from(content: &str) -> Vec<MdToken> {
|
|
||||||
let mut output = Vec::new();
|
|
||||||
|
|
||||||
let mut iter = content.lines();
|
|
||||||
while let Some(line) = iter.next() {
|
|
||||||
// assume this is a table
|
|
||||||
if line.contains('|') {
|
|
||||||
let to_columns = |column_line: &str| {
|
|
||||||
column_line
|
|
||||||
.replace('`', "")
|
|
||||||
.split('|')
|
|
||||||
.map(|s| s.trim().to_string())
|
|
||||||
.collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
let table_header = TableRow {
|
|
||||||
raw: line.into(),
|
|
||||||
columns: to_columns(line),
|
|
||||||
};
|
|
||||||
let table_split = iter.next().unwrap();
|
|
||||||
let mut table_rows = Vec::new();
|
|
||||||
while let Some(row_line) = iter.next() {
|
|
||||||
if !row_line.contains('|') {
|
|
||||||
// we've reached the end of the table, let's go back one step
|
|
||||||
iter.next_back();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let table_row = TableRow {
|
|
||||||
raw: row_line.into(),
|
|
||||||
columns: to_columns(row_line),
|
|
||||||
};
|
|
||||||
|
|
||||||
table_rows.push(table_row);
|
|
||||||
}
|
|
||||||
|
|
||||||
output.push(MdToken::Content(MdContent::Table(Table {
|
|
||||||
header: table_header,
|
|
||||||
split: table_split.to_string(),
|
|
||||||
rows: table_rows,
|
|
||||||
})));
|
|
||||||
} else {
|
|
||||||
output.push(MdToken::parse_token(line));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TokenTree {
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub content: Vec<MdContent>,
|
|
||||||
pub children: Vec<TokenTree>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Rc<TokenTreeFactory>> for TokenTree {
|
|
||||||
fn from(builder: Rc<TokenTreeFactory>) -> Self {
|
|
||||||
let children = builder
|
|
||||||
.children
|
|
||||||
.clone()
|
|
||||||
.into_inner()
|
|
||||||
.into_iter()
|
|
||||||
.map(|child| child.into())
|
|
||||||
.collect::<Vec<TokenTree>>();
|
|
||||||
|
|
||||||
let content = builder.content.clone().into_inner();
|
|
||||||
|
|
||||||
TokenTree {
|
|
||||||
title: builder.title.clone(),
|
|
||||||
content,
|
|
||||||
children,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct TokenTreeFactory {
|
|
||||||
title: Option<String>,
|
|
||||||
content: RefCell<Vec<MdContent>>,
|
|
||||||
children: RefCell<Vec<Rc<TokenTreeFactory>>>,
|
|
||||||
level: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TokenTreeFactory {
|
|
||||||
fn new(title: &str, level: i32) -> Self {
|
|
||||||
Self {
|
|
||||||
title: if title.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(title.to_string())
|
|
||||||
},
|
|
||||||
level,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_content(&self, content: MdContent) {
|
|
||||||
self.content.borrow_mut().push(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append(&self, child: &Rc<TokenTreeFactory>) {
|
|
||||||
self.children.borrow_mut().push(child.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(content: &str) -> TokenTree {
|
|
||||||
let tokens = MdToken::from(content);
|
|
||||||
|
|
||||||
let mut stack = Vec::new();
|
|
||||||
let root = Rc::new(TokenTreeFactory::default());
|
|
||||||
stack.push(root.clone());
|
|
||||||
|
|
||||||
for token in tokens {
|
|
||||||
match token {
|
|
||||||
MdToken::Header(Header { level, content }) => {
|
|
||||||
let new_header = Rc::new(TokenTreeFactory::new(&content, level));
|
|
||||||
|
|
||||||
// go back until we're at the same or lower level.
|
|
||||||
while let Some(current) = stack.pop() {
|
|
||||||
if current.level < level {
|
|
||||||
current.append(&new_header);
|
|
||||||
stack.push(current);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.push(new_header.clone());
|
|
||||||
}
|
|
||||||
MdToken::Content(content) => {
|
|
||||||
let current = stack.pop().unwrap();
|
|
||||||
current.add_content(content);
|
|
||||||
stack.push(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
root.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_remove_surrounding_asterix() {
|
|
||||||
// given
|
|
||||||
let input = r#"
|
|
||||||
# A
|
|
||||||
**B**
|
|
||||||
"#
|
|
||||||
.trim_matches('\n')
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let tree = TokenTreeFactory::create(input);
|
|
||||||
|
|
||||||
// then
|
|
||||||
println!("{:#?}", tree);
|
|
||||||
let first = tree.children.first().unwrap();
|
|
||||||
let content = first.content.first().unwrap();
|
|
||||||
assert_eq!(*content, MdContent::Asterix("B".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_remove_surrounding_hash() {
|
|
||||||
// given
|
|
||||||
let input = r#"
|
|
||||||
# A #
|
|
||||||
"#
|
|
||||||
.trim_matches('\n')
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let tree = TokenTreeFactory::create(input);
|
|
||||||
|
|
||||||
// then
|
|
||||||
println!("{:#?}", tree);
|
|
||||||
assert_eq!(tree.children.first().unwrap().title, Some("A".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn single_level() {
|
|
||||||
// given
|
|
||||||
let input = r#"
|
|
||||||
# A
|
|
||||||
Foo
|
|
||||||
"#
|
|
||||||
.trim_matches('\n')
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let tree = TokenTreeFactory::create(input);
|
|
||||||
|
|
||||||
// then
|
|
||||||
println!("{:#?}", tree);
|
|
||||||
assert_eq!(tree.title, None);
|
|
||||||
let first_child = tree.children.first().unwrap();
|
|
||||||
assert_eq!(first_child.title, Some("A".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn complex() {
|
|
||||||
// given
|
|
||||||
let input = r#"
|
|
||||||
# A
|
|
||||||
Foo
|
|
||||||
## B
|
|
||||||
# C
|
|
||||||
## D
|
|
||||||
Bar
|
|
||||||
"#
|
|
||||||
.trim_matches('\n')
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
// when
|
|
||||||
let tree = TokenTreeFactory::create(input);
|
|
||||||
|
|
||||||
// then
|
|
||||||
println!("{:#?}", tree);
|
|
||||||
assert_eq!(tree.title, None);
|
|
||||||
assert_eq!(tree.children.len(), 2);
|
|
||||||
|
|
||||||
let first = tree.children.get(0).unwrap();
|
|
||||||
assert_eq!(first.title, Some("A".into()));
|
|
||||||
assert_eq!(first.children.len(), 1);
|
|
||||||
assert_eq!(first.children.first().unwrap().title, Some("B".into()));
|
|
||||||
|
|
||||||
let second = tree.children.get(1).unwrap();
|
|
||||||
assert_eq!(second.title, Some("C".into()));
|
|
||||||
assert_eq!(second.children.len(), 1);
|
|
||||||
assert_eq!(second.children.first().unwrap().title, Some("D".into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
30
qbittorrent-web-api-gen/src/md_parser/token_tree.rs
Normal file
30
qbittorrent-web-api-gen/src/md_parser/token_tree.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use super::{md_token::MdContent, token_tree_factory::TokenTreeFactory};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TokenTree {
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub content: Vec<MdContent>,
|
||||||
|
pub children: Vec<TokenTree>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rc<TokenTreeFactory>> for TokenTree {
|
||||||
|
fn from(builder: Rc<TokenTreeFactory>) -> Self {
|
||||||
|
let children = builder
|
||||||
|
.children
|
||||||
|
.clone()
|
||||||
|
.into_inner()
|
||||||
|
.into_iter()
|
||||||
|
.map(|child| child.into())
|
||||||
|
.collect::<Vec<TokenTree>>();
|
||||||
|
|
||||||
|
let content = builder.content.clone().into_inner();
|
||||||
|
|
||||||
|
TokenTree {
|
||||||
|
title: builder.title.clone(),
|
||||||
|
content,
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
166
qbittorrent-web-api-gen/src/md_parser/token_tree_factory.rs
Normal file
166
qbittorrent-web-api-gen/src/md_parser/token_tree_factory.rs
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
md_token::{Header, MdContent, MdToken},
|
||||||
|
token_tree::TokenTree,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TokenTreeFactory {
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub content: RefCell<Vec<MdContent>>,
|
||||||
|
pub children: RefCell<Vec<Rc<TokenTreeFactory>>>,
|
||||||
|
pub level: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenTreeFactory {
|
||||||
|
fn new(title: &str, level: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
title: if title.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(title.to_string())
|
||||||
|
},
|
||||||
|
level,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_content(&self, content: MdContent) {
|
||||||
|
self.content.borrow_mut().push(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append(&self, child: &Rc<TokenTreeFactory>) {
|
||||||
|
self.children.borrow_mut().push(child.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(content: &str) -> TokenTree {
|
||||||
|
let tokens = MdToken::from(content);
|
||||||
|
|
||||||
|
let mut stack = Vec::new();
|
||||||
|
let root = Rc::new(TokenTreeFactory::default());
|
||||||
|
stack.push(root.clone());
|
||||||
|
|
||||||
|
for token in tokens {
|
||||||
|
match token {
|
||||||
|
MdToken::Header(Header { level, content }) => {
|
||||||
|
let new_header = Rc::new(TokenTreeFactory::new(&content, level));
|
||||||
|
|
||||||
|
// go back until we're at the same or lower level.
|
||||||
|
while let Some(current) = stack.pop() {
|
||||||
|
if current.level < level {
|
||||||
|
current.append(&new_header);
|
||||||
|
stack.push(current);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.push(new_header.clone());
|
||||||
|
}
|
||||||
|
MdToken::Content(content) => {
|
||||||
|
let current = stack.pop().unwrap();
|
||||||
|
current.add_content(content);
|
||||||
|
stack.push(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_remove_surrounding_asterix() {
|
||||||
|
// given
|
||||||
|
let input = r#"
|
||||||
|
# A
|
||||||
|
**B**
|
||||||
|
"#
|
||||||
|
.trim_matches('\n')
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let tree = TokenTreeFactory::create(input);
|
||||||
|
|
||||||
|
// then
|
||||||
|
println!("{:#?}", tree);
|
||||||
|
let first = tree.children.first().unwrap();
|
||||||
|
let content = first.content.first().unwrap();
|
||||||
|
assert_eq!(*content, MdContent::Asterix("B".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_remove_surrounding_hash() {
|
||||||
|
// given
|
||||||
|
let input = r#"
|
||||||
|
# A #
|
||||||
|
"#
|
||||||
|
.trim_matches('\n')
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let tree = TokenTreeFactory::create(input);
|
||||||
|
|
||||||
|
// then
|
||||||
|
println!("{:#?}", tree);
|
||||||
|
assert_eq!(tree.children.first().unwrap().title, Some("A".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn single_level() {
|
||||||
|
// given
|
||||||
|
let input = r#"
|
||||||
|
# A
|
||||||
|
Foo
|
||||||
|
"#
|
||||||
|
.trim_matches('\n')
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let tree = TokenTreeFactory::create(input);
|
||||||
|
|
||||||
|
// then
|
||||||
|
println!("{:#?}", tree);
|
||||||
|
assert_eq!(tree.title, None);
|
||||||
|
let first_child = tree.children.first().unwrap();
|
||||||
|
assert_eq!(first_child.title, Some("A".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complex() {
|
||||||
|
// given
|
||||||
|
let input = r#"
|
||||||
|
# A
|
||||||
|
Foo
|
||||||
|
## B
|
||||||
|
# C
|
||||||
|
## D
|
||||||
|
Bar
|
||||||
|
"#
|
||||||
|
.trim_matches('\n')
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let tree = TokenTreeFactory::create(input);
|
||||||
|
|
||||||
|
// then
|
||||||
|
println!("{:#?}", tree);
|
||||||
|
assert_eq!(tree.title, None);
|
||||||
|
assert_eq!(tree.children.len(), 2);
|
||||||
|
|
||||||
|
let first = tree.children.get(0).unwrap();
|
||||||
|
assert_eq!(first.title, Some("A".into()));
|
||||||
|
assert_eq!(first.children.len(), 1);
|
||||||
|
assert_eq!(first.children.first().unwrap().title, Some("B".into()));
|
||||||
|
|
||||||
|
let second = tree.children.get(1).unwrap();
|
||||||
|
assert_eq!(second.title, Some("C".into()));
|
||||||
|
assert_eq!(second.children.len(), 1);
|
||||||
|
assert_eq!(second.children.first().unwrap().title, Some("D".into()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Api {}
|
struct Api {}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Api {}
|
struct Api {}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Foo {}
|
struct Foo {}
|
||||||
|
|
3
qbittorrent-web-api-gen/tests/common/mod.rs
Normal file
3
qbittorrent-web-api-gen/tests/common/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub const USERNAME: &str = "admin";
|
||||||
|
pub const PASSWORD: &str = "adminadmin";
|
||||||
|
pub const BASE_URL: &str = "http://localhost:8080";
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Api {}
|
struct Api {}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Api {}
|
struct Api {}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Api {}
|
struct Api {}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Api {}
|
struct Api {}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
use tokio::time::{sleep, Duration};
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
use tokio::time::*;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Api {}
|
struct Api {}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
use tokio::time::{sleep, Duration};
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
use tokio::time::*;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Api {}
|
struct Api {}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#[test]
|
#[test]
|
||||||
fn tests() {
|
fn tests() {
|
||||||
let t = trybuild::TestCases::new();
|
let t = trybuild::TestCases::new();
|
||||||
|
|
||||||
// --- Auth ---
|
// --- Auth ---
|
||||||
t.pass("tests/login.rs");
|
t.pass("tests/login.rs");
|
||||||
t.pass("tests/logout.rs");
|
t.pass("tests/logout.rs");
|
||||||
|
@ -19,5 +20,5 @@ fn tests() {
|
||||||
t.pass("tests/add_torrent.rs");
|
t.pass("tests/add_torrent.rs");
|
||||||
t.pass("tests/another_struct_name.rs");
|
t.pass("tests/another_struct_name.rs");
|
||||||
t.pass("tests/access_impl_types.rs");
|
t.pass("tests/access_impl_types.rs");
|
||||||
t.pass("tests/search_types.rs");
|
// t.pass("tests/search_types.rs");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
mod common;
|
||||||
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
|
||||||
|
|
||||||
const USERNAME: &str = "admin";
|
use anyhow::Result;
|
||||||
const PASSWORD: &str = "adminadmin";
|
use common::*;
|
||||||
const BASE_URL: &str = "http://localhost:8080";
|
use qbittorrent_web_api_gen::QBittorrentApiGen;
|
||||||
|
|
||||||
#[derive(QBittorrentApiGen)]
|
#[derive(QBittorrentApiGen)]
|
||||||
struct Api {}
|
struct Api {}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user