gopher_protocol/lib.rs
1//! # gopher-protocol
2//!
3//! An implementation of [Gopher](https://datatracker.ietf.org/doc/html/rfc1436)
4//! (`gopher://`, port 70) and its **Gopher+** successor: an async client, a
5//! menu parser, and the Gopher+ attribute, view, and form model.
6//!
7//! Gopher is the elder smolweb protocol. A request is a selector and a CRLF; a
8//! reply is a body with no status line and no MIME type. The item-type
9//! character carried in the URL path is the only hint about what the bytes are,
10//! which is why this crate reports a best-effort MIME rather than inventing a
11//! status the protocol does not have.
12//!
13//! ## Gopher+
14//!
15//! [Gopher+](https://github.com/gopher-protocol/gopher-plus) (1993) is an
16//! upward-compatible superset, and this crate treats it as one rather than as a
17//! separate protocol: a plain RFC 1436 menu simply has no
18//! [`GopherPlus`](menu::GopherPlus) markers on its items. Gopher+ adds a
19//! response header carrying a real length, attribute blocks describing an item
20//! without fetching it, alternate representations, and `+ASK` forms. See
21//! [`plus`], and [`client::fetch_plus`] to run a Gopher+ transaction.
22//!
23//! ## Two halves, separately usable
24//!
25//! [`menu`] parses RFC 1436 menus into typed items with RFC 4266 URLs, and
26//! [`plus`] parses everything Gopher+ adds. Both have no dependencies and are
27//! always compiled, so a consumer that only renders gophermaps can take this
28//! crate with `default-features = false` and pull no async runtime:
29//!
30//! ```toml
31//! gopher-protocol = { version = "0.1", default-features = false }
32//! ```
33//!
34//! [`client`] fetches over TCP and rides the default `client` feature.
35//!
36//! ## Parsing a menu
37//!
38//! ```
39//! let menu = "1Software\t/software\tgopher.example\t70\r\niA note\t\t\t\r\n";
40//! let items = gopher_protocol::parse_menu(menu);
41//!
42//! assert_eq!(items[0].url.as_deref(), Some("gopher://gopher.example/1/software"));
43//! assert_eq!(items[1].kind, gopher_protocol::GopherKind::Info);
44//! assert!(items[1].url.is_none(), "info lines carry no resource");
45//! ```
46//!
47//! Fetching is [`client::fetch`], documented on that module so this example
48//! stays honest under `default-features = false`.
49//!
50//! ## Scope
51//!
52//! This crate is a client and a parser. It does not serve gopher, and it holds
53//! no document or render model: what a `Search` item or an `Image` item should
54//! look like on screen is the consumer's decision.
55
56#![forbid(unsafe_code)]
57
58pub mod menu;
59pub mod plus;
60
61#[cfg(feature = "client")]
62pub mod client;
63
64pub use menu::{GopherItem, GopherKind, GopherPlus, parse as parse_menu};
65pub use plus::{AskDirective, AttributeBlock, PlusHeader, View};
66
67#[cfg(feature = "client")]
68pub use client::{
69 ClientError, DEFAULT_PORT, PlusReply, PlusRequest, Response, fetch, fetch_attributes,
70 fetch_directory_attributes, fetch_plus, mime_for_item_type,
71};