#![doc = include_str!("../README.md")]
#![forbid(unsafe_code)]
mod handlers;
pub mod store;
use std::sync::Arc;
use cratefield_core::{
Config, ConfigError, DataKind, Disposition, Migrations, Module, ModuleContext, PersonalDataSet,
Port, SqlMigration, SubjectVia, Surface,
};
pub const MODULE_NAME: &str = "crm";
const MIGRATION_INIT: SqlMigration = SqlMigration::new(
"0001",
"init",
include_str!("../migrations/sqlite/0001_init.sql"),
);
const TABLES: &[&str] = &[
"crm_contacts",
"crm_organisations",
"crm_tags",
"crm_taggings",
];
#[derive(Debug, Default, Clone, Copy)]
pub struct Crm;
impl Crm {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl Module for Crm {
fn name(&self) -> &'static str {
MODULE_NAME
}
fn version(&self) -> &'static str {
env!("CARGO_PKG_VERSION")
}
fn requires(&self) -> &'static [Port] {
&[Port::Db, Port::IdGen, Port::Clock]
}
fn optional(&self) -> &'static [Port] {
&[Port::Auth]
}
fn tables(&self) -> &'static [&'static str] {
TABLES
}
fn personal_data(&self) -> &'static [PersonalDataSet] {
const SETS: &[PersonalDataSet] = &[
PersonalDataSet {
table: "crm_contacts",
subject: "id",
kind: DataKind::Contact,
disposition: Disposition::Erase,
description: "One person or lead: their email address, name, phone number, \
locale, an optional link to the organisation they belong to, the \
source they arrived from, and any structured fields the venture \
keeps alongside them.",
redacted: &[],
subject_via: None,
},
PersonalDataSet::none(
"crm_organisations",
"The companies this venture deals with, a business record rather than a \
person's: name, domain, website and postal address. Review before relying on \
this: `email` and `phone` may hold an individual's details rather than a \
shared switchboard, and a sole trader's record is a person's data in \
practice. Erasure does not reach these rows today.",
),
PersonalDataSet::none(
"crm_tags",
"The labels a venture files its records under — a name and a colour. Nothing \
in this table names a person.",
),
PersonalDataSet {
table: "crm_taggings",
subject: "subject_id",
kind: DataKind::Identifier,
disposition: Disposition::Erase,
description: "Which labels are filed against which record. The row holds a tag \
and the id of whatever it labels — a contact, an organisation, or \
an item the venture owns — so it is erased with the contact it \
names.",
redacted: &[],
subject_via: Some(SubjectVia {
table: "crm_contacts",
subject: "id",
key: "id",
}),
},
];
SETS
}
fn emits(&self) -> &'static [&'static str] {
&[
handlers::EVENT_CONTACT_CREATED,
handlers::EVENT_CONTACT_UPDATED,
handlers::EVENT_ORGANISATION_CREATED,
]
}
fn migrations(&self) -> Migrations {
const MIGRATIONS: [SqlMigration; 1] = [MIGRATION_INIT];
const _: () = cratefield_core::assert_migration_set(&MIGRATIONS);
Migrations {
sqlite: &MIGRATIONS,
postgres: &[],
}
}
fn validate_config(&self, _cfg: &dyn Config) -> Result<(), ConfigError> {
Ok(())
}
fn router(&self, ctx: ModuleContext) -> axum::Router {
handlers::router(Arc::new(ctx))
}
fn surface(&self) -> Surface {
handlers::surface()
}
}