Update `gemini`: Convenience methods, make streaming parsers public; update tests
[?]
Apr 16, 2021, 2:24 AM
3UXSFGJYCPS5IT5O5QDKLJOMDJKE7ZBTCFX3BYXTH2JFLP5GLDMACDependencies
- [2]
M5HGUS2TAdd parser tests and fix some bugs - [3]
2L47PJWNFix Cargo repo again - [4]
2OIMF64IUpdate README - [5]
J4PKMKJXAdd root doc comment, gemtext module - [6]
N3U3GWELFix README formatting - [7]
LYRVL33UBump version, add CHANGELOG - [8]
ENARAOABTweak Cargo manifest for gemini - [9]
LSYI2TXCHousekeeping, add RequestType - [10]
NKDEPTTNFix typo - [11]
55JQX7NFAdd sputnik - [12]
K37J3USBAdd WIP gemtext parsers - [13]
Y55SCAUNFinish gemtext parsing implementation - [14]
RTDVYLFTGive gemtext parser a more uniform interface wrt other parsers - [15]
BOFUYB6IAdd documentation and implement some feedback from Discord (https://discord.com/channels/273534239310479360/354038657075904544/796256815024701480) - [16]
KP6SZZ34Fix building without parsers - [17]
JS6JZ7IAAdd an initial planned feature list for `gemini` - [18]
JBZGFYVOAdd nom parsers for main types - [19]
7DH43OFGAdd gemtext helpers for headers and responses - [20]
362KVUHOEven more housekeeping - [21]
YYPDM4NDMore housekeeping - [22]
XZ6Z2D6HDRY parser impls - [23]
G6HLEG2XUpdate ignore - [24]
XHDJKZOSFix test - [25]
AOSTKTLOLicensing - [26]
WJROXZ3CAdd note to README thanking mgattozzi - [27]
5II6T7YEAdd gemini library - [28]
L6GZJGTUAdd READMEs
Change contents
- file move: gemini → gemini
- file move: Cargo.toml → Cargo.toml
- replacement in gemini/Cargo.toml at line 11
version = "0.0.3"version = "0.0.4" - replacement in gemini/Cargo.toml at line 20
thiserror = "1.0.23"url = "2.2.0"thiserror = "1.0.24"url = "2.2.1" - replacement in gemini/Cargo.toml at line 23
nom = { version = "6.0.1", optional = true }paste = { version = "1.0.4", optional = true }[5.4313]nom = { version = "6.1.2", optional = true }paste = { version = "1.0.5", optional = true } - file move: CHANGELOG.md → CHANGELOG.md
- replacement in gemini/CHANGELOG.md at line 9
## [0.0.3] - 2020-01-14## [0.0.4] - 2021-04-15### Added- Expose both complete and streaming parsers.### Changed- Gemtext parsing doesn't preserve whitespace## [0.0.3] - 2021-01-14 - replacement in gemini/CHANGELOG.md at line 25
## [0.0.2] - 2020-01-14## [0.0.2] - 2021-01-14 - file move: README.md → README.md
- replacement in gemini/README.md at line 8
- [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 - edit in gemini/README.md at line 18
- file move: src → src
- file move: status.rs → status.rs
- edit in gemini/src/status.rs at line 83
- replacement in gemini/src/status.rs at line 239
use nom::{bytes::complete::take, combinator::map_res, error::context, IResult};use nom::{bytes::streaming::take, combinator::map_res, error::context, IResult}; - file move: response.rs → response.rs
- file move: request.rs → request.rs
- edit in gemini/src/request.rs at line 11
pub use request_type::{Any, Gemini}; - replacement in gemini/src/request.rs at line 115
self.url.scheme() == Self::GEMINI_SCHEMEself.scheme() == Self::GEMINI_SCHEME - edit in gemini/src/request.rs at line 122
/// Return the scheme for the underlying url.pub fn scheme(&self) -> &str {self.url.scheme()}/// Return a reference to the underlying url.pub fn url(&self) -> &Url {&self.url} - edit in gemini/src/request.rs at line 152
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()}} - replacement in gemini/src/request.rs at line 175
bytes::complete::{tag, take_until},bytes::streaming::{tag, take_until}, - file move: request → request
- file move: request_type.rs → request_type.rs
- file move: lib.rs → lib.rs
- replacement in gemini/src/lib.rs at line 24
pub use request::{InvalidRequest, Request, Url};pub use request::{AnyRequest, GeminiRequest, InvalidRequest, Request, Url}; - edit in gemini/src/lib.rs at line 28
/// Helper module with parsers for *COMPLETE* streams of bytes. If you want/// streaming/resumable parsers, use the parser functions in each submodule/// directly. Gemtext parsers only work with complete input. - replacement in gemini/src/lib.rs at line 32
mod parse {pub mod parse { - edit in gemini/src/lib.rs at line 36
pub use nom::Err; - replacement in gemini/src/lib.rs at line 65
/// Parse a `Header` from bytes./// Parse a complete `Header` from bytes. - replacement in gemini/src/lib.rs at line 67
/// Parse a `Request` from bytes./// Parse a complete `Request` from bytes. - replacement in gemini/src/lib.rs at line 69
/// Parse a `Response` from bytes./// Parse a complete `Response` from bytes. - replacement in gemini/src/lib.rs at line 71
/// Parse a `Status` from bytes./// Parse a complete `Status` from bytes. - edit in gemini/src/lib.rs at line 87
#[cfg(feature = "parsers")]pub use parse::{parse_gemtext, parse_header, parse_request, parse_response, parse_status}; - file move: header.rs → header.rs
- edit in gemini/src/header.rs at line 99
/// Construct a new `Header` without checking the validity of the arguments.pub fn new_unchecked(status: Status, meta: String) -> Self {Header { status, meta }} - replacement in gemini/src/header.rs at line 105
pub fn success(mime_type: impl Into<String>) -> Option<Self> {Self::new(Status::SUCCESS, mime_type.into())pub fn success(mime_type: String) -> Option<Self> {Self::new(Status::SUCCESS, mime_type) - replacement in gemini/src/header.rs at line 121
bytes::complete::{tag, take_until},bytes::streaming::{tag, take_until}, - replacement in gemini/src/header.rs at line 162
Header::success("text/json").unwrap()Header::success("text/json".to_string()).unwrap() - file move: gemtext.rs → gemtext.rs
- edit in gemini/src/gemtext.rs at line 9
//!//! NB: Parsing gemtext from strings won't necessarily preserve whitespace. - edit in gemini/src/gemtext.rs at line 21
// TODO: track "trivia"? eg spacing between sigils and content, trailing whitespace - edit in gemini/src/gemtext.rs at line 201
#[cfg(test)]const DOCUMENT: &str = r#"```logowooo/^^^^\| |\____/``` - edit in gemini/src/gemtext.rs at line 211
# 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"#; - replacement in gemini/src/gemtext.rs at line 228
bytes::complete::{is_not, tag, take_until},character::complete::{line_ending, not_line_ending, space0, space1},bytes::complete::{tag, take_until},character::complete::{line_ending, not_line_ending}, - replacement in gemini/src/gemtext.rs at line 232
multi::many1,sequence::{pair, preceded, terminated, tuple},multi::separated_list0,sequence::{delimited, pair, preceded, terminated}, - edit in gemini/src/gemtext.rs at line 239
fn line(input: &str) -> IResult<&str, &str> {terminated(not_line_ending, line_ending)(input)} - replacement in gemini/src/gemtext.rs at line 242
map(line, |s: &str| {map(not_line_ending, |s: &str| { - replacement in gemini/src/gemtext.rs at line 255[5.1362]→[5.1362:1379](∅→∅),[5.1379]→[2.3384:3655](∅→∅),[2.3655]→[5.1461:1653](∅→∅),[5.1461]→[5.1461:1653](∅→∅)
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 }}), - edit in gemini/src/gemtext.rs at line 271
value(Level::One, tag("#")),value(Level::Two, tag("##")), - edit in gemini/src/gemtext.rs at line 272
value(Level::Two, tag("##")),value(Level::One, tag("#")), - replacement in gemini/src/gemtext.rs at line 280[5.1920]→[5.1920:1937](∅→∅),[5.1937]→[2.3656:3806](∅→∅),[2.3806]→[5.2008:2084](∅→∅),[5.2008]→[5.2008:2084](∅→∅)
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())}), - replacement in gemini/src/gemtext.rs at line 289[5.2193]→[5.2193:2210](∅→∅),[5.2210]→[2.3807:3886](∅→∅),[2.3886]→[5.2288:2359](∅→∅),[5.2288]→[5.2288:2359](∅→∅)
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())}), - replacement in gemini/src/gemtext.rs at line 298[5.2478]→[5.2478:2495](∅→∅),[5.2495]→[2.3887:4044](∅→∅),[2.4044]→[5.2573:2641](∅→∅),[5.2573]→[5.2573:2641](∅→∅)
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())}), - replacement in gemini/src/gemtext.rs at line 310
terminated(preceded(tag("```"), opt(not_line_ending)), line_ending),delimited(tag("```"), opt(not_line_ending), line_ending), - replacement in gemini/src/gemtext.rs at line 313
tuple((line_ending, tag("```"), not_line_ending, line_ending)),pair(line_ending, tag("```")), - replacement in gemini/src/gemtext.rs at line 316
alt: alt.map(|s| s.to_string()),alt: alt.and_then(|s| {let s = s.trim();if s.is_empty() {None} else {Some(s.to_string())}}), - replacement in gemini/src/gemtext.rs at line 330
/// Parse a utf-8 encoded gemtext document./// Parse a *complete* gemtext document from utf-8 encoded text. - replacement in gemini/src/gemtext.rs at line 335
many1(alt((link, heading, list_item, quote, preformatted, text))),terminated(separated_list0(line_ending,alt((link, heading, list_item, quote, preformatted, text)),),opt(line_ending),), - edit in gemini/src/gemtext.rs at line 350
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);} - edit in gemini/src/gemtext.rs at line 386
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] - replacement in gemini/src/gemtext.rs at line 482
assert_eq!(document(DOCUMENT).unwrap().1,assert_is!(document(DOCUMENT), - replacement in gemini/src/gemtext.rs at line 503
}},Doc::Blank - edit in gemini/src/gemtext.rs at line 531[2.5536]→[2.5536:5585](∅→∅),[2.5585]→[5.112:120](∅→∅),[5.112]→[5.112:120](∅→∅),[5.120]→[5.766:979](∅→∅),[5.766]→[5.766:979](∅→∅),[5.979]→[2.5586:5590](∅→∅)
#[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"#;