gemini/
lib.rs

1#![deny(
2    missing_debug_implementations,
3    missing_docs,
4    missing_copy_implementations
5)]
6#![forbid(unsafe_code)]
7
8//! A general purpose library of types for working with the Gemini protocol
9//!
10//! Represented are Gemini requests, responses, headers, and status codes.
11//! Requests are currently just a small layer of functionality over top of a
12//! `Url` from the aptly named `url` crate. All types are implemented with an
13//! eye towards implementing the Gemini specification faithfully and making
14//! illegal or invalid states unrepresentable.
15
16pub mod gemtext;
17pub mod header;
18pub mod request;
19pub mod response;
20pub mod status;
21
22pub use gemtext::{Builder, Doc, Level};
23pub use header::{Header, MetaKind};
24pub use request::{AnyRequest, GeminiRequest, InvalidRequest, Request, Url};
25pub use response::Response;
26pub use status::{Category, Code, InvalidStatusCode, Status};
27
28/// Helper module with parsers for *COMPLETE* streams of bytes. If you want
29/// streaming/resumable parsers, use the parser functions in each submodule
30/// directly. Gemtext parsers only work with complete input.
31#[cfg(feature = "parsers")]
32pub mod parse {
33    use nom::{error::Error, Finish};
34    use paste::paste;
35
36    pub use nom::Err;
37
38    use crate::{gemtext, header, request, response, status};
39
40    macro_rules! parsers {
41        ($(
42            $(#[$doc:meta])*
43            $name:ident: $type:ident
44        ),*) => {
45            $(
46                paste! {
47                    $(#[$doc])*
48                    pub fn [< parse_ $name >](input: impl AsRef<[u8]>) -> Result<$name::$type, Error<String>> {
49                        let bytes = input.as_ref();
50                        match $name::parse::$name(bytes).finish() {
51                            Ok((_, res)) => Ok(res),
52                            Err(Error { input, code }) => Err({
53                                let bytes = input.to_owned();
54                                let input = String::from_utf8_lossy(&bytes).to_string();
55                                Error { input, code }
56                            })
57                        }
58                    }
59                }
60            )*
61        };
62    }
63
64    parsers!(
65        /// Parse a complete `Header` from bytes.
66        header: Header,
67        /// Parse a complete `Request` from bytes.
68        request: AnyRequest,
69        /// Parse a complete `Response` from bytes.
70        response: Response,
71        /// Parse a complete `Status` from bytes.
72        status: Status
73    );
74
75    /// Parse a gemtext document from utf-8 text.
76    pub fn parse_gemtext(input: impl AsRef<str>) -> Result<gemtext::Builder, Error<String>> {
77        let input = input.as_ref();
78        match gemtext::parse::document(input).finish() {
79            Ok((_, res)) => Ok(res),
80            Err(Error { input, code }) => Err({
81                let input = input.to_string();
82                Error { input, code }
83            }),
84        }
85    }
86}