#![allow(unused)] mod data { #[derive(Debug, Clone, Eq, PartialEq)] pub struct UserId(pub String); #[derive(Debug, Clone, Eq, PartialEq)] pub struct PostId(pub String); #[derive(Debug, Clone, Eq, PartialEq)] pub struct GroupId(pub String); #[derive(Debug)] pub struct User { pub user_id: UserId, pub username: String, pub display_name: String, } #[derive(Debug)] pub struct Group { pub group_id: GroupId, pub group_name: String, pub description: String, } #[derive(Debug)] pub struct Post { pub post_id: PostId, pub author_id: UserId, pub group_id: Option, pub privacy: PostPrivacy, pub title: String, pub content: String, } #[derive(Debug)] pub enum PostPrivacy { Public, Private, GroupRead, GroupEdit, } } mod raw_query { use super::data::*; pub struct DbError; pub fn get_user_info(user_id: &UserId) -> Result { todo!() } pub fn get_user_groups(user_id: &UserId) -> Result, DbError> { todo!() } pub fn user_is_admin(user_id: &UserId) -> Result { todo!() } pub fn get_post_info(post_id: &PostId) -> Result { todo!() } } mod named_query { use mononym::*; use super::{ data::*, raw_query::{ self, DbError, }, }; exists! { ExistUser(user: User) => UserHasId(user_id: UserId); ExistGroups(groups: Vec) => UserInGroups(user_id: UserId); ExistPost(post: Post) => PostHasId(post_id: PostId); } proof! { UserIsAdmin(user_id: UserId); } pub fn get_user_info>( seed: Seed, user_id: Named, ) -> Result, UserIdVal>, DbError> { let user = raw_query::get_user_info(user_id.value())?; Ok(new_exist_user(seed, user)) } pub fn get_user_groups>( seed: Seed, user_id: &Named, ) -> Result>, UserIdVal>, DbError> { let groups = raw_query::get_user_groups(user_id.value())?; Ok(new_exist_groups(seed, groups)) } pub fn user_is_admin>( user_id: Named ) -> Result>, DbError> { let is_admin = raw_query::user_is_admin(user_id.value())?; if is_admin { Ok(Some(UserIsAdmin::new())) } else { Ok(None) } } pub fn get_post_info>( seed: Seed, post_id: &Named, ) -> Result, PostIdVal>, DbError> { let post = raw_query::get_post_info(post_id.value())?; Ok(new_exist_post(seed, post)) } } mod privacy { use mononym::*; use super::{ data::*, named_query::*, }; pub struct Public; pub struct Private; pub struct GroupRead; pub struct GroupEdit; proof! { PostHasPrivacy(post_id: PostId); } pub enum SomePostPrivacy> { Public(PostHasPrivacy), Private(PostHasPrivacy), GroupRead(PostHasPrivacy), GroupEdit(PostHasPrivacy), } pub fn check_post_privacy< UserIdVal: HasType, PostIdVal: HasType, PostVal: HasType, >( post: &Named, _post_has_id: &PostHasId, ) -> SomePostPrivacy { match post.value().privacy { PostPrivacy::Public => SomePostPrivacy::Public(PostHasPrivacy::new()), PostPrivacy::Private => SomePostPrivacy::Private(PostHasPrivacy::new()), PostPrivacy::GroupRead => { SomePostPrivacy::GroupRead(PostHasPrivacy::new()) } PostPrivacy::GroupEdit => { SomePostPrivacy::GroupEdit(PostHasPrivacy::new()) } } } } mod access_control { use mononym::*; use super::{ data::*, named_query::*, privacy::*, }; proof! { UserCanReadPost(post_id: PostId, user_id: UserId); UserCanEditPost(post_id: PostId, user_id: UserId); UserIsAuthor(post_id: PostId, user_id: UserId); UserInGroup(group_id: GroupId, user_id: UserId); } exists! { ExistPostGroup(group_id: GroupId) => PostInGroup(post_id: PostId); } pub fn check_user_is_author< UserIdVal: HasType, PostIdVal: HasType, PostVal: HasType, >( user_id: &Named, post: &Named, _post_has_id: &PostHasId, ) -> Option> { if &post.value().author_id == user_id.value() { Some(UserIsAuthor::new()) } else { None } } pub fn check_user_in_group< UserIdVal: HasType, GroupIdVal: HasType, GroupsVal: HasType>, >( user_id: &Named, group_id: &Named, groups: &Named>, _user_in_groups: &UserInGroups, ) -> Option> { for group in groups.value().iter() { if &group.group_id == group_id.value() { return Some(UserInGroup::new()); } } None } pub fn get_post_group, PostVal: HasType>( seed: Seed, post: &Named, _post_has_id: &PostHasId, ) -> Option, PostIdVal>> { post .value() .group_id .as_ref() .map(move |group_id| new_exist_post_group(seed, group_id.clone())) } pub fn check_post_in_group< GroupIdVal: HasType, PostIdVal: HasType, PostVal: HasType, >( group_id: &Named, post: &Named, _post_has_id: &PostHasId, ) -> Option> { if post.value().group_id.as_ref() == Some(group_id.value()) { Some(PostInGroup::new()) } else { None } } pub fn author_can_edit_post< UserIdVal: HasType, PostIdVal: HasType, >( _user_is_author: &UserIsAuthor ) -> UserCanEditPost { UserCanEditPost::new() } pub fn can_edit_also_can_read< UserIdVal: HasType, PostIdVal: HasType, >( _can_edit: &UserCanEditPost ) -> UserCanReadPost { UserCanReadPost::new() } pub fn anyone_can_read_public_post< UserIdVal: HasType, PostIdVal: HasType, >( _post_is_public: &PostHasPrivacy ) -> UserCanReadPost { UserCanReadPost::new() } pub fn admin_can_edit_any_post< UserIdVal: HasType, PostIdVal: HasType, >( _user_is_admin: &UserIsAdmin ) -> UserCanEditPost { UserCanEditPost::new() } pub fn group_member_can_read_post_with_group_read_privacy< UserIdVal: HasType, PostIdVal: HasType, GroupIdVal: HasType, >( _user_in_group: &UserInGroup, _post_in_group: &PostInGroup, _post_has_group_read_privacy: &PostHasPrivacy, ) -> UserCanReadPost { UserCanReadPost::new() } pub fn group_member_can_edit_post_with_group_edit_privacy< UserIdVal: HasType, PostIdVal: HasType, GroupIdVal: HasType, >( _user_in_group: &UserInGroup, _post_in_group: &PostInGroup, _post_has_group_read_privacy: &PostHasPrivacy, ) -> UserCanEditPost { UserCanEditPost::new() } } fn main() {}