aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2022-06-14 08:45:09 +0000
committerFuwn <[email protected]>2022-06-14 08:45:09 +0000
commitb5703a59c59bfe95143cf8dc6cc54f20f67fb4fd (patch)
tree44022e3944119c39df5b9082aaac3a77841c8c10 /src
parentfix(ast): list ast construction (diff)
downloadgerm-b5703a59c59bfe95143cf8dc6cc54f20f67fb4fd.tar.xz
germ-b5703a59c59bfe95143cf8dc6cc54f20f67fb4fd.zip
feat(macros): general utility macros
Diffstat (limited to 'src')
-rw-r--r--src/ast.rs446
-rw-r--r--src/convert.rs69
-rw-r--r--src/convert/html.rs75
-rw-r--r--src/convert/markdown.rs77
-rw-r--r--src/lib.rs43
-rw-r--r--src/meta.rs162
-rw-r--r--src/request.rs73
-rw-r--r--src/request/response.rs71
-rw-r--r--src/request/status.rs110
-rw-r--r--src/request/verifier.rs39
10 files changed, 0 insertions, 1165 deletions
diff --git a/src/ast.rs b/src/ast.rs
deleted file mode 100644
index eae0683..0000000
--- a/src/ast.rs
+++ /dev/null
@@ -1,446 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-//! Build AST trees from Gemtext
-
-/// A Gemtext AST node.
-///
-/// Each Gemtext line is a `Node`, and some lines can even be grouped together,
-/// such as the `Node::List` `Node`!
-///
-/// # Gemtext Resources
-///
-/// - [Gemtext Documentation](https://gemini.circumlunar.space/docs/gemtext.gmi)
-/// - [Gemtext Cheatsheet](https://gemini.circumlunar.space/docs/cheatsheet.gmi).
-/// - [Gemini Specification](https://gemini.circumlunar.space/docs/specification.gmi).
-#[derive(Debug, PartialEq)]
-pub enum Node {
- /// A text line
- ///
- /// # Example
- ///
- /// ```gemini
- /// This is a text line
- /// ```
- Text(String),
- /// A link line
- ///
- /// # Examples
- ///
- /// ```gemini
- /// => /this-is-the-to This is the text
- ///
- /// => gemini://to.somewhere.link
- /// ```
- Link {
- /// The location that a link line is pointing to
- ///
- /// # Examples
- ///
- /// ```gemini
- /// => /this-is-the-to This is the text
- ///
- /// => gemini://to.somewhere.link
- /// ```
- to: String,
- /// The text a link line *may* have
- ///
- /// # Examples
- ///
- /// ```gemini
- /// => /this-is-the-to This line has text, unlike the next one.
- ///
- /// => gemini://to.somewhere.link
- /// ```
- text: Option<String>,
- },
- /// A heading line
- ///
- /// # Examples
- ///
- /// ```gemini
- /// # This is a heading
- ///
- /// ## This is a sub-heading
- ///
- /// ### This is a sub-sub-heading
- /// ```
- Heading {
- /// The level of a heading
- ///
- /// # Examples
- ///
- /// ```gemini
- /// # This is a level 1 heading
- ///
- /// ## This is a level 2 sub-heading
- ///
- /// ### This is a level 3 sub-sub-heading
- /// ```
- level: usize,
- /// The text of a heading
- ///
- /// # Examples
- ///
- /// ```gemini
- /// # This is the headings text
- ///
- /// # This is also the headings text
- /// ```
- text: String,
- },
- /// A collection of sequential list item lines
- ///
- /// # Examples
- ///
- /// ```gemini
- /// * These are
- /// * sequential list
- /// * items.
- /// ```
- List(Vec<String>),
- /// A blockquote line
- ///
- /// # Examples
- ///
- /// ```gemini
- /// > This is a blockquote line
- ///
- /// > This is also a blockquote line
- /// ```
- Blockquote(String),
- /// A preformatted block
- ///
- /// # Examples
- ///
- /// Try to ignore the leading backslash in-front of the triple backticks,
- /// they are there to not confuse the Markdown engine.
- ///
- /// ```gemini
- /// \```This is the alt-text
- /// This is the preformatted block
- ///
- /// This is the rest of the preformatted block
- /// \```
- /// ```
- PreformattedText {
- /// A preformatted blocks alt-text
- ///
- /// # Examples
- ///
- /// Try to ignore the leading backslash in-front of the triple backticks,
- /// they are there to not confuse the Markdown engine.
- ///
- /// ```gemini
- /// \```This is the alt-text
- /// This is the preformatted block
- ///
- /// This is the rest of the preformatted block
- /// \```
- /// ```
- alt_text: Option<String>,
- /// A preformatted blocks content
- ///
- /// # Examples
- ///
- /// Try to ignore the leading backslash in-front of the triple backticks,
- /// they are there to not confuse the Markdown engine.
- ///
- /// ```gemini
- /// \```This is the alt-text
- /// This is the preformatted blocks content
- ///
- /// This is the rest of the preformatted blocks content
- /// \```
- /// ```
- text: String,
- },
- /// A whitespace line, a line which contains nothing but whitespace.
- Whitespace,
-}
-
-/// An AST structure which contains an AST tree
-///
-/// # Example
-///
-/// ```rust
-/// let _ = germ::ast::Ast::from_string(r#"=> gemini://gem.rest/ GemRest"#);
-/// ```
-pub struct Ast {
- inner: Vec<Node>,
-}
-impl Ast {
- /// Build an AST tree from Gemtext.
- ///
- /// # Example
- ///
- /// ```rust
- /// let _ = germ::ast::Ast::from_string(r#"=> gemini://gem.rest/ GemRest"#);
- /// ```
- #[must_use]
- pub fn from_string(source: &str) -> Self {
- let mut ast = vec![];
- let mut in_preformatted = false;
- let mut in_list = false;
- let mut lines = source.lines();
-
- // Iterate over all lines in the Gemtext `source`
- while let Some(line) = lines.next() {
- // Evaluate the Gemtext line and append its AST node to the `ast` tree
- ast.append(&mut Self::evaluate(
- line,
- &mut lines,
- &mut in_preformatted,
- &mut in_list,
- ));
- }
-
- Self {
- inner: ast
- }
- }
-
- #[must_use]
- pub fn to_gemtext(&self) -> String {
- let mut gemtext = "".to_string();
-
- for node in &self.inner {
- match node {
- Node::Text(text) => gemtext.push_str(&format!("{}\n", text)),
- Node::Link {
- to,
- text,
- } =>
- gemtext.push_str(&format!(
- "=> {}{}\n",
- to,
- text
- .clone()
- .map_or_else(|| "".to_string(), |text| format!(" {}", text)),
- )),
- Node::Heading {
- level,
- text,
- } =>
- gemtext.push_str(&format!(
- "{} {}\n",
- match level {
- 1 => "#",
- 2 => "##",
- 3 => "###",
- _ => "",
- },
- text
- )),
- Node::List(items) =>
- gemtext.push_str(&format!(
- "{}\n",
- items
- .iter()
- .map(|i| format!("* {}", i))
- .collect::<Vec<String>>()
- .join("\n"),
- )),
- Node::Blockquote(text) => gemtext.push_str(&format!("> {}\n", text)),
- Node::PreformattedText {
- alt_text,
- text,
- } =>
- gemtext.push_str(&format!(
- "```{}\n{}```\n",
- alt_text.clone().unwrap_or_else(|| "".to_string()),
- text
- )),
- Node::Whitespace => gemtext.push('\n'),
- }
- }
-
- gemtext
- }
-
- /// The actual AST of `Ast`
- ///
- /// # Example
- ///
- /// ```rust
- /// let _ =
- /// germ::ast::Ast::from_string(r#"=> gemini://gem.rest/ GemRest"#).inner();
- /// ```
- #[must_use]
- pub const fn inner(&self) -> &Vec<Node> { &self.inner }
-
- #[allow(clippy::too_many_lines)]
- fn evaluate(
- line: &str,
- lines: &mut std::str::Lines<'_>,
- in_preformatted: &mut bool,
- in_list: &mut bool,
- ) -> Vec<Node> {
- let mut preformatted = String::new();
- let mut alt_text = String::new();
- let mut nodes = vec![];
- let mut line = line;
- let mut list_items = vec![];
-
- // Enter a not-so-infinite loop as sometimes, we may need to stay in an
- // evaluation loop, e.g., multiline contexts: preformatted text, lists, etc.
- loop {
- // Match the first character of the Gemtext line to understand the line
- // type
- match line.get(0..1).unwrap_or("") {
- "=" => {
- // If the Gemtext line starts with an "=" ("=>"), it is a link line,
- // so splitting it up should be easy enough.
- let line = line.get(2..).unwrap();
- let mut split = line
- .split_whitespace()
- .map(String::from)
- .collect::<Vec<String>>()
- .into_iter();
-
- nodes.push(Node::Link {
- to: split.next().expect("no location in link"),
- text: {
- let rest = split.collect::<Vec<String>>().join(" ");
-
- if rest.is_empty() {
- None
- } else {
- Some(rest)
- }
- },
- });
-
- break;
- }
- "#" => {
- // If the Gemtext line starts with an "#", it is a heading, so let's
- // find out how deep it goes.
- let level = match line.get(0..3) {
- Some(root) =>
- if root.contains("###") {
- 3
- } else if root.contains("##") {
- 2
- } else if root.contains('#') {
- 1
- } else {
- 0
- },
- None => 0,
- };
-
- nodes.push(Node::Heading {
- level,
- // Here, we are `get`ing the `&str` starting at the `level`-th
- // index, then trimming the start. These operations
- // effectively off the line identifier.
- text: line.get(level..).unwrap_or("").trim_start().to_string(),
- });
-
- break;
- }
- "*" => {
- // If the Gemtext line starts with an asterisk, it is a list item, so
- // let's enter a list context.
- if !*in_list {
- *in_list = true;
- }
-
- list_items.push(line.get(1..).unwrap_or("").trim_start().to_string());
-
- if let Some(next_line) = lines.next() {
- line = next_line;
- } else {
- break;
- }
- }
- ">" => {
- // If the Gemtext line starts with an ">", it is a blockquote, so
- // let's just clip off the line identifier.
- nodes.push(Node::Blockquote(
- line.get(1..).unwrap_or("").trim_start().to_string(),
- ));
-
- break;
- }
- "`" => {
- // If the Gemtext line starts with a backtick, it is a list item, so
- // let's enter a preformatted text context.
- *in_preformatted = !*in_preformatted;
-
- if *in_preformatted {
- alt_text = line.get(3..).unwrap_or("").to_string();
- line = lines.next().unwrap();
- } else {
- nodes.push(Node::PreformattedText {
- alt_text: if alt_text.is_empty() {
- None
- } else {
- Some(alt_text)
- },
- text: preformatted,
- });
-
- break;
- }
- }
- "" if !*in_preformatted => {
- // If the line has nothing on it, it is a whitespace line, as long as
- // we aren't in a preformatted line context.
- nodes.push(Node::Whitespace);
-
- break;
- }
- // This as a catchall, it does a number of things.
- _ => {
- if *in_preformatted {
- // If we are in a preformatted line context, add the line to the
- // preformatted blocks content and increment the line.
- preformatted.push_str(&format!("{}\n", line));
-
- line = lines.next().unwrap();
- } else {
- // If we are in a list item and hit a catchall, that must mean that
- // we encountered a line which is not a list line, so
- // let's stop adding items to the list context.
- if *in_list {
- *in_list = false;
-
- nodes.push(Node::Text(line.to_string()));
-
- break;
- }
-
- nodes.push(Node::Text(line.to_string()));
-
- break;
- }
- }
- }
- }
-
- if !list_items.is_empty() {
- nodes.reverse();
- nodes.push(Node::List(list_items));
- nodes.reverse();
- }
-
- nodes
- }
-}
diff --git a/src/convert.rs b/src/convert.rs
deleted file mode 100644
index c68696e..0000000
--- a/src/convert.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-//! Convert Gemtext into many types of markup.
-
-use crate::ast::Ast;
-
-mod html;
-mod markdown;
-
-/// Different targets to convert Gemtext to
-pub enum Target {
- /// Convert Gemtext to HTML
- HTML,
- /// Convert Gemtext to Markdown
- Markdown,
-}
-
-/// Convert AST'd Gemtext into an alternative markup format.
-///
-/// # Example
-///
-/// ```rust
-/// use germ::convert;
-///
-/// let _ = convert::from_ast(
-/// &germ::ast::Ast::from_string(r#"=> gemini://gem.rest/ GemRest"#),
-/// &convert::Target::HTML,
-/// );
-/// ```
-#[must_use]
-pub fn from_ast(source: &Ast, target: &Target) -> String {
- match target {
- Target::Markdown => markdown::convert(source.inner()),
- Target::HTML => html::convert(source.inner()),
- }
-}
-
-/// Convert raw Gemtext into an alternative markup format.
-///
-/// # Example
-///
-/// ```rust
-/// use germ::convert;
-///
-/// let _ = convert::from_string(
-/// r#"=> gemini://gem.rest/ GemRest"#,
-/// &convert::Target::HTML,
-/// );
-/// ```
-#[must_use]
-pub fn from_string(source: &str, target: &Target) -> String {
- from_ast(&Ast::from_string(source), target)
-}
diff --git a/src/convert/html.rs b/src/convert/html.rs
deleted file mode 100644
index 7b1cafe..0000000
--- a/src/convert/html.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-use crate::ast::Node;
-
-pub fn convert(source: &[Node]) -> String {
- let mut html = String::new();
-
- // Since we have an AST tree of the Gemtext, it is very easy to convert from
- // this AST tree to an alternative markup format.
- for node in source {
- match node {
- Node::Text(text) => html.push_str(&format!("<p>{}</p>", text)),
- Node::Link {
- to,
- text,
- } => {
- html.push_str(&format!(
- "<a href=\"{}\">{}</a><br>",
- to,
- text.clone().unwrap_or_else(|| to.clone())
- ));
- }
- Node::Heading {
- level,
- text,
- } => {
- html.push_str(&format!(
- "<{}>{}</{0}>",
- match level {
- 1 => "h1",
- 2 => "h2",
- 3 => "h3",
- _ => "p",
- },
- text
- ));
- }
- Node::List(items) =>
- html.push_str(&format!(
- "<ul>{}</ul>",
- items
- .iter()
- .map(|i| format!("<li>{}</li>", i))
- .collect::<Vec<String>>()
- .join("\n")
- )),
- Node::Blockquote(text) =>
- html.push_str(&format!("<blockquote>{}</blockquote>", text)),
- Node::PreformattedText {
- text, ..
- } => {
- html.push_str(&format!("<pre>{}</pre>", text));
- }
- Node::Whitespace => {}
- }
- }
-
- html
-}
diff --git a/src/convert/markdown.rs b/src/convert/markdown.rs
deleted file mode 100644
index a38da9f..0000000
--- a/src/convert/markdown.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-use crate::ast::Node;
-
-pub fn convert(source: &[Node]) -> String {
- let mut markdown = String::new();
-
- // Since we have an AST tree of the Gemtext, it is very easy to convert from
- // this AST tree to an alternative markup format.
- for node in source {
- match node {
- Node::Text(text) => markdown.push_str(&format!("{}\n", text)),
- Node::Link {
- to,
- text,
- } =>
- markdown.push_str(&*text.clone().map_or_else(
- || format!("<{}>\n", to),
- |text| format!("[{}]({})\n", text, to),
- )),
- Node::Heading {
- level,
- text,
- } => {
- markdown.push_str(&format!(
- "{} {}\n",
- match level {
- 1 => "#",
- 2 => "##",
- 3 => "###",
- _ => "",
- },
- text
- ));
- }
- Node::List(items) =>
- markdown.push_str(&format!(
- "{}\n",
- items
- .iter()
- .map(|i| format!("- {}", i))
- .collect::<Vec<String>>()
- .join("\n"),
- )),
- Node::Blockquote(text) => markdown.push_str(&format!("> {}\n", text)),
- Node::PreformattedText {
- alt_text,
- text,
- } => {
- markdown.push_str(&format!(
- "```{}\n{}```\n",
- alt_text.clone().unwrap_or_else(|| "".to_string()),
- text
- ));
- }
- Node::Whitespace => markdown.push('\n'),
- }
- }
-
- markdown
-}
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index 83aef0e..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-#![deny(
- warnings,
- nonstandard_style,
- unused,
- future_incompatible,
- rust_2018_idioms,
- unsafe_code,
- clippy::all,
- clippy::nursery,
- clippy::pedantic
-)]
-#![doc = include_str!("../README.md")]
-#![recursion_limit = "128"]
-
-#[cfg(feature = "ast")]
-pub mod ast;
-
-#[cfg(feature = "convert")]
-pub mod convert;
-
-#[cfg(feature = "request")]
-pub mod request;
-
-#[cfg(feature = "meta")]
-pub mod meta;
diff --git a/src/meta.rs b/src/meta.rs
deleted file mode 100644
index f8a9dfb..0000000
--- a/src/meta.rs
+++ /dev/null
@@ -1,162 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-use std::collections::HashMap;
-
-/// Structure-ize a Gemini response's meta section into it's mime type and it's
-/// parameters.
-#[derive(Debug, Default)]
-pub struct Meta {
- /// The mime type of a Gemini response
- mime: String,
- /// The parameters of a Gemini response
- parameters: HashMap<String, String>,
-}
-impl ToString for Meta {
- /// Convert a `Meta` into a `String`
- ///
- /// # Example
- /// ```rust
- /// let original_string = "text/gemini; hi=2; hi2=string=2";
- ///
- /// assert_eq!(
- /// germ::meta::Meta::from_string(original_string).to_string(),
- /// original_string
- /// );
- /// ```
- fn to_string(&self) -> String {
- format!("{}{}", self.mime, {
- let mut parameters = self
- .parameters
- .iter()
- .map(|(k, v)| format!("{}={}", *k, v))
- .collect::<Vec<_>>();
-
- parameters.sort();
- parameters.reverse();
-
- if parameters.is_empty() {
- "".to_string()
- } else {
- format!("; {}", parameters.join("; "))
- }
- })
- }
-}
-impl Meta {
- /// Create a new `Meta`
- ///
- /// # Example
- ///
- /// ```rust
- /// let mut meta = germ::meta::Meta::new();
- /// ```
- #[must_use]
- pub fn new() -> Self { Self::default() }
-
- /// Create a `Meta` from a string
- ///
- /// # Example
- ///
- /// ```rust
- /// assert_eq!(
- /// germ::meta::Meta::from_string("text/gemini; hi=2; hi2=string=2").mime(),
- /// "text/gemini",
- /// );
- /// ```
- #[must_use]
- pub fn from_string(meta: &str) -> Self {
- let mut metas = meta.split(';');
- let mime = metas.next().unwrap_or("").to_string();
- let mut parameters = HashMap::new();
-
- for parameter in metas {
- let key_value = parameter
- .trim_start()
- .split_at(parameter.find('=').unwrap_or(0));
-
- parameters.insert(
- key_value.0.to_string().replace('=', ""),
- key_value.1.to_string(),
- );
- }
-
- Self {
- mime,
- parameters,
- }
- }
-
- /// Obtain non-mutable access to the mime of the `Meta`
- ///
- /// # Example
- ///
- /// ```rust
- /// assert_eq!(
- /// germ::meta::Meta::from_string("text/gemini; hi=2; hi2=string=2").mime(),
- /// "text/gemini",
- /// );
- /// ```
- #[must_use]
- pub fn mime(&self) -> &str { &self.mime }
-
- /// Obtain mutable access to the mime of the `Meta`
- ///
- /// # Example
- ///
- /// ```rust
- /// let mut meta = germ::meta::Meta::new();
- ///
- /// *meta.mime_mut() = "text/gemini".to_string();
- /// ```
- pub fn mime_mut(&mut self) -> &mut String { &mut self.mime }
-
- /// Obtain non-mutable access to the parameters of the `Meta`
- ///
- /// # Example
- ///
- /// ```rust
- /// assert_eq!(
- /// germ::meta::Meta::from_string("text/gemini; hi=2; hi2=string=2")
- /// .parameters()
- /// .get("hi2"),
- /// Some(&"string=2".to_string()),
- /// );
- /// ```
- #[must_use]
- pub const fn parameters(&self) -> &HashMap<String, String> {
- &self.parameters
- }
-
- /// Obtain mutable access to the parameters of the `Meta`
- ///
- /// # Example
- ///
- /// ```rust
- /// let mut meta = germ::meta::Meta::new();
- /// let mut parameters = std::collections::HashMap::new();
- ///
- /// parameters.insert("hi".to_string(), "2".to_string());
- /// parameters.insert("hi2".to_string(), "string=2".to_string());
- ///
- /// *meta.parameters_mut() = parameters;
- /// ```
- pub fn parameters_mut(&mut self) -> &mut HashMap<String, String> {
- &mut self.parameters
- }
-}
diff --git a/src/request.rs b/src/request.rs
deleted file mode 100644
index 07d3552..0000000
--- a/src/request.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-//! Make Gemini requests and get sane, structured results
-
-mod response;
-mod status;
-mod verifier;
-
-use std::io::{Read, Write};
-
-pub use response::Response;
-pub use status::Status;
-use verifier::GermVerifier;
-
-/// Make a request to a Gemini server. The `url` **should** be prefixed with a
-/// scheme (e.g. "gemini://").
-///
-/// # Example
-///
-/// ```rust
-/// match germ::request::request(&url::Url::parse("gemini://fuwn.me").unwrap()) {
-/// Ok(response) => println!("{:?}", response),
-/// Err(_) => {}
-/// }
-/// ```
-///
-/// # Errors
-/// - May error if the URL is invalid
-/// - May error if the TLS write fails
-/// - May error if the TLS read fails
-pub fn request(url: &url::Url) -> anyhow::Result<Response> {
- let config = rustls::ClientConfig::builder()
- .with_safe_defaults()
- .with_custom_certificate_verifier(std::sync::Arc::new(GermVerifier::new()))
- .with_no_client_auth();
- let mut connection = rustls::ClientConnection::new(
- std::sync::Arc::new(config),
- url.domain().unwrap_or("").try_into()?,
- )?;
- let mut stream = std::net::TcpStream::connect(format!(
- "{}:{}",
- url.domain().unwrap_or(""),
- url.port().unwrap_or(1965)
- ))?;
- let mut tls = rustls::Stream::new(&mut connection, &mut stream);
-
- tls.write_all(format!("{}\r\n", url).as_bytes())?;
-
- let mut plain_text = Vec::new();
-
- tls.read_to_end(&mut plain_text)?;
-
- Ok(Response::new(
- &plain_text,
- tls.conn.negotiated_cipher_suite(),
- ))
-}
diff --git a/src/request/response.rs b/src/request/response.rs
deleted file mode 100644
index 5e1f436..0000000
--- a/src/request/response.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-use rustls::SupportedCipherSuite;
-
-use crate::request::Status;
-
-#[derive(Debug)]
-pub struct Response {
- status: Status,
- meta: String,
- content: Option<String>,
- size: usize,
- suite: Option<SupportedCipherSuite>,
-}
-impl Response {
- pub(super) fn new(data: &[u8], suite: Option<SupportedCipherSuite>) -> Self {
- let string_form = String::from_utf8_lossy(data).to_string();
- let mut content = None;
- let header;
-
- if string_form.ends_with("\r\n") {
- header = string_form;
- } else {
- let mut string_split = string_form.split("\r\n");
-
- header = string_split.next().unwrap_or("").to_string();
- content = Some(string_split.collect());
- }
-
- let header_split = header.split_at(2);
-
- Self {
- status: Status::from(header_split.0.parse::<i32>().unwrap_or(0)),
- meta: header_split.1.trim_start().to_string(),
- content,
- size: data.len(),
- suite,
- }
- }
-
- #[must_use]
- pub const fn status(&self) -> &Status { &self.status }
-
- #[must_use]
- pub fn meta(&self) -> &str { &self.meta }
-
- #[must_use]
- pub const fn content(&self) -> &Option<String> { &self.content }
-
- #[must_use]
- pub const fn size(&self) -> &usize { &self.size }
-
- #[must_use]
- pub const fn suite(&self) -> &Option<SupportedCipherSuite> { &self.suite }
-}
diff --git a/src/request/status.rs b/src/request/status.rs
deleted file mode 100644
index f46059a..0000000
--- a/src/request/status.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-use std::{fmt, fmt::Formatter};
-
-/// Simple Gemini status reporting
-///
-/// # Examples
-///
-/// ```rust
-/// use germ::request::Status;
-///
-/// assert_eq!(Status::from(10), Status::Input);
-/// assert_eq!(i32::from(Status::Input), 10);
-/// ```
-#[derive(Debug, PartialEq)]
-pub enum Status {
- Input,
- SensitiveInput,
- Success,
- TemporaryRedirect,
- PermanentRedirect,
- TemporaryFailure,
- ServerUnavailable,
- CGIError,
- ProxyError,
- SlowDown,
- PermanentFailure,
- NotFound,
- Gone,
- ProxyRefused,
- BadRequest,
- ClientCertificateRequired,
- CertificateNotAuthorised,
- CertificateNotValid,
- Unsupported,
-}
-impl Default for Status {
- fn default() -> Self { Self::Success }
-}
-impl From<Status> for i32 {
- fn from(n: Status) -> Self {
- match n {
- Status::Input => 10,
- Status::SensitiveInput => 11,
- Status::Success => 20,
- Status::TemporaryRedirect => 30,
- Status::PermanentRedirect => 31,
- Status::TemporaryFailure => 40,
- Status::ServerUnavailable => 41,
- Status::CGIError => 42,
- Status::ProxyError => 43,
- Status::SlowDown => 44,
- Status::PermanentFailure => 50,
- Status::NotFound => 51,
- Status::Gone => 52,
- Status::ProxyRefused => 53,
- Status::BadRequest => 59,
- Status::ClientCertificateRequired => 60,
- Status::CertificateNotAuthorised => 61,
- Status::CertificateNotValid => 62,
- Status::Unsupported => 0,
- }
- }
-}
-impl From<i32> for Status {
- fn from(n: i32) -> Self {
- match n {
- 10 => Self::Input,
- 11 => Self::SensitiveInput,
- 20 => Self::Success,
- 30 => Self::TemporaryRedirect,
- 31 => Self::PermanentRedirect,
- 40 => Self::TemporaryFailure,
- 41 => Self::ServerUnavailable,
- 42 => Self::CGIError,
- 43 => Self::ProxyError,
- 44 => Self::SlowDown,
- 50 => Self::PermanentFailure,
- 51 => Self::NotFound,
- 52 => Self::Gone,
- 53 => Self::ProxyRefused,
- 59 => Self::BadRequest,
- 60 => Self::ClientCertificateRequired,
- 61 => Self::CertificateNotAuthorised,
- 62 => Self::CertificateNotValid,
- _ => Self::Unsupported,
- }
- }
-}
-impl fmt::Display for Status {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "{:?}", self)
- }
-}
diff --git a/src/request/verifier.rs b/src/request/verifier.rs
deleted file mode 100644
index d6511c3..0000000
--- a/src/request/verifier.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-// This file is part of Germ <https://github.com/gemrest/germ>.
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-// Copyright (C) 2022-2022 Fuwn <[email protected]>
-// SPDX-License-Identifier: GPL-3.0-only
-
-use std::time::SystemTime;
-
-use rustls::{client, client::ServerCertVerified, Certificate};
-
-pub(super) struct GermVerifier;
-impl GermVerifier {
- pub const fn new() -> Self { Self {} }
-}
-impl client::ServerCertVerifier for GermVerifier {
- fn verify_server_cert(
- &self,
- _end_entity: &Certificate,
- _intermediates: &[Certificate],
- _server_name: &client::ServerName,
- _scts: &mut dyn Iterator<Item = &[u8]>,
- _ocsp_response: &[u8],
- _now: SystemTime,
- ) -> Result<ServerCertVerified, rustls::Error> {
- Ok(ServerCertVerified::assertion())
- }
-}