Expand description
The main bbcode system. You create this to parse bbcode! Inexpensive clones, since fields are all reference counted.
Fields§
§matchers: Arc<Vec<MatchInfo>>Supply this!
Implementations§
source§impl BBCode
impl BBCode
sourcepub fn default() -> Result<Self, Error>
pub fn default() -> Result<Self, Error>
Get a default bbcode parser. Should hopefully have reasonable defaults!
sourcepub fn from_matchers(matchers: Vec<MatchInfo>) -> Self
pub fn from_matchers(matchers: Vec<MatchInfo>) -> Self
Create a BBCode parser from the given list of matchers. If you’re building a custom set of tags,
or merging [BBCode::basic()] with BBCode::extras() (and maybe more), use this endpoint
sourcepub fn to_consumer(&mut self)
pub fn to_consumer(&mut self)
Convert the current bbcode instance to one which consumes all tags it used to parse. The raw text SHOULD be left untouched (I think?)
sourcepub fn get_tagregex(
tag: &'static str,
open_consume: Option<(i32, i32)>,
close_consume: Option<(i32, i32)>
) -> (String, String)
pub fn get_tagregex(
tag: &'static str,
open_consume: Option<(i32, i32)>,
close_consume: Option<(i32, i32)>
) -> (String, String)
Produce the two basic regexes (open and close) for bbcode tags
Examples found in repository?
325 326 327 328 329 330 331 332 333 334 335 336 337 338
pub fn add_tagmatcher(matchers: &mut Vec<MatchInfo>, tag: &'static str, info: ScopeInfo, open_consume: Option<(i32,i32)>, close_consume: Option<(i32,i32)>) -> Result<(), Error> { //open_regex: String, close_regex: String) -> Result<(), Error> {
let (open_tag, close_tag) = Self::get_tagregex(tag, open_consume, close_consume);
matchers.push(MatchInfo {
id: tag,
regex: Regex::new(&open_tag)?,
match_type: MatchType::Open(Arc::new(info))
});
matchers.push(MatchInfo {
id: tag,
regex: Regex::new(&close_tag)?,
match_type: MatchType::Close,
});
Ok(())
}sourcepub fn add_tagmatcher(
matchers: &mut Vec<MatchInfo>,
tag: &'static str,
info: ScopeInfo,
open_consume: Option<(i32, i32)>,
close_consume: Option<(i32, i32)>
) -> Result<(), Error>
pub fn add_tagmatcher(
matchers: &mut Vec<MatchInfo>,
tag: &'static str,
info: ScopeInfo,
open_consume: Option<(i32, i32)>,
close_consume: Option<(i32, i32)>
) -> Result<(), Error>
Add the open and close matches to the given vector for the given tag (you must construct ScopeInfo yourself). open_consume and close_consume are the amount of newlines to take before and after the open and close tag
Examples found in repository?
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
pub fn basics() -> Result<Vec<MatchInfo>, regex::Error>
{
//First, get the default direct replacements
let mut matches : Vec<MatchInfo> = Vec::new();
//This is an optimization: any large block of characters that has no meaning in bbcode can go straight through.
matches.push(MatchInfo {
id: NORMALTEXTID,
//We use h to catch ourselves on https. this unfortunately breaks up large sections of text into much
//smaller ones, but it should be ok... I don't know. My parser is stupid lol
regex: Regex::new(r#"^[^\[\n\rh]+"#)?,
match_type : MatchType::Simple(Arc::new(|c| String::from(html_escape::encode_quoted_attribute(&c[0]))))
});
matches.push(MatchInfo {
id: CONSUMETEXTID,
regex: Regex::new(r#"^[\r]+"#)?,
match_type: MatchType::Simple(Arc::new(|_c| String::new()))
});
#[allow(unused_variables)]
{
Self::add_tagmatcher(&mut matches, "b", ScopeInfo::basic(Arc::new(|o,b,c| format!("<b>{b}</b>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "i", ScopeInfo::basic(Arc::new(|o,b,c| format!("<i>{b}</i>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "sup", ScopeInfo::basic(Arc::new(|o,b,c| format!("<sup>{b}</sup>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "sub", ScopeInfo::basic(Arc::new(|o,b,c| format!("<sub>{b}</sub>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "u", ScopeInfo::basic(Arc::new(|o,b,c| format!("<u>{b}</u>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "s", ScopeInfo::basic(Arc::new(|o,b,c| format!("<s>{b}</s>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "list", ScopeInfo::basic(Arc::new(|o,b,c| format!("<ul>{b}</ul>"))), Some((0,1)), Some((0,1)))?;
Self::add_tagmatcher(&mut matches, r"\*", ScopeInfo {
only: None, double_closes: true, emit: Arc::new(|o,b,c| format!("<li>{b}</li>"))
}, Some((1,0)), Some((1,0)))?;
Self::add_tagmatcher(&mut matches, r"url", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,c| format!(r#"<a href="{}" target="_blank">{}</a>"#, Self::attr_or_body(o,b), b) )
}, None, None)?;
Self::add_tagmatcher(&mut matches, r"img", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,c| format!(r#"<img src="{}">"#, Self::attr_or_body(o,b)) )
}, None, None)?;
}
//This new autolinker is taken from 12 since it works better
let url_chars = r#"[-a-zA-Z0-9_/%&=#+~@$*'!?,.;:]*"#;
let end_chars = r#"[-a-zA-Z0-9_/%&=#+~@$*']"#;
let autolink_regex = format!("^https?://{0}{1}([(]{0}[)]({0}{1})?)?", url_chars, end_chars);
//Don't forget about autolinking! This is a crappy autolinker and it doesn't matter too much!
matches.push(MatchInfo {
id: AUTOLINKID,
//characters taken from google's page https://developers.google.com/maps/url-encoding
//NOTE: removed certain characters from autolinking because they SUCK
regex: Regex::new(&autolink_regex)?,
match_type: MatchType::Simple(Arc::new(|c| format!(r#"<a href="{0}" target="_blank">{0}</a>"#, &c[0])))
});
//There's a [list=1] thing, wonder how to do that. It's nonstandard, our list format is entirely nonstandard
Ok(matches)
}
/// Some fancy extra bbcode. Does not include basics! These are nonstandard, you don't have to use them!
pub fn extras() -> Result<Vec<MatchInfo>, Error>
{
let mut matches : Vec<MatchInfo> = Vec::new();
Self::add_tagmatcher(&mut matches, "h1", ScopeInfo::basic(Arc::new(|_o,b,_c| format!("<h1>{}</h1>",b))), None, None)?;
Self::add_tagmatcher(&mut matches, "h2", ScopeInfo::basic(Arc::new(|_o,b,_c| format!("<h2>{}</h2>",b))), None, None)?;
Self::add_tagmatcher(&mut matches, "h3", ScopeInfo::basic(Arc::new(|_o,b,_c| format!("<h3>{}</h3>",b))), None, None)?;
Self::add_tagmatcher(&mut matches, r"quote", ScopeInfo::basic(
Arc::new(|o,b,_c| format!(r#"<blockquote{}>{}</blockquote>"#, Self::attr_or_nothing(o,"cite"), b) )
), Some((0,1)), Some((0,1)))?;
Self::add_tagmatcher(&mut matches, r"spoiler", ScopeInfo::basic(
Arc::new(|o,b,_c| format!(r#"<details class="spoiler">{}{}</details>"#, Self::tag_or_something(o,"summary", Some("Spoiler")), b) )
), None, None)?;
Self::add_tagmatcher(&mut matches, r"anchor", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,_c| format!(r#"<a{}>{}</a>"#, Self::attr_or_nothing(o,"name"), b) )
}, None, None)?;
Self::add_tagmatcher(&mut matches, r"icode", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|_o,b,_c| format!(r#"<span class="icode">{b}</span>"#) )
}, None, None)?;
Self::add_tagmatcher(&mut matches, r"code", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,_c| format!(r#"<pre class="code"{}>{}</pre>"#, Self::attr_or_nothing(o, "data-code"), b) )
}, Some((0,1)), Some((0,1)))?;
Self::add_tagmatcher(&mut matches, r"youtube", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,_c| format!(r#"<a href={} target="_blank" data-youtube>{}</a>"#, Self::attr_or_body(o, b), b) )
}, None, None)?;
Ok(matches)
}sourcepub fn plaintext_ids() -> Vec<&'static str> ⓘ
pub fn plaintext_ids() -> Vec<&'static str> ⓘ
Examples found in repository?
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495
pub fn basics() -> Result<Vec<MatchInfo>, regex::Error>
{
//First, get the default direct replacements
let mut matches : Vec<MatchInfo> = Vec::new();
//This is an optimization: any large block of characters that has no meaning in bbcode can go straight through.
matches.push(MatchInfo {
id: NORMALTEXTID,
//We use h to catch ourselves on https. this unfortunately breaks up large sections of text into much
//smaller ones, but it should be ok... I don't know. My parser is stupid lol
regex: Regex::new(r#"^[^\[\n\rh]+"#)?,
match_type : MatchType::Simple(Arc::new(|c| String::from(html_escape::encode_quoted_attribute(&c[0]))))
});
matches.push(MatchInfo {
id: CONSUMETEXTID,
regex: Regex::new(r#"^[\r]+"#)?,
match_type: MatchType::Simple(Arc::new(|_c| String::new()))
});
#[allow(unused_variables)]
{
Self::add_tagmatcher(&mut matches, "b", ScopeInfo::basic(Arc::new(|o,b,c| format!("<b>{b}</b>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "i", ScopeInfo::basic(Arc::new(|o,b,c| format!("<i>{b}</i>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "sup", ScopeInfo::basic(Arc::new(|o,b,c| format!("<sup>{b}</sup>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "sub", ScopeInfo::basic(Arc::new(|o,b,c| format!("<sub>{b}</sub>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "u", ScopeInfo::basic(Arc::new(|o,b,c| format!("<u>{b}</u>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "s", ScopeInfo::basic(Arc::new(|o,b,c| format!("<s>{b}</s>"))), None, None)?;
Self::add_tagmatcher(&mut matches, "list", ScopeInfo::basic(Arc::new(|o,b,c| format!("<ul>{b}</ul>"))), Some((0,1)), Some((0,1)))?;
Self::add_tagmatcher(&mut matches, r"\*", ScopeInfo {
only: None, double_closes: true, emit: Arc::new(|o,b,c| format!("<li>{b}</li>"))
}, Some((1,0)), Some((1,0)))?;
Self::add_tagmatcher(&mut matches, r"url", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,c| format!(r#"<a href="{}" target="_blank">{}</a>"#, Self::attr_or_body(o,b), b) )
}, None, None)?;
Self::add_tagmatcher(&mut matches, r"img", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,c| format!(r#"<img src="{}">"#, Self::attr_or_body(o,b)) )
}, None, None)?;
}
//This new autolinker is taken from 12 since it works better
let url_chars = r#"[-a-zA-Z0-9_/%&=#+~@$*'!?,.;:]*"#;
let end_chars = r#"[-a-zA-Z0-9_/%&=#+~@$*']"#;
let autolink_regex = format!("^https?://{0}{1}([(]{0}[)]({0}{1})?)?", url_chars, end_chars);
//Don't forget about autolinking! This is a crappy autolinker and it doesn't matter too much!
matches.push(MatchInfo {
id: AUTOLINKID,
//characters taken from google's page https://developers.google.com/maps/url-encoding
//NOTE: removed certain characters from autolinking because they SUCK
regex: Regex::new(&autolink_regex)?,
match_type: MatchType::Simple(Arc::new(|c| format!(r#"<a href="{0}" target="_blank">{0}</a>"#, &c[0])))
});
//There's a [list=1] thing, wonder how to do that. It's nonstandard, our list format is entirely nonstandard
Ok(matches)
}
/// Some fancy extra bbcode. Does not include basics! These are nonstandard, you don't have to use them!
pub fn extras() -> Result<Vec<MatchInfo>, Error>
{
let mut matches : Vec<MatchInfo> = Vec::new();
Self::add_tagmatcher(&mut matches, "h1", ScopeInfo::basic(Arc::new(|_o,b,_c| format!("<h1>{}</h1>",b))), None, None)?;
Self::add_tagmatcher(&mut matches, "h2", ScopeInfo::basic(Arc::new(|_o,b,_c| format!("<h2>{}</h2>",b))), None, None)?;
Self::add_tagmatcher(&mut matches, "h3", ScopeInfo::basic(Arc::new(|_o,b,_c| format!("<h3>{}</h3>",b))), None, None)?;
Self::add_tagmatcher(&mut matches, r"quote", ScopeInfo::basic(
Arc::new(|o,b,_c| format!(r#"<blockquote{}>{}</blockquote>"#, Self::attr_or_nothing(o,"cite"), b) )
), Some((0,1)), Some((0,1)))?;
Self::add_tagmatcher(&mut matches, r"spoiler", ScopeInfo::basic(
Arc::new(|o,b,_c| format!(r#"<details class="spoiler">{}{}</details>"#, Self::tag_or_something(o,"summary", Some("Spoiler")), b) )
), None, None)?;
Self::add_tagmatcher(&mut matches, r"anchor", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,_c| format!(r#"<a{}>{}</a>"#, Self::attr_or_nothing(o,"name"), b) )
}, None, None)?;
Self::add_tagmatcher(&mut matches, r"icode", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|_o,b,_c| format!(r#"<span class="icode">{b}</span>"#) )
}, None, None)?;
Self::add_tagmatcher(&mut matches, r"code", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,_c| format!(r#"<pre class="code"{}>{}</pre>"#, Self::attr_or_nothing(o, "data-code"), b) )
}, Some((0,1)), Some((0,1)))?;
Self::add_tagmatcher(&mut matches, r"youtube", ScopeInfo {
only: Some(Self::plaintext_ids()),
double_closes: false,
emit: Arc::new(|o,b,_c| format!(r#"<a href={} target="_blank" data-youtube>{}</a>"#, Self::attr_or_body(o, b), b) )
}, None, None)?;
Ok(matches)
}sourcepub fn basics() -> Result<Vec<MatchInfo>, Error>
pub fn basics() -> Result<Vec<MatchInfo>, Error>
Get a vector of ALL basic matchers! This is the function you want to call to get a vector for the bbcode generator!
sourcepub fn extras() -> Result<Vec<MatchInfo>, Error>
pub fn extras() -> Result<Vec<MatchInfo>, Error>
Some fancy extra bbcode. Does not include basics! These are nonstandard, you don’t have to use them!
sourcepub fn parse(&self, input: &str) -> String
pub fn parse(&self, input: &str) -> String
Main function! You call this to parse your raw bbcode! It also escapes html stuff so it can be used raw! Current version keeps newlines as-is and it’s expected you use pre-wrap, later there may be modes for more standard implementations
Examples found in repository?
575 576 577 578 579 580 581 582 583 584 585 586 587 588
pub fn parse_profiled_opt(&mut self, input: &str, _name: String) -> String
{
#[cfg(feature = "profiling")]
{
let mut profile = onestop::OneDuration::new(_name);
let result = self.parse(input);
profile.finish();
self.profiler.add(profile);
result
}
#[cfg(not(feature = "profiling"))]
return self.parse(input);
}sourcepub fn parse_profiled_opt(&mut self, input: &str, _name: String) -> String
pub fn parse_profiled_opt(&mut self, input: &str, _name: String) -> String
This MAY OR MAY NOT profile, depending on your featureset!