1#![doc(
2 html_logo_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png",
3 html_favicon_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png"
4)]
5
6use std::fs;
7use std::io::{self, Read};
8use std::path::PathBuf;
9
10use anyhow::{Context, Result, format_err};
11use clap::Parser;
12use relay_event_normalization::{
13 EventValidationConfig, NormalizationConfig, normalize_event, validate_event,
14};
15use relay_event_schema::processor::{ProcessingState, process_value};
16use relay_event_schema::protocol::Event;
17use relay_pii::{PiiConfig, PiiProcessor};
18use relay_protocol::Annotated;
19
20#[derive(Debug, Parser)]
25#[structopt(verbatim_doc_comment)]
26struct Cli {
27 #[arg(short = 'c', long)]
29 pii_config: Option<PathBuf>,
30
31 #[arg(short, long)]
33 event: Option<PathBuf>,
34
35 #[arg(long)]
37 store: bool,
38
39 #[arg(long, conflicts_with = "debug")]
41 pretty: bool,
42
43 #[arg(long)]
45 debug: bool,
46}
47
48impl Cli {
49 fn load_pii_config(&self) -> Result<Option<PiiConfig>> {
50 let path = match self.pii_config {
51 Some(ref path) => path,
52 None => return Ok(None),
53 };
54
55 let json = fs::read_to_string(path).with_context(|| "failed to read PII config")?;
56 let config = serde_json::from_str(&json).with_context(|| "failed to parse PII config")?;
57 Ok(Some(config))
58 }
59
60 fn load_event(&self) -> Result<Annotated<Event>> {
61 let json = match self.event {
62 Some(ref path) => fs::read_to_string(path).with_context(|| "failed to read event")?,
63 None => {
64 let mut json = String::new();
65 io::stdin()
66 .read_to_string(&mut json)
67 .with_context(|| "failed to read event")?;
68 json
69 }
70 };
71
72 let event = Annotated::from_json(&json).with_context(|| "failed to parse event")?;
73 Ok(event)
74 }
75
76 pub fn run(self) -> Result<()> {
77 let mut event = self.load_event()?;
78
79 if let Some(pii_config) = self.load_pii_config()? {
80 let mut processor = PiiProcessor::new(pii_config.compiled());
81 process_value(&mut event, &mut processor, ProcessingState::root())
82 .map_err(|e| format_err!("{e}"))?;
83 }
84
85 if self.store {
86 validate_event(&mut event, &EventValidationConfig::default())
87 .map_err(|e| format_err!("{e}"))?;
88 normalize_event(&mut event, &NormalizationConfig::default());
89 }
90
91 if self.debug {
92 println!("{event:#?}");
93 } else if self.pretty {
94 println!("{}", event.to_json_pretty()?);
95 } else {
96 println!("{}", event.to_json()?);
97 }
98
99 Ok(())
100 }
101}
102
103fn print_error(error: &anyhow::Error) {
104 eprintln!("Error: {error}");
105
106 let mut cause = error.source();
107 while let Some(ref e) = cause {
108 eprintln!(" caused by: {e}");
109 cause = e.source();
110 }
111}
112
113fn main() {
114 let cli = Cli::parse();
115
116 match cli.run() {
117 Ok(()) => (),
118 Err(error) => {
119 print_error(&error);
120 std::process::exit(1);
121 }
122 }
123}