trace_share_core/
consent.rs1use anyhow::{Result, bail};
2use chrono::Utc;
3use serde::{Deserialize, Serialize};
4
5use crate::state::StateStore;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ConsentState {
9 pub accepted_at: String,
10 pub consent_version: String,
11 pub license: String,
12 pub public_searchable: bool,
13 pub trainable: bool,
14 pub ack_sanitization: bool,
15 pub ack_public_search: bool,
16 pub ack_training_release: bool,
17}
18
19pub fn allowed_license(license: &str) -> bool {
20 matches!(license, "CC0-1.0" | "CC-BY-4.0")
21}
22
23pub fn init_consent(
24 store: &StateStore,
25 license: &str,
26 consent_version: &str,
27) -> Result<ConsentState> {
28 if !allowed_license(license) {
29 bail!("license must be one of: CC0-1.0, CC-BY-4.0");
30 }
31
32 let state = ConsentState {
33 accepted_at: Utc::now().to_rfc3339(),
34 consent_version: consent_version.to_string(),
35 license: license.to_string(),
36 public_searchable: true,
37 trainable: true,
38 ack_sanitization: true,
39 ack_public_search: true,
40 ack_training_release: true,
41 };
42 store.upsert_consent_state(&state)?;
43 Ok(state)
44}
45
46pub fn require_consent(store: &StateStore) -> Result<ConsentState> {
47 let Some(state) = store.consent_state()? else {
48 bail!(
49 "consent not initialized. run: trace-share consent init --license <CC0-1.0|CC-BY-4.0>"
50 );
51 };
52 if !state.public_searchable || !state.trainable {
53 bail!("consent state does not allow searchable+trainable uploads");
54 }
55 if !allowed_license(&state.license) {
56 bail!("consent license invalid: {}", state.license);
57 }
58 if !(state.ack_sanitization && state.ack_public_search && state.ack_training_release) {
59 bail!("consent acknowledgements incomplete");
60 }
61 Ok(state)
62}