3UXSFGJYCPS5IT5O5QDKLJOMDJKE7ZBTCFX3BYXTH2JFLP5GLDMAC M5HGUS2TL72TRSUS7NN7ZQND4CDPVBERJQNUQJ4XTZ6GRUEPUIEAC 2L47PJWNRMFJQTSQR2R2AY67QW7CHQ5NK2SHJY6AUHBYDIVXDISQC 2OIMF64IICYQ3TZW3Z7YMYWQSA5WT2XNWSMMOYRKYTO4RUSIN4FQC 5II6T7YETYWUIKUMNJIYFUOQHCA26N2YVJDSHLMNO2GR62MP2ORQC LYRVL33UETNJV4SJ2YC4LBLKMMYPVCHDAIQMWBY7NLWCHL4Q2V2QC ENARAOABLYA4PF2FRU6FFO75KVXQYUB7IVDXL6FSPZOLJ3NYP3ZAC LSYI2TXC2N33NQF4MG2RNBETSPSO7BRTJVUVVO7P3XN4SRJDEWLAC JBZGFYVOKR3X2GH25SOOW3X7RKY6ZACESYFF5NO26PJFUP3XGXUAC L6GZJGTUSHJVC3RGMEIRWEEDZN2LNQCRA2Q4KFTATA42WBP6IBIAC JS6JZ7IAZ6EUSPFURFLZ7AQKFSDGSHV3E7VKEV6VJHYJZMTITHIQC N3U3GWELZPVC2MXIYR4QMBL6VVBKEBOCKKGYWSD3X5AGPF23I2CQC WJROXZ3CGQOYXG5BTGFKJ7UWE26WJC7DRZ4FOZDECGRPBYCGF2TQC BOFUYB6IISDQYT3G5MKVDNWB2WWGHMNYDKTITJBVS5RED6XJLB4QC RTDVYLFT3PRNC4F4TWU47YFMLQPXR65CAAB6IVS7FIY2EB7HJGJQC KP6SZZ346AU4MCK2EZK3WHGGGNDMUGX7AG4CBYKVO4HZMUYRBU6AC 7DH43OFGO4RVLW3LL23FZDMXRHQLAEMK7VD62T7FZBRHZOF3RRDAC J4PKMKJXBUPG4QFHQVATUOXHWLEWMX3LTGWMNFKFAQUTJHU5GSAAC K37J3USB4DC54L5CFARGQOZYF76I4USNJHYEXCTTRF5D5CTJCN3QC Y55SCAUN3MQCMRGJQBBMICEO6ARTGICWGGZYA7VB6ZRPK6US4B5AC XHDJKZOSBFCZWKPTBFTSLDRDGTUBOJLL6XVKVCGMBJXDOELSGLYQC - [x] a `gemtext` module for representing `text/gemini` content- [X] parsers using `nom`- [ ] streaming response bodies- [ ] `no_std` support- [ ] improve doc comments with links n such
- [X] a `gemtext` module for representing `text/gemini` content- [X] parsers using `nom`- [ ] streaming response bodies- [ ] no_std support- [ ] improve doc comments with links n such- [ ] lossless `gemtext` representation
impl GeminiRequest {/// Return the hostname associated with the Gemini request.pub fn host(&self) -> &str {self.url.host_str().unwrap()}/// Return the path portion of the Gemini request url.pub fn path(&self) -> &str {self.url.path()}/// Return the port associated with the Gemini request.pub fn port(&self) -> u16 {self.url.port().unwrap()}}
map(preceded(pair(tag("=>"), space1),terminated(pair(is_not(" \t\r\n"), opt(preceded(space1, not_line_ending))),line_ending,),),|(to, name): (&str, Option<&str>)| Doc::Link {to: to.to_string(),name: name.map(|s| s.to_string()),},),
map(preceded(tag("=>"), not_line_ending), |s: &str| {let s = s.trim();let idx = s.chars().position(char::is_whitespace);let (to, name) = if let Some(idx) = idx {let (to, name) = s.split_at(idx);(to.to_string(), Some(name.trim().to_string()))} else {(s.to_string(), None)};Doc::Link { to, name }}),
map(terminated(pair(terminated(level, space0), not_line_ending),line_ending,),|(lvl, s)| Doc::Heading(lvl, s.to_string()),),
map(pair(level, not_line_ending), |(lvl, s)| {Doc::Heading(lvl, s.trim().to_string())}),
map(terminated(preceded(tag("* "), not_line_ending), line_ending),|s: &str| Doc::ListItem(s.to_string()),),
map(preceded(tag("* "), not_line_ending), |s: &str| {Doc::ListItem(s.trim().to_string())}),
map(terminated(preceded(terminated(tag(">"), space0), not_line_ending),line_ending,),|s: &str| Doc::Quote(s.to_string()),),
map(preceded(tag(">"), not_line_ending), |s: &str| {Doc::Quote(s.trim().to_string())}),
trait Is {type Item;fn is(&self, other: Self::Item) -> bool;}impl<T: Eq, U, E> Is for Result<(U, T), E> {type Item = T;fn is(&self, other: Self::Item) -> bool {if let Ok((_, inner)) = self {inner == &other} else {false}}}macro_rules! assert_is {($actual:expr, $expected:expr) => {assert!($actual.is($expected),"\n\nexpected: {:#?}\n\nactual: {:#?}",$expected,$actual)};}#[test]fn test_text() {assert_is!(text("foo"), Doc::Text("foo".to_string()));assert_is!(text("\nfoo"), Doc::Blank);}
fn test_link() {assert_is!(link("=> gemini://foo"),Doc::Link {to: "gemini://foo".to_string(),name: None});assert_is!(link("=> gemini://foo bar"),Doc::Link {to: "gemini://foo".to_string(),name: Some("bar".to_string()),});assert_is!(link("=> gemini://foo bar baz bax"),Doc::Link {to: "gemini://foo".to_string(),name: Some("bar baz bax".to_string())});assert_is!(link("=>gemini://foo"),Doc::Link {to: "gemini://foo".to_string(),name: None})}#[test]fn test_heading() {assert_is!(heading("# foo"),Doc::Heading(Level::One, "foo".to_string()));assert_is!(heading("## bar"),Doc::Heading(Level::Two, "bar".to_string()));assert_is!(heading("###baz "),Doc::Heading(Level::Three, "baz".to_string()))}#[test]fn test_list_item() {assert_is!(list_item("* foo"), Doc::ListItem("foo".to_string()));assert_is!(list_item("* bar"), Doc::ListItem("bar".to_string()));assert!(list_item("*bad").is_err())}#[test]fn test_quote() {assert_is!(quote("> foo"), Doc::Quote("foo".to_string()));assert_is!(quote(">bar"), Doc::Quote("bar".to_string()));}#[test]fn test_preformatted() {assert_is!(preformatted("```\nfoo\n```"),Doc::Preformatted {alt: None,text: "foo".to_string()});assert_is!(preformatted("```\n\nfoo\n> bar\n=> baz\n```\n"),Doc::Preformatted {alt: None,text: "\nfoo\n> bar\n=> baz".to_string()});assert_is!(preformatted("```foo\nbar\n```"),Doc::Preformatted {alt: Some("foo".to_string()),text: "bar".to_string()});assert_is!(preformatted("``` foo \nbar\n```"),Doc::Preformatted {alt: Some("foo".to_string()),text: "bar".to_string()});assert!(preformatted("```\n").is_err());assert!(preformatted("```\nfoo```").is_err());}#[test]
#[cfg(test)]const DOCUMENT: &'static str = r#"```logowooo/^^^^\| |\____/```# GAZE INTO THE SPHERE!critics are raving> i love the sphere - bort> the sphere gives me purpose - frelvin* always* trust* the sphere=> gemini://sphere.gaze gaze more here"#;