From c6956ad7299d6fe01e8c2fb524fb4c79ac7b418d Mon Sep 17 00:00:00 2001 From: Awiteb Date: Mon, 12 Aug 2024 20:11:42 +0000 Subject: [PATCH] feat: New `SimpleGenerator` to generate the captcha Signed-off-by: Awiteb --- src/captcha_gen.rs | 40 ----------- src/captcha_gen/mod.rs | 27 +++++++ src/captcha_gen/simple_generator.rs | 107 ++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 40 deletions(-) delete mode 100644 src/captcha_gen.rs create mode 100644 src/captcha_gen/mod.rs create mode 100644 src/captcha_gen/simple_generator.rs diff --git a/src/captcha_gen.rs b/src/captcha_gen.rs deleted file mode 100644 index dbb48da..0000000 --- a/src/captcha_gen.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2024, Awiteb -// A captcha middleware for Salvo framework. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -use crate::{CaptchaDifficulty, CaptchaName, CaptchaStorage}; - -/// Captcha generator, used to generate a new captcha image. This trait are implemented for all [`CaptchaStorage`]. -pub trait CaptchaGenerator: CaptchaStorage { - /// Create a new captcha image and return the token and the image encoded as png. Will return None if the captcha crate failed to create the captcha. - /// - /// The returned captcha image is 220x110 pixels. - /// - /// For more information about the captcha name and difficulty, see the [`README.md`](https://git.4rs.nl/awiteb/salvo-captcha/#captcha-name-and-difficulty). - fn new_captcha( - &self, - name: CaptchaName, - difficulty: CaptchaDifficulty, - ) -> impl std::future::Future)>, Self::Error>> + Send - { - async { - let Some((captcha_answer, captcha_image)) = - captcha::by_name(difficulty, name).as_tuple() - else { - return Ok(None); - }; - - let token = self.store_answer(captcha_answer).await?; - Ok(Some((token, captcha_image))) - } - } -} - -impl CaptchaGenerator for T where T: CaptchaStorage {} diff --git a/src/captcha_gen/mod.rs b/src/captcha_gen/mod.rs new file mode 100644 index 0000000..7241ace --- /dev/null +++ b/src/captcha_gen/mod.rs @@ -0,0 +1,27 @@ +// Copyright (c) 2024, Awiteb +// A captcha middleware for Salvo framework. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#[cfg(feature = "simple_generator")] +mod simple_generator; + +#[cfg(feature = "simple_generator")] +pub use simple_generator::*; + +/// Captcha generator, used to generate a new captcha image and answer. +pub trait CaptchaGenerator: Send { + /// The error type of the captcha generator + type Error: std::error::Error; + + /// Create a new captcha image and return the answer and the image encoded as png + fn new_captcha( + &self, + ) -> impl std::future::Future), Self::Error>> + Send; +} diff --git a/src/captcha_gen/simple_generator.rs b/src/captcha_gen/simple_generator.rs new file mode 100644 index 0000000..04845c1 --- /dev/null +++ b/src/captcha_gen/simple_generator.rs @@ -0,0 +1,107 @@ +// Copyright (c) 2024, Awiteb +// A captcha middleware for Salvo framework. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +use crate::CaptchaGenerator; + +use std::fmt::Display; + +/// Supported captcha names +/// +/// See [`README.md`](https://git.4rs.nl/awiteb/salvo-captcha/#captcha-name-and-difficulty) for more information. +#[derive(Debug, Clone, Copy)] +pub enum CaptchaName { + /// Plain text, without any distortion + Normal, + /// Slightly twisted text + SlightlyTwisted, + /// Very twisted text + VeryTwisted, +} + +/// Supported captcha difficulties +/// +/// See [`README.md`](https://git.4rs.nl/awiteb/salvo-captcha/#captcha-name-and-difficulty) for more information. +#[derive(Debug, Clone, Copy)] +pub enum CaptchaDifficulty { + /// Easy to read text + Easy, + /// Medium difficulty text + Medium, + /// Hard to read text + Hard, +} + +impl From for captcha::CaptchaName { + /// Function to convert the [`CaptchaName`] to the [`captcha::CaptchaName`] + fn from(value: CaptchaName) -> Self { + match value { + CaptchaName::Normal => Self::Lucy, + CaptchaName::SlightlyTwisted => Self::Amelia, + CaptchaName::VeryTwisted => Self::Mila, + } + } +} + +impl From for captcha::Difficulty { + /// Function to convert the [`CaptchaDifficulty`] to the [`captcha::Difficulty`] + fn from(value: CaptchaDifficulty) -> captcha::Difficulty { + match value { + CaptchaDifficulty::Easy => Self::Easy, + CaptchaDifficulty::Medium => Self::Medium, + CaptchaDifficulty::Hard => Self::Hard, + } + } +} + +#[derive(Debug)] +/// Error type for the [`SimpleGenerator`] +pub enum SimpleGeneratorError { + /// Failed to encode the captcha to png image + FaildEncodedToPng, +} + +impl Display for SimpleGeneratorError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Faild to encode the captcha to png image") + } +} + +impl std::error::Error for SimpleGeneratorError {} + +/// The simple captcha generator +pub struct SimpleGenerator { + name: CaptchaName, + difficulty: CaptchaDifficulty, +} + +impl SimpleGenerator { + /// Create new [`SimpleGenerator`] instance + pub const fn new(name: CaptchaName, difficulty: CaptchaDifficulty) -> Self { + Self { name, difficulty } + } +} + +impl CaptchaGenerator for SimpleGenerator { + type Error = SimpleGeneratorError; + + /// The returned captcha image is 220x110 pixels in png format. + /// + /// For more information about the captcha name and difficulty, see the [`README.md`](https://git.4rs.nl/awiteb/salvo-captcha/#captcha-name-and-difficulty). + async fn new_captcha(&self) -> Result<(String, Vec), Self::Error> { + let Some((captcha_answer, captcha_image)) = + captcha::by_name(self.difficulty.into(), self.name.into()).as_tuple() + else { + return Err(SimpleGeneratorError::FaildEncodedToPng); + }; + + Ok((captcha_answer, captcha_image)) + } +}