1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
// Enable all clippy lints and enforce, and opt out of individual lints #![cfg_attr(feature = "cargo-clippy", warn(clippy::cargo, clippy::pedantic, clippy::nursery))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::module_name_repetitions, clippy::non_ascii_literal))] //! To use this crate, simply call one of the `validate*` functions, depending on your use case and desired return type: //! //! - [`validate(S) -> bool`] //! - [`validate_nom(S) -> nom::IResult<&str, &str>`] //! - [`validate_opt(S) -> Option<&str>`] //! - [`validate_res(S) -> anyhow::Result<&str>`] //! //! Where `S` is some type that implements [`Into<&str>`]; for example, [`&str`] itself! //! //! All functions except the [`_nom`] variant will additionally ensure that the entire input is consumed and //! matches exactly what was parsed internally. If you require that this check is not done (e.g. as part of a larger //! parsing exercise), then use the [`_nom`] variant. //! //! [`validate(S) -> bool`]: fn.validate.html //! [`validate_nom(S) -> nom::IResult<&str, &str>`]: fn.validate_nom.html //! [`validate_opt(S) -> Option<&str>`]: fn.validate_opt.html //! [`validate_res(S) -> anyhow::Result<&str>`]: fn.validate_res.html //! [`Into<&str>`]: https://doc.rust-lang.org/std/convert/trait.Into.html //! [`&str`]: https://doc.rust-lang.org/std/primitive.str.html //! [`_nom`]: fn.validate_nom.html use anyhow::Context; mod combinators; mod parsers; /// Validate the given URI against [RFC2396], returning a [`bool`] indicating its validity. /// /// Entire input consumption is also checked as part of validation. /// /// # Examples /// /// ```rust /// let is_valid = rfc2396::validate("https://example.com"); /// assert!(is_valid); /// ``` /// /// [RFC2396]: https://tools.ietf.org/html/rfc2396 /// [`bool`]: https://doc.rust-lang.org/std/primitive.bool.html pub fn validate<'a, S>(uri: S) -> bool where S: Into<&'a str>, { let input = uri.into(); match crate::parsers::uri_reference(input) { Ok((_, matched)) => input == matched, Err(_) => false, } } /// Validate the given URI against [RFC2396], returning a [`nom::IResult<&str, &str>`] indicating its validity. /// /// # Examples /// /// ```rust /// let validation = rfc2396::validate_nom("https://example.com"); /// assert_eq!(validation, nom::IResult::Ok(("", "https://example.com"))); /// ``` /// /// # Errors /// /// If validation fails, the raw, default [`nom::IResult`] generated by [`nom`] will be returned. /// /// [RFC2396]: https://tools.ietf.org/html/rfc2396 /// [`nom::IResult<&str, &str>`]: https://docs.rs/nom/6.0.1/nom/type.IResult.html /// [`nom::IResult`]: https://docs.rs/nom/6.0.1/nom/type.IResult.html /// [`nom`]: https://crates.io/crates/nom pub fn validate_nom<'a, S>(uri: S) -> nom::IResult<&'a str, &'a str> where S: Into<&'a str>, { crate::parsers::uri_reference(uri.into()) } /// Validate the given URI against [RFC2396], returning an [`Option<&str>`] indicating its validity. /// /// Entire input consumption is also checked as part of validation. The embedded [`&str`] is the input. /// /// # Examples /// /// ```rust /// let valid_uri = rfc2396::validate_opt("https://example.com"); /// assert_eq!(valid_uri, Some("https://example.com")); /// ``` /// /// [RFC2396]: https://tools.ietf.org/html/rfc2396 /// [`Option<&str>`]: https://doc.rust-lang.org/std/option/enum.Option.html /// [`&str`]: https://doc.rust-lang.org/std/primitive.str.html pub fn validate_opt<'a, S>(uri: S) -> Option<&'a str> where S: Into<&'a str>, { let input = uri.into(); match crate::parsers::uri_reference(input) { Ok((_, matched)) if matched == input => Some(input), _ => None, } } /// Validate the given URI against [RFC2396], returning an [`anyhow::Result<&str>`] indicating its validity. /// /// Entire input consumption is also checked as part of validation. The embedded [`&str`] is the input. /// /// # Examples /// /// ```rust /// # /// <https://stackoverflow.com/a/58119924> /// # fn type_of<T>(_: &T) -> &str { /// # std::any::type_name::<T>() /// # } /// # /// # fn main() -> anyhow::Result<()> { /// let validation = rfc2396::validate_res("https://example.com"); /// assert_eq!(type_of(&validation), std::any::type_name::<anyhow::Result<&str>>()); /// assert_eq!(validation?, "https://example.com"); /// # /// # Ok(()) /// # } /// ``` /// /// # Errors /// /// If validation fails, a default [`nom::IResult`] converted into an [`anyhow::Result`] with additional context added /// by [`anyhow`] will be returned. /// /// [RFC2396]: https://tools.ietf.org/html/rfc2396 /// [`anyhow::Result<&str>`]: https://docs.rs/anyhow/1.0.36/anyhow/type.Result.html /// [`&str`]: https://doc.rust-lang.org/std/primitive.str.html /// [`nom::IResult`]: https://docs.rs/nom/6.0.1/nom/type.IResult.html /// [`anyhow::Result`]: https://docs.rs/anyhow/1.0.36/anyhow/type.Result.html /// [`anyhow`]:https://crates.io/crates/anyhow pub fn validate_res<'a, S>(uri: S) -> anyhow::Result<&'a str> where S: Into<&'a str>, { let input = uri.into(); (match crate::parsers::uri_reference(input) { Ok((_, matched)) => { if matched == input { Ok(input) } else { Err(anyhow::anyhow!("URI matched by parser (`{}`) does not equal input (`{}`)", matched, input)) } } Err(error) => Err(anyhow::anyhow!(error.map(|mut e| e.input = input))), }) .context("failed to parse URI") }