diff options
Diffstat (limited to 'src/data')
-rw-r--r-- | src/data/effective.rs | 173 | ||||
-rw-r--r-- | src/data/kinds.rs | 96 | ||||
-rw-r--r-- | src/data/managers.rs | 98 |
3 files changed, 367 insertions, 0 deletions
diff --git a/src/data/effective.rs b/src/data/effective.rs new file mode 100644 index 0000000..12167dc --- /dev/null +++ b/src/data/effective.rs @@ -0,0 +1,173 @@ +// This file is part of GAnarchy - decentralized development hub +// Copyright (C) 2021 Soni L. +// +// 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 <https://www.gnu.org/licenses/>. + +//! A wrapper for [`DataSource`] that automatically handles +//! [`OverridableKind`]. + +use std::collections::BTreeSet; +use std::collections::HashSet; +use std::error; +use std::time::Duration; + +use impl_trait::impl_trait; + +use super::DataSourceBase; +use super::DataSource; +use super::Kind; +use super::OverridableKind; + +/// A wrapper for [`DataSource`] that automatically handles +/// [`OverridableKind`]. +pub struct EffectiveDataSource<T: DataSourceBase>(T); + +/// A wrapper for [`OverridableKind`] that acts like the key for Eq/Hash/Ord. +#[repr(transparent)] +#[derive(Debug)] +pub struct EffectiveKind<T: OverridableKind>(pub T); + +impl<T: Kind<Values=BTreeSet<T>> + OverridableKind> Kind for EffectiveKind<T> { + type Values = BTreeSet<Self>; +} + +impl_trait! { + impl<T: Kind + OverridableKind> EffectiveKind<T> where Self: Kind { + impl trait OverridableKind { + type Key = T::Key; + + fn as_key(&self) -> &Self::Key { + self.0.as_key() + } + } + impl trait PartialEq { + fn eq(&self, other: &Self) -> bool { + self.as_key().eq(other.as_key()) + } + fn ne(&self, other: &Self) -> bool { + self.as_key().ne(other.as_key()) + } + } + impl trait Eq { + } + impl trait PartialOrd { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + Some(self.cmp(other)) + } + } + impl trait Ord { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.as_key().cmp(other.as_key()) + } + } + impl trait std::hash::Hash { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.as_key().hash(state); + } + } + } +} + +/// Hack to provide appropriate impls for [`EffectiveDataSource`]. +pub trait EffectiveDataSourceHelper<K: Kind<Values=V>, V> { + /// Returns the values associated with this kind. + fn get_values_impl(&self) -> K::Values; +} + +impl_trait! { + impl<T: DataSourceBase> EffectiveDataSource<T> { + /// Wraps the given [`DataSource`]. + pub fn new(source: T) -> Self { + Self(source) + } + + /// Unwraps and returns the inner [`DataSource`]. + pub fn into_inner(self) -> T { + self.0 + } + + /// Forwards to the inner [`DataSourceBase`]. + impl trait DataSourceBase { + fn update(&mut self) -> ( + Duration, + Result<(), Box<dyn error::Error + Send + Sync + 'static>>, + ) { + self.0.update() + } + + fn exists(&self) -> bool { + self.0.exists() + } + } + + /// Filters the inner [`DataSource`] using the appropriate impl. + impl trait<K: Kind> DataSource<K> + where + Self: EffectiveDataSourceHelper<K, K::Values> + { + fn get_values(&self) -> K::Values { + self.get_values_impl() + } + } + + /// Filters the inner [`DataSource`] such that only the first instance + /// of a given key is returned. + impl trait<K> EffectiveDataSourceHelper<K, Vec<K>> + where + K: Kind<Values=Vec<K>> + OverridableKind, + T: DataSource<K> + { + fn get_values_impl(&self) -> Vec<K> { + let mut values: Vec<K> = self.0.get_values(); + let mut seen = HashSet::<&K::Key>::new(); + let mut keep = Vec::with_capacity(values.len()); + for value in &values { + keep.push(seen.insert(value.as_key())); + } + drop(seen); + let mut keep = keep.into_iter(); + values.retain(|_| keep.next().unwrap()); + values + } + } + + /// Forwards to the inner [`DataSource`] as there's no filtering we + /// can/need to do here. + impl trait<K> EffectiveDataSourceHelper< + EffectiveKind<K>, + BTreeSet<EffectiveKind<K>>, + > + where + K: Kind<Values=BTreeSet<K>> + OverridableKind, + T: DataSource<EffectiveKind<K>>, + EffectiveKind<K>: Kind<Values=BTreeSet<EffectiveKind<K>>>, + EffectiveKind<K>: OverridableKind, + { + fn get_values_impl(&self) -> BTreeSet<EffectiveKind<K>> { + self.0.get_values() + } + } + + /// Forwards to the inner [`DataSource`]. + impl trait<K> EffectiveDataSourceHelper<K, Option<K>> + where + K: Kind<Values=Option<K>>, + T: DataSource<K> + { + fn get_values_impl(&self) -> Option<K> { + self.0.get_values() + } + } + } +} diff --git a/src/data/kinds.rs b/src/data/kinds.rs new file mode 100644 index 0000000..d492a92 --- /dev/null +++ b/src/data/kinds.rs @@ -0,0 +1,96 @@ +// This file is part of GAnarchy - decentralized development hub +// Copyright (C) 2021 Soni L. +// +// 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 <https://www.gnu.org/licenses/>. + +//! Kinds of data for use with `DataSource`. + +use arcstr::ArcStr; + +use url::Url; + +use super::Kind; +use super::OverridableKind; + +/// Title of an instance. +#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct InstanceTitle(pub String); + +impl Kind for InstanceTitle { + /// A source may only have one `InstanceTitle`. + type Values = Option<Self>; +} + +/// URL of an instance. +#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct InstanceBaseUrl(pub Url); + +impl Kind for InstanceBaseUrl { + /// A source may only have one `InstanceBaseUrl`. + type Values = Option<Self>; +} + +/// URL of a repo list. +#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct RepoListUrl { + // `Url` is actually fairly expensive, but we don't usually have a lot of + // `RepoListUrl` around. Additionally these are generally unique. + /// The actual URL that references a repo list. + pub url: Url, + /// Whether this entry is active. + pub active: bool, + /// Whether this repo list is allowed to have negative entries. + pub allow_negative_entries: bool, +} + +impl Kind for RepoListUrl { + /// A source may have many `RepoListUrl`. + type Values = Vec<Self>; +} + +impl OverridableKind for RepoListUrl { + type Key = Url; + + fn as_key(&self) -> &Self::Key { + &self.url + } +} + +/// The key for a [`ProjectFork`]. +#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ProjectForkKey { + project: ArcStr, + url: ArcStr, + branch: ArcStr, +} + +/// A fork of a project. +#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct ProjectFork { + pub key: ProjectForkKey, + pub active: bool, +} + +impl Kind for ProjectFork { + /// A source may have many `ProjectFork`. + type Values = Vec<Self>; +} + +impl OverridableKind for ProjectFork { + type Key = ProjectForkKey; + + fn as_key(&self) -> &Self::Key { + &self.key + } +} diff --git a/src/data/managers.rs b/src/data/managers.rs new file mode 100644 index 0000000..71a9484 --- /dev/null +++ b/src/data/managers.rs @@ -0,0 +1,98 @@ +// This file is part of GAnarchy - decentralized development hub +// Copyright (C) 2021 Soni L. +// +// 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 <https://www.gnu.org/licenses/>. + +use std::error; +use std::sync::Arc; +use std::sync::Mutex; +use std::sync::RwLock; +use std::time::Duration; + +use impl_trait::impl_trait; + +use super::DataSourceBase; +use super::DataSource; +use super::effective::EffectiveKind; +//use super::Kind; +use super::kinds::{InstanceTitle, InstanceBaseUrl, RepoListUrl, ProjectFork}; +//use super::OverridableKind; + +/// Stores multiple DataSource capable of ProjectFork +#[derive(Default)] +pub struct RepoListManager { + repos: Vec<Box<dyn DataSource<EffectiveKind<ProjectFork>> + Send + Sync>>, + durations: Vec<Duration>, + valid: usize, +} + +/// Stores multiple DataSource capable of InstanceTitle, InstanceBaseUrl and +/// RepoListUrl +#[derive(Default)] +pub struct ConfigManager { + // conceptually the actual objects + bases: Vec<Arc<RwLock<dyn DataSourceBase + Send + Sync>>>, + // conceptually just views of the above objects + titles: Vec<Option<Arc<RwLock<dyn DataSource<InstanceTitle> + Send + Sync>>>>, + urls: Vec<Option<Arc<RwLock<dyn DataSource<InstanceBaseUrl> + Send + Sync>>>>, + repolists: Vec<Option<Arc<RwLock<dyn DataSource<RepoListUrl> + Send + Sync>>>>, + durations: Vec<Duration>, + // add_source can be called after update. + valid: usize, +} + +impl_trait! { + impl ConfigManager { + /// Creates a new `ConfigManager`. + pub fn new() -> Self { + Default::default() + } + + /// Adds the given combined `DataSource` to this `ConfigManager`. + pub fn add_source<T>(&mut self, source: T) + where + T: DataSource<InstanceTitle>, + T: DataSource<InstanceBaseUrl>, + T: DataSource<RepoListUrl>, + T: Send + Sync + 'static, + { + let arc = Arc::new(RwLock::new(source)); + self.bases.push(arc.clone()); + self.titles.push(Some(arc.clone())); + self.urls.push(Some(arc.clone())); + self.repolists.push(Some(arc)); + } + + impl trait DataSourceBase { + /// Updates the contained `DataSource`s, and returns the shortest + /// duration for the next update. + /// + /// # Errors + /// + /// Returns an error if any `DataSource` returns an error. Always + /// updates all `DataSource`s. + fn update(&mut self) -> ( + Duration, + Result<(), Box<dyn error::Error + Send + Sync + 'static>>, + ) { + todo!() + } + + /// Returns whether this data source contains any (valid) data. + fn exists(&self) -> bool { + todo!() + } + } + } +} |