diff --git a/src/types/document.rs b/src/types/document.rs index e62eecb..3d65194 100644 --- a/src/types/document.rs +++ b/src/types/document.rs @@ -3,6 +3,7 @@ use std::fmt; use itertools::Itertools; use crate::types::URIReference; +use crate::util::Cowy; #[derive(Default)] pub struct Document { @@ -42,7 +43,7 @@ impl Document { self } - pub fn add_link<'a, U>(&mut self, uri: U, label: impl AsRef + Into) -> &mut Self + pub fn add_link<'a, U>(&mut self, uri: U, label: impl Cowy) -> &mut Self where U: TryInto>, { @@ -92,7 +93,7 @@ impl Document { self } - pub fn add_heading(&mut self, level: HeadingLevel, text: impl AsRef + Into) -> &mut Self { + pub fn add_heading(&mut self, level: HeadingLevel, text: impl Cowy) -> &mut Self { let text = HeadingText::new_lossy(text); let heading = Heading { level, @@ -182,7 +183,7 @@ impl Text { Self::default() } - pub fn new_lossy(line: impl AsRef + Into) -> Self { + pub fn new_lossy(line: impl Cowy) -> Self { Self(lossy_escaped_line(line, SPECIAL_STARTS)) } } @@ -195,7 +196,7 @@ pub struct Link { pub struct LinkLabel(String); impl LinkLabel { - pub fn from_lossy(line: impl AsRef + Into) -> Self { + pub fn from_lossy(line: impl Cowy) -> Self { let line = strip_newlines(line); LinkLabel(line) @@ -210,7 +211,7 @@ pub struct Preformatted { pub struct PreformattedText(String); impl PreformattedText { - pub fn new_lossy(line: impl AsRef + Into) -> Self { + pub fn new_lossy(line: impl Cowy) -> Self { Self(lossy_escaped_line(line, &[PREFORMATTED_TOGGLE_START])) } } @@ -248,7 +249,7 @@ impl Heading { pub struct HeadingText(String); impl HeadingText { - pub fn new_lossy(line: impl AsRef + Into) -> Self { + pub fn new_lossy(line: impl Cowy) -> Self { let line = strip_newlines(line); Self(lossy_escaped_line(line, &[HEADING_START])) @@ -298,7 +299,7 @@ fn starts_with_any(s: &str, starts: &[&str]) -> bool { false } -fn lossy_escaped_line(line: impl AsRef + Into, escape_starts: &[&str]) -> String { +fn lossy_escaped_line(line: impl Cowy, escape_starts: &[&str]) -> String { let line_ref = line.as_ref(); let contains_newline = line_ref.contains('\n'); let has_special_start = starts_with_any(line_ref, escape_starts); @@ -320,7 +321,7 @@ fn lossy_escaped_line(line: impl AsRef + Into, escape_starts: &[&st line } -fn strip_newlines(text: impl AsRef + Into) -> String { +fn strip_newlines(text: impl Cowy) -> String { if !text.as_ref().contains(&['\r', '\n'][..]) { return text.into(); } diff --git a/src/types/meta.rs b/src/types/meta.rs index 144fc13..ccc17ba 100644 --- a/src/types/meta.rs +++ b/src/types/meta.rs @@ -1,5 +1,7 @@ use anyhow::*; -use mime::Mime; +use crate::Mime; +use crate::util::Cowy; + #[derive(Debug,Clone,PartialEq,Eq,Default)] pub struct Meta(String); @@ -9,7 +11,7 @@ impl Meta { /// Creates a new "Meta" string. /// Fails if `meta` contains `\n`. - pub fn new(meta: impl AsRef + Into) -> Result { + pub fn new(meta: impl Cowy) -> Result { ensure!(!meta.as_ref().contains("\n"), "Meta must not contain newlines"); ensure!(meta.as_ref().len() <= Self::MAX_LEN, "Meta must not exceed {} bytes", Self::MAX_LEN); @@ -20,7 +22,7 @@ impl Meta { /// Truncates `meta` to before: /// - the first occurrence of `\n` /// - the character that makes `meta` exceed `Meta::MAX_LEN` - pub fn new_lossy(meta: impl AsRef + Into) -> Self { + pub fn new_lossy(meta: impl Cowy) -> Self { let meta = meta.as_ref(); let truncate_pos = meta.char_indices().position(|(i, ch)| { let is_newline = ch == '\n'; diff --git a/src/types/response.rs b/src/types/response.rs index 6a24de7..a76d6a1 100644 --- a/src/types/response.rs +++ b/src/types/response.rs @@ -1,5 +1,6 @@ use anyhow::*; use crate::types::{ResponseHeader, Body, Mime, Document}; +use crate::util::Cowy; use crate::GEMINI_MIME; pub struct Response { @@ -19,12 +20,12 @@ impl Response { Self::success(&GEMINI_MIME).with_body(document) } - pub fn input(prompt: impl AsRef + Into) -> Result { + pub fn input(prompt: impl Cowy) -> Result { let header = ResponseHeader::input(prompt)?; Ok(Self::new(header)) } - pub fn input_lossy(prompt: impl AsRef + Into) -> Self { + pub fn input_lossy(prompt: impl Cowy) -> Self { let header = ResponseHeader::input_lossy(prompt); Self::new(header) } @@ -34,7 +35,7 @@ impl Response { Self::new(header) } - pub fn server_error(reason: impl AsRef + Into) -> Result { + pub fn server_error(reason: impl Cowy) -> Result { let header = ResponseHeader::server_error(reason)?; Ok(Self::new(header)) } diff --git a/src/types/response_header.rs b/src/types/response_header.rs index 8307707..824401e 100644 --- a/src/types/response_header.rs +++ b/src/types/response_header.rs @@ -1,5 +1,6 @@ use anyhow::*; -use mime::Mime; +use crate::Mime; +use crate::util::Cowy; use crate::types::{Status, Meta}; #[derive(Debug,Clone)] @@ -9,14 +10,14 @@ pub struct ResponseHeader { } impl ResponseHeader { - pub fn input(prompt: impl AsRef + Into) -> Result { + pub fn input(prompt: impl Cowy) -> Result { Ok(Self { status: Status::INPUT, meta: Meta::new(prompt).context("Invalid input prompt")?, }) } - pub fn input_lossy(prompt: impl AsRef + Into) -> Self { + pub fn input_lossy(prompt: impl Cowy) -> Self { Self { status: Status::INPUT, meta: Meta::new_lossy(prompt), @@ -30,14 +31,14 @@ impl ResponseHeader { } } - pub fn server_error(reason: impl AsRef + Into) -> Result { + pub fn server_error(reason: impl Cowy) -> Result { Ok(Self { status: Status::PERMANENT_FAILURE, meta: Meta::new(reason).context("Invalid server error reason")?, }) } - pub fn server_error_lossy(reason: impl AsRef + Into) -> Self { + pub fn server_error_lossy(reason: impl Cowy) -> Self { Self { status: Status::PERMANENT_FAILURE, meta: Meta::new_lossy(reason), diff --git a/src/util.rs b/src/util.rs index bd7a28c..4382723 100644 --- a/src/util.rs +++ b/src/util.rs @@ -102,3 +102,19 @@ pub fn guess_mime_from_path>(path: P) -> Mime { mime.parse::().unwrap_or(mime::APPLICATION_OCTET_STREAM) } + +/// A convenience trait alias for `AsRef + Into`, +/// most commonly used to accept `&str` or `String`: +/// +/// `Cowy` ⇔ `AsRef + Into` +pub trait Cowy +where + Self: AsRef + Into, + T: ToOwned + ?Sized, +{} + +impl Cowy for C +where + C: AsRef + Into, + T: ToOwned + ?Sized, +{}