Browse Source

+ [lib] added basic structure

main
Riley Apeldoorn 9 months ago
commit
cd2fbe1cd6
  1. 1
      .gitignore
  2. 1114
      Cargo.lock
  3. 17
      Cargo.toml
  4. 75
      src/id.rs
  5. 149
      src/lib.rs
  6. 5
      src/main.rs

1
.gitignore

@ -0,0 +1 @@
/target

1114
Cargo.lock

File diff suppressed because it is too large

17
Cargo.toml

@ -0,0 +1,17 @@
[package]
name = "pod"
version = "0.1.0"
authors = ["Riley Apeldoorn <info@guusapeldoorn.nl>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
url = { version = "2", features = [ "serde" ] }
async-trait = "*"
reqwest = { version = "^0.11", features = [ "json" ] }
bytes = "1"
serde = "1"
sled = "^0.34"
rand = "*"
base64 = "^0.13"

75
src/id.rs

@ -0,0 +1,75 @@
use std::marker::PhantomData;
use crate::cfg;
/// An identifier used for both local and remote data.
///
/// Within ActivityPub, URLs are used for identifiers. To keep things
/// simple, Pod does the same thing within its internal data model.
///
/// # ID generation
///
/// IDs generated by Pod follow the following structure:
///
/// | Type | Structure | Example |
/// | ------------- | --------------------------------- | ----------------------------------------- |
/// | `Actor` | `https://{cfg.host}/ap/{name}` | `https://rly.cx/ap/riley` |
/// | `Activity` | `{actor.id}/a/{base64_str}` | `https://rly.cx/ap/riley/a/ZHNmamZkcwo-` |
/// | `Object` | `{actor.id}/o/{base64_str}` | `https://rly.cx/ap/riley/o/YTMyaXVrZAo-` |
///
/// Alternatively, using the `host` definition in [RFC 3986](https://tools.ietf.org/html/rfc3986)
/// for the `<rfc-3986-host>` rule:
///
/// ```bnf
/// anum-char ::= 'a' ..= 'z' | 'A' ..= 'Z' | '0' ..= '9'
/// base64-char ::= <anum-char> | '-' | '_'
/// base64-str ::= <base64-char> | <base64-char> <base64-str>
///
/// actor-name ::= <anum-char> | <anum-char> <actor-name>
///
/// actor-id ::= "https://" <rfc-3986-host> "/ap/" <actor-name>
/// activity-id ::= <actor-id> "/a/" <base64-str>
/// object-id ::= <actor-id> "/o/" <base64-str>
/// ```
pub struct Id <T: HasId + ?Sized> {
pub (super) value: url::Url,
pub (super) _type: PhantomData<T>,
}
impl<T: HasId> Clone for Id<T> {
fn clone(&self) -> Id<T> {
Id {
value: self.value.clone(),
_type: PhantomData,
}
}
}
unsafe impl<T: HasId> Send for Id<T> {}
unsafe impl<T: HasId> Sync for Id<T> {}
impl<T: HasId> AsRef<[u8]> for Id<T> {
fn as_ref (&self) -> &[u8] {
self.value
.as_ref()
.as_bytes()
}
}
pub trait HasId {
fn get_id (&self) -> &Id<Self>;
}
pub trait GenId {
type For: HasId;
fn gen_id (&self, ctx: &ctx::Context) -> Id<Self::For>;
}
pub mod ctx {
pub struct Context {
web: reqwest::Client,
cfg: crate::cfg::Config,
db: crate::db::Client,
}
}

149
src/lib.rs

@ -0,0 +1,149 @@
pub mod ctl {
pub mod ipc {}
pub mod job {}
pub mod cli {}
pub struct MessageStream {}
pub enum Message {
Terminate
}
pub enum Error {}
}
pub mod cfg {
pub struct Config {
pub hostname: String,
}
pub fn get () -> &'static Config {
todo!()
}
}
pub mod id;
pub use id::Id;
pub mod db {
//! Database interaction for Pod.
//!
//! # Backend
//!
//! Currently, the only supported database backend is [sled].
//! Optional support for [terminus] and [postgres] is planned.
//!
//! [terminus]: https://terminusdb.com/
//! [postgres]: https://www.postgresql.org/
use super::id::{ Id, GenId, HasId, };
pub enum Error {
Sled (sled::Error),
NotFound,
}
/// A client that can be used to interact with the database.
pub struct Client {
db: sled::Db,
}
impl Client {
/// GET data from the database.
///
/// For more information, see the documentation for [`Get`].
pub async fn get <G: Get> (&self, key: G) -> Result<G::Val, Error> {
key.get(&*self.db)
.map_err (|err| {
Error::Sled (err)
})
}
/// PUT data into the database.
///
/// For more information, see the documentation for [`Put`].
pub async fn put <P: Put> (&self, val: P) -> Result<P::Key, Error> {
val.put(&*self.db)
.map_err (|err| {
Error::Sled (err)
})
}
/// Generate a unique [`Id`].
pub fn gen_id <G: GenId<For = T>, T: HasId> (&self, generator: &G, ctx: &mut crate::id::ctx::Context) -> Result<Id<T>, Error> {
use std::marker::PhantomData;
use rand::Rng;
let mut rng = rand::thread_rng();
// Loop until we find an unused id.
loop {
let raw_id: [u8; 32] = rng.gen();
// Use the random data to generate an id which can be
// checked for uniqueness.
let id = generator.gen_id(&ctx);
// Check whether the id exists in the database.
// If communicating with the database fails, stop. Otherwise,
// return the id if it is free.
if !self.id_available(&id)? {
break Ok (id)
}
}
}
/// Check whether an [`Id`] is available.
fn id_available <T: HasId> (&self, id: &Id<T>) -> Result<bool, Error> {
self.db.contains_key(id)
.map_err (|err| {
Error::Sled (err)
})
}
}
use sealed::Sealed;
mod sealed { pub trait Sealed {} }
/// A key that can be used to retrieve a value from the database.
pub trait Get: Sealed {
/// The type of the value associated with this key.
type Val: Put<Key = Self>;
/// Retrieve the value this instance identifies from the database.
#[doc(hidden)]
fn get (self, db: &sled::Tree) -> sled::Result<Self::Val>;
}
/// A sealed trait implemented for values which can be retrieved from
/// the database by a key.
///
/// This trait is used to make using the [`Client`] to access the
/// database more convenient.
///
/// # HTTP Semantics
///
/// The PUT operation on the database has the same semantics as the
/// HTTP PUT: it either creates a new resource or replaces an existing
/// one with a given payload. In addition, PUT is idempotent.
pub trait Put: Sealed {
/// A key by which the data can be retrieved from the database again.
type Key: Get<Val = Self>;
/// Update or insert a value into the database and return the key of
/// the updated value.
#[doc(hidden)]
fn put (self, db: &sled::Tree) -> sled::Result<Self::Key>;
}
}

5
src/main.rs

@ -0,0 +1,5 @@
use pod;
fn main() {
println!("Hello, world!");
}
Loading…
Cancel
Save