diff --git a/db/db-core/.gitignore b/db/db-core/.gitignore new file mode 100644 index 0000000..fedaa2b --- /dev/null +++ b/db/db-core/.gitignore @@ -0,0 +1,2 @@ +/target +.env diff --git a/db/db-core/Cargo.lock b/db/db-core/Cargo.lock new file mode 100644 index 0000000..aba2809 --- /dev/null +++ b/db/db-core/Cargo.lock @@ -0,0 +1,122 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "async-trait" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "db-core" +version = "0.1.0" +dependencies = [ + "async-trait", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "proc-macro2" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" diff --git a/db/db-core/Cargo.toml b/db/db-core/Cargo.toml new file mode 100644 index 0000000..6d3c04f --- /dev/null +++ b/db/db-core/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "db-core" +version = "0.1.0" +edition = "2021" +homepage = "https://github.com/forgeflux-org/starchart" +repository = "https://github.com/forgeflux-org/starchart" +documentation = "https://github.con/forgeflux-org/starchart" +readme = "https://github.com/forgeflux-org/starchart/blob/master/README.md" +license = "AGPLv3 or later version" +authors = ["Aravinth Manivannan "] + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-trait = "0.1.51" +thiserror = "1.0.30" +serde = { version = "1", features = ["derive"]} + +[features] +default = [] +test = [] + +[dev-dependencies] +serde_json = "1" diff --git a/db/db-core/src/errors.rs b/db/db-core/src/errors.rs new file mode 100644 index 0000000..0925874 --- /dev/null +++ b/db/db-core/src/errors.rs @@ -0,0 +1,48 @@ +/* + * ForgeFlux StarChart - A federated software forge spider + * Copyright (C) 2022 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +//! represents all the ways a trait can fail using this crate +use std::error::Error as StdError; + +//use derive_more::{error, Error as DeriveError}; +use thiserror::Error; + +/// Error data structure grouping various error subtypes +#[derive(Debug, Error)] +pub enum DBError { + /// DNS challenge value is already taken + #[error("DNS challenge is already taken")] + DuplicateChallengeText, + + /// DNS challenge hostname is already taken + #[error("DNS challenge hostname is already taken")] + DuplicateChallengeHostname, + + /// forge instance type is unknown + #[error("Unknown forge instance specifier {}", _0)] + UnknownForgeType(String), + + /// errors that are specific to a database implementation + #[error("{0}")] + DBError(#[source] BoxDynError), +} + +/// Convenience type alias for grouping driver-specific errors +pub type BoxDynError = Box; + +/// Generic result data structure +pub type DBResult = std::result::Result; diff --git a/db/db-core/src/lib.rs b/db/db-core/src/lib.rs new file mode 100644 index 0000000..cd2646c --- /dev/null +++ b/db/db-core/src/lib.rs @@ -0,0 +1,136 @@ +/* + * ForgeFlux StarChart - A federated software forge spider + * Copyright (C) 2022 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#![warn(missing_docs)] +//! # `Starchart` database operations +//! +//! Traits and datastructures used in Starchart to interact with database. +//! +//! To use an unsupported database with Starchart, traits present within this crate should be +//! implemented. +//! +//! +//! ## Organisation +//! +//! Database functionallity is divided accross various modules: +//! +//! - [errors](crate::auth): error data structures used in this crate +//! - [ops](crate::ops): meta operations like connection pool creation, migrations and getting +//! connection from pool +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; + +pub mod errors; +pub mod ops; +#[cfg(feature = "test")] +pub mod tests; + +pub use ops::GetConnection; + +pub mod prelude { + //! useful imports for users working with a supported database + + pub use super::errors::*; + pub use super::ops::*; + pub use super::*; +} + +pub mod dev { + //! useful imports for supporting a new database + pub use super::prelude::*; + pub use async_trait::async_trait; +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +/// create a new forge on the database +pub struct CreateForge<'a> { + /// hostname of the forge instance: with scheme but remove trailing slash + pub hostname: &'a str, + /// forge type: which software is the instance running? + pub forge_type: ForgeImplementation, +} + +use dev::*; +#[async_trait] +/// Starchart's database requirements. To implement support for $Database, kindly implement this +/// trait. +pub trait SCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase { + /// ping DB + async fn ping(&self) -> bool; + + /// create forge isntance + async fn create_forge_isntance(&self, f: &CreateForge) -> DBResult<()>; + + /// delete forge isntance + async fn delete_forge_instance(&self, hostname: &str) -> DBResult<()>; + + /// check if a forge instance exists + async fn forge_exists(&self, hostname: &str) -> DBResult; +} + +/// Trait to clone SCDatabase +pub trait CloneSPDatabase { + /// clone DB + fn clone_db(&self) -> Box; +} + +impl CloneSPDatabase for T +where + T: SCDatabase + Clone + 'static, +{ + fn clone_db(&self) -> Box { + Box::new(self.clone()) + } +} + +impl Clone for Box { + fn clone(&self) -> Self { + (**self).clone_db() + } +} + +/// Forge type: Gitea, Sourcehut, GitLab, etc. Support is currently only available for Gitea +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ForgeImplementation { + /// [Gitea](https://gitea.io) softare forge + Gitea, +} + +impl ForgeImplementation { + /// Convert [ForgeImplementation] to [str] + pub const fn to_str(&self) -> &'static str { + match self { + ForgeImplementation::Gitea => "gitea", + } + } +} + +impl FromStr for ForgeImplementation { + type Err = DBError; + + /// Convert [str] to [ForgeImplementation] + fn from_str(s: &str) -> DBResult { + const GITEA: &str = ForgeImplementation::Gitea.to_str(); + let s = s.trim(); + match s { + GITEA => Ok(Self::Gitea), + _ => Err(DBError::UnknownForgeType(s.to_owned())), + } + } +} diff --git a/db/db-core/src/ops.rs b/db/db-core/src/ops.rs new file mode 100644 index 0000000..eb73720 --- /dev/null +++ b/db/db-core/src/ops.rs @@ -0,0 +1,50 @@ +/* + * ForgeFlux StarChart - A federated software forge spider + * Copyright (C) 2022 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +//! meta operations like migration and connecting to a database +use crate::dev::*; + +/// Database operations trait(migrations, pool creation and fetching connection from pool) +pub trait DBOps: GetConnection + Migrate {} + +/// Get database connection +#[async_trait] +pub trait GetConnection { + /// database connection type + type Conn; + /// database specific error-type + /// get connection from connection pool + async fn get_conn(&self) -> DBResult; +} + +/// Create databse connection +#[async_trait] +pub trait Connect { + /// database specific pool-type + type Pool: SCDatabase; + /// database specific error-type + /// create connection pool + async fn connect(self) -> DBResult; +} + +/// database migrations +#[async_trait] +pub trait Migrate: SCDatabase { + /// database specific error-type + /// run migrations + async fn migrate(&self) -> DBResult<()>; +} diff --git a/db/db-core/src/tests.rs b/db/db-core/src/tests.rs new file mode 100644 index 0000000..41a1968 --- /dev/null +++ b/db/db-core/src/tests.rs @@ -0,0 +1,29 @@ +/* + * ForgeFlux StarChart - A federated software forge spider + * Copyright (C) 2022 Aravinth Manivannan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +//! Test utilities +use crate::prelude::*; + +/// adding forge works +pub async fn adding_forge_works( + db: &T, + create_forge_msg: CreateForge<'static> +) { + let _ = db.delete_forge_instance(&create_forge_msg.hostname).await; + db.create_forge_isntance(&create_forge_msg).await.unwrap(); + assert!(db.forge_exists(create_forge_msg.hostname).await.unwrap(), "forge creation failed, forge existance check failure"); +}