diff --git a/server/Cargo.toml b/server/Cargo.toml index 9fa06589..01ccf77e 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -57,3 +57,4 @@ env_logger = "0.11.3" tempdir = "0.3.7" hamcrest = "0.1.5" reqwest = "0.12.5" +uuid = { version = "1.10.0", features = ["v4", "fast-rng", "macro-diagnostics", ] } diff --git a/server/src/lib.rs b/server/src/lib.rs index 616b926a..be0b31a5 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -31,16 +31,13 @@ use tokio::net::TcpListener; use crate::handlers::home; use crate::handlers::register_user::register_user; -use crate::xml::storage::Storage; /// Handlers. pub mod handlers; -/// GitHub domain objects. +/// Fakehub objects. pub mod objects; /// Reports. pub mod report; -/// XML storage. -pub mod xml; #[allow(unused_imports)] #[macro_use] extern crate hamcrest; @@ -85,7 +82,7 @@ pub struct ServerConfig { impl Server { /// Start a server. pub async fn start(self) -> Result<()> { - Storage::new(Some("fakehub.xml")); + // Storage::new(Some("fakehub.xml")); let addr: String = format!("0.0.0.0:{}", self.port); let started: io::Result = TcpListener::bind(addr.clone()).await; match started { diff --git a/server/src/objects/fakehub.rs b/server/src/objects/fakehub.rs new file mode 100644 index 00000000..9c8572dd --- /dev/null +++ b/server/src/objects/fakehub.rs @@ -0,0 +1,68 @@ +// The MIT License (MIT) +// +// Copyright (c) 2024 Aliaksei Bialiauski +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// 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 NON-INFRINGEMENT. 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::objects::github::GitHub; +use uuid::Uuid; + +/// Fakehub. +pub struct Fakehub { + hubs: Vec, +} + +impl Default for Fakehub { + fn default() -> Fakehub { + Fakehub { + hubs: vec![GitHub { + id: Uuid::new_v4(), + url: String::from("https://github.com"), + }], + } + } +} + +impl Fakehub { + /// New fakehub + pub fn new(hubs: Vec) -> Fakehub { + Fakehub { hubs } + } + + /// Add new GitHub + pub fn add(mut self, github: GitHub) { + self.hubs.push(github); + } +} + +#[cfg(test)] +mod tests { + + use crate::objects::fakehub::Fakehub; + use anyhow::Result; + use hamcrest::{equal_to, is, HamcrestMatcher}; + + #[test] + fn creates_default_fakehub() -> Result<()> { + let fakehub = Fakehub::default(); + let default = fakehub.hubs.first().expect("Can not obtain GitHub"); + assert_that!(default.id.is_nil(), is(equal_to(false))); + assert_that!(&default.url, is(equal_to("https://github.com"))); + Ok(()) + } +} diff --git a/server/src/xml/mod.rs b/server/src/objects/github.rs similarity index 91% rename from server/src/xml/mod.rs rename to server/src/objects/github.rs index 770a42f2..cd9d7d4e 100644 --- a/server/src/xml/mod.rs +++ b/server/src/objects/github.rs @@ -19,5 +19,10 @@ // 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. -/// Storage. -pub mod storage; +use uuid::Uuid; + +/// GitHub. +pub struct GitHub { + pub(crate) id: Uuid, + pub(crate) url: String, +} diff --git a/server/src/objects/mod.rs b/server/src/objects/mod.rs index 5c804301..5bda9151 100644 --- a/server/src/objects/mod.rs +++ b/server/src/objects/mod.rs @@ -21,3 +21,5 @@ // SOFTWARE. /// GitHub user. pub mod user; +pub mod fakehub; +pub mod github; diff --git a/server/src/xml/storage.rs b/server/src/xml/storage.rs deleted file mode 100644 index 1c4a50ca..00000000 --- a/server/src/xml/storage.rs +++ /dev/null @@ -1,144 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2024 Aliaksei Bialiauski -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// 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 NON-INFRINGEMENT. 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 log::info; -use std::fs::File; -use std::io::{Read, Write}; - -#[derive(Default)] -#[allow(dead_code)] -/// Storage. -pub struct Storage { - pub(crate) path: String, -} - -const INIT_XML: &str = " - - -"; - -impl Storage { - /// New storage. - /// - /// # Fields - /// * `path`: Storage path - /// - /// # Examples - /// - /// ``` - /// use server::xml::storage::Storage; - /// let storage = Storage::new(Some("test.xml")); - /// ``` - /// Or use it with default path: - /// ``` - /// use server::xml::storage::Storage; - /// let storage = Storage::new(None); - /// ``` - pub fn new(path: Option<&str>) -> Storage { - let location = path.unwrap_or("fakehub.xml"); - info!("Initializing XML storage: {location}"); - let mut file = match File::create(location) { - Ok(file) => file, - Err(err) => { - panic!("fakehub storage failed to initialize in '{location}': {err}"); - } - }; - if let Err(err) = file.write_all(INIT_XML.as_bytes()) { - panic!("Failed to write initial content to '{}': {}", location, err); - } - info!("'{}' initialized", location); - Storage { - path: String::from(location), - } - } -} - -impl Storage { - /// Returns full XML from the storage. - // @todo #75:60min Make xml() thread-safe. - // We should make this function thread-safe in order to get sequential of - // reads and write to the store. Don't forget to create a unit-test that - // checks concurrency cases. - // @todo #75:35min Prohibit to use `?` (question mark operator). - // Let's prohibit to use `?` as we did with `unwrap()`. Don't forget to - // remove this puzzle. - pub fn xml(self) -> String { - let mut file = File::open(self.path).expect("Can't open file"); - let mut xml = String::new(); - file.read_to_string(&mut xml) - .expect("Can't read file with XML"); - xml - } -} - -#[allow(clippy::question_mark_used)] -#[cfg(test)] -mod tests { - use std::fs; - - use anyhow::Result; - use hamcrest::{equal_to, is, HamcrestMatcher}; - use tempdir::TempDir; - - use crate::xml::storage::Storage; - - #[test] - fn creates_xml_storage() -> Result<()> { - let temp = TempDir::new("temp")?; - let path = temp.path().join("fakehub.xml"); - let storage = path.to_str(); - Storage::new(storage); - assert_that!(path.exists(), is(equal_to(true))); - Ok(()) - } - - #[test] - fn reads_initial_content() -> Result<()> { - let temp = TempDir::new("temp")?; - let path = temp.path().join("fakehub.xml"); - Storage::new(path.to_str()); - let xml = fs::read_to_string(path)?; - let expected = "\n\n\n"; - assert_that!(xml, is(equal_to(String::from(expected)))); - Ok(()) - } - - #[test] - fn creates_xml_storage_with_different_name() -> Result<()> { - let temp = TempDir::new("temp")?; - let path = temp.path().join("test.xml"); - let storage = path.to_str(); - Storage::new(storage); - assert_that!(path.exists(), is(equal_to(true))); - Ok(()) - } - - #[test] - fn outputs_full_xml() -> Result<()> { - let temp = TempDir::new("temp")?; - let path = temp.path().join("test.xml"); - let xml = Storage::new(path.to_str()).xml(); - assert_that!(xml.contains(""), is(equal_to(true))); - assert_that!(xml.contains(""), is(equal_to(true))); - assert_that!(xml.contains(""), is(equal_to(true))); - Ok(()) - } -}