summaryrefslogtreecommitdiff
path: root/ofborg/tickborg/build.rs
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-04 20:47:05 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-04 20:47:05 +0300
commit17962fd076e857921c374b4d705a54d5e1055178 (patch)
tree6195e9cfdc913cd95b8f577eca3f39d41b089008 /ofborg/tickborg/build.rs
parent7c7f28532f1898a81b0250f875614ad3aa494a1c (diff)
downloadProject-Tick-17962fd076e857921c374b4d705a54d5e1055178.tar.gz
Project-Tick-17962fd076e857921c374b4d705a54d5e1055178.zip
NOISSUE welcome to ofborg! (tickborg)
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'ofborg/tickborg/build.rs')
-rw-r--r--ofborg/tickborg/build.rs566
1 files changed, 566 insertions, 0 deletions
diff --git a/ofborg/tickborg/build.rs b/ofborg/tickborg/build.rs
new file mode 100644
index 0000000000..23c19933ed
--- /dev/null
+++ b/ofborg/tickborg/build.rs
@@ -0,0 +1,566 @@
+use std::env;
+use std::fs::File;
+use std::io::Write;
+use std::path::Path;
+
+enum MetricType {
+ Ticker(Metric),
+ Counter(Metric),
+}
+
+impl MetricType {
+ fn collector_type(&self) -> String {
+ match self {
+ MetricType::Ticker(_) => String::from("u64"),
+ MetricType::Counter(_) => String::from("u64"),
+ }
+ }
+
+ fn enum_matcher_types(&self) -> String {
+ let fields = self.enum_field_types();
+
+ if !fields.is_empty() {
+ format!("{}({})", self.variant(), fields.join(", "))
+ } else {
+ self.variant()
+ }
+ }
+
+ fn variant(&self) -> String {
+ match self {
+ MetricType::Ticker(event) => event.variant.clone(),
+ MetricType::Counter(event) => event.variant.clone(),
+ }
+ }
+
+ fn metric_type(&self) -> String {
+ match self {
+ MetricType::Ticker(_) => String::from("counter"),
+ MetricType::Counter(_) => String::from("counter"),
+ }
+ }
+
+ fn metric_name(&self) -> String {
+ match self {
+ MetricType::Ticker(event) => event.metric_name.clone(),
+ MetricType::Counter(event) => event.metric_name.clone(),
+ }
+ }
+
+ fn description(&self) -> String {
+ match self {
+ MetricType::Ticker(event) => event.description.clone(),
+ MetricType::Counter(event) => event.description.clone(),
+ }
+ }
+
+ fn enum_index_types(&self) -> Vec<String> {
+ let event: &Metric = match self {
+ MetricType::Ticker(i_event) => i_event,
+ MetricType::Counter(i_event) => i_event,
+ };
+
+ let fields: Vec<String> = event
+ .fields
+ .iter()
+ .map(|(_fieldname, fieldtype)| fieldtype.clone())
+ .collect();
+
+ fields
+ }
+
+ fn enum_field_types(&self) -> Vec<String> {
+ let mut extra_fields: Vec<String> = vec![];
+
+ match self {
+ MetricType::Ticker(_) => {}
+ MetricType::Counter(_) => {
+ extra_fields = vec![self.collector_type()];
+ }
+ }
+
+ let mut fields: Vec<String> = self.enum_index_types();
+ fields.append(&mut extra_fields);
+
+ fields
+ }
+
+ fn enum_index_names(&self) -> Vec<String> {
+ let event: &Metric = match self {
+ MetricType::Ticker(i_event) => i_event,
+ MetricType::Counter(i_event) => i_event,
+ };
+
+ let fields: Vec<String> = event
+ .fields
+ .iter()
+ .map(|(fieldname, _fieldtype)| fieldname.clone())
+ .collect();
+
+ fields
+ }
+
+ fn enum_field_names(&self) -> Vec<String> {
+ let mut extra_fields: Vec<String> = vec![];
+
+ match self {
+ MetricType::Ticker(_) => {}
+ MetricType::Counter(_) => {
+ extra_fields = vec!["value".to_owned()];
+ }
+ }
+
+ let mut fields: Vec<String> = self.enum_index_names();
+ fields.append(&mut extra_fields);
+
+ fields
+ }
+
+ fn record_value(&self) -> String {
+ match self {
+ MetricType::Ticker(_) => String::from("1"),
+ MetricType::Counter(_) => String::from("value"),
+ }
+ }
+}
+
+struct Metric {
+ variant: String,
+ fields: Vec<(String, String)>, // Vec because it is sorted
+ metric_name: String,
+ description: String,
+}
+
+fn name_to_parts(name: &str) -> Vec<String> {
+ let mut parts: Vec<String> = vec![];
+ let mut buf = String::from("");
+ for c in name.chars() {
+ if char::is_uppercase(c) && !buf.is_empty() {
+ parts.push(buf.to_owned());
+ buf = String::from("");
+ }
+ buf.push(c);
+ }
+ if !buf.is_empty() {
+ parts.push(buf.to_owned());
+ std::mem::drop(buf);
+ }
+
+ parts
+}
+
+impl Metric {
+ pub fn ticker(name: &str, desc: &str, fields: Option<Vec<(&str, &str)>>) -> MetricType {
+ let parts = name_to_parts(name);
+
+ MetricType::Ticker(Metric {
+ variant: parts.iter().cloned().collect(),
+ fields: fields
+ .unwrap_or_default()
+ .iter()
+ .map(|(fieldname, fieldtype)| ((*fieldname).to_string(), (*fieldtype).to_string()))
+ .collect(),
+ metric_name: parts.join("_").to_lowercase(),
+ description: desc.to_owned(),
+ })
+ }
+
+ pub fn counter(name: &str, desc: &str, fields: Option<Vec<(&str, &str)>>) -> MetricType {
+ let parts = name_to_parts(name);
+
+ MetricType::Counter(Metric {
+ variant: parts.iter().cloned().collect(),
+ fields: fields
+ .unwrap_or_default()
+ .iter()
+ .map(|(fieldname, fieldtype)| ((*fieldname).to_string(), (*fieldtype).to_string()))
+ .collect(),
+ metric_name: parts.join("_").to_lowercase(),
+ description: desc.to_owned(),
+ })
+ }
+}
+
+fn events() -> Vec<MetricType> {
+ vec![
+ Metric::ticker(
+ "StatCollectorLegacyEvent",
+ "Number of received legacy events",
+ Some(vec![("event", "String")]),
+ ),
+ Metric::ticker(
+ "StatCollectorBogusEvent",
+ "Number of received unparseable events",
+ None,
+ ),
+ Metric::ticker("JobReceived", "Number of received worker jobs", None),
+ Metric::counter(
+ "EvaluationDuration",
+ "Amount of time spent running evaluations",
+ Some(vec![("branch", "String")]),
+ ),
+ Metric::ticker(
+ "EvaluationDurationCount",
+ "Number of timed evaluations performed",
+ Some(vec![("branch", "String")]),
+ ),
+ Metric::ticker(
+ "TargetBranchFailsEvaluation",
+ "Number of PR evaluations which failed because the target branch failed",
+ Some(vec![("branch", "String")]),
+ ),
+ Metric::ticker(
+ "JobDecodeSuccess",
+ "Number of successfully decoded jobs",
+ None,
+ ),
+ Metric::ticker(
+ "JobDecodeFailure",
+ "Number of jobs which failed to parse",
+ None,
+ ),
+ Metric::ticker(
+ "IssueAlreadyClosed",
+ "Number of jobs for issues which are already closed",
+ None,
+ ),
+ Metric::ticker(
+ "IssueFetchFailed",
+ "Number of failed fetches for GitHub issues",
+ None,
+ ),
+ Metric::ticker(
+ "TaskEvaluationCheckComplete",
+ "Number of completed evaluation tasks",
+ None,
+ ),
+ /*
+ Metric::counter(
+ "TimeElapsed",
+ "",
+ None
+ ),
+ Metric::counter(
+ "EnvironmentsAllocatedCount",
+ "",
+ None
+ ),
+ Metric::counter(
+ "EnvironmentsAllocatedBytes",
+ "",
+ None
+ ),
+ Metric::counter(
+ "ListElementsCount",
+ "",
+ None
+ ),
+ Metric::counter(
+ "ListElementsBytes",
+ "",
+ None
+ ),
+ Metric::counter(
+ "ListConcatenations",
+ "",
+ None
+ ),
+ Metric::counter(
+ "ValuesAllocatedCount",
+ "",
+ None
+ ),
+ Metric::counter(
+ "ValuesAllocatedBytes",
+ "",
+ None
+ ),
+ Metric::counter(
+ "SetsAllocatedCount",
+ "",
+ None
+ ),
+ Metric::counter(
+ "SetsAllocatedBytes",
+ "",
+ None
+ ),
+ Metric::counter(
+ "RightBiasedUnions",
+ "",
+ None
+ ),
+ Metric::counter(
+ "ValuesCopiedInRightBiasedUnions",
+ "",
+ None
+ ),
+ Metric::counter(
+ "SymbolsInSymbolTable",
+ "",
+ None
+ ),
+ Metric::counter(
+ "SizeOfSymbolTable",
+ "",
+ None
+ ),
+ Metric::counter(
+ "NumberOfThunks",
+ "",
+ None
+ ),
+ Metric::counter(
+ "NumberOfThunksAvoided",
+ "",
+ None
+ ),
+ Metric::counter(
+ "NumberOfAttrLookups",
+ "",
+ None
+ ),
+ Metric::counter(
+ "NumberOfPrimopCalls",
+ "",
+ None
+ ),
+ Metric::counter(
+ "NumberOfFunctionCalls",
+ "",
+ None
+ ),
+ Metric::counter(
+ "TotalAllocations",
+ "",
+ None
+ ),
+ Metric::counter(
+ "CurrentBoehmHeapSizeBytes",
+ "",
+ None
+ ),
+ Metric::counter(
+ "TotalBoehmHeapAllocationsBytes",
+ "",
+ None
+ ),
+ */
+ ]
+}
+
+fn main() {
+ let out_dir = env::var("OUT_DIR").unwrap();
+ let dest_path = Path::new(&out_dir).join("events.rs");
+ let mut f = File::create(dest_path).unwrap();
+
+ println!("cargo:rerun-if-changed=build.rs");
+
+ // Write the Event enum, which contains all possible event types
+ f.write_all(
+ b"
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::Mutex;
+#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
+#[serde(rename_all=\"kebab-case\")]
+pub enum Event {
+",
+ )
+ .unwrap();
+
+ let variants: Vec<String> = events()
+ .iter()
+ .map(|mtype| format!(" {}", mtype.enum_matcher_types()))
+ .collect();
+
+ f.write_all(variants.join(",\n").as_bytes()).unwrap();
+ f.write_all(b"\n}\n\n").unwrap();
+
+ f.write_all(
+ b"pub fn event_metric_name(event: &Event) -> String {
+ match *event {
+",
+ )
+ .unwrap();
+
+ let variants: Vec<String> = events()
+ .iter()
+ .map(|mtype| {
+ let fields: Vec<String> = mtype
+ .enum_field_names()
+ .iter()
+ .map(|_| String::from("_"))
+ .collect();
+
+ let variant_match = if !fields.is_empty() {
+ format!("{}({})", &mtype.variant(), fields.join(", "))
+ } else {
+ mtype.variant()
+ };
+
+ format!(
+ " Event::{} => String::from(\"{}\")",
+ &variant_match,
+ &mtype.metric_name(),
+ )
+ })
+ .collect();
+
+ f.write_all(variants.join(",\n").as_bytes()).unwrap();
+ f.write_all(b"}\n }").unwrap();
+
+ // Create a struct to hold all the possible metrics
+ f.write_all(
+ b"
+#[derive(Default, Clone)]
+pub struct MetricCollector {
+",
+ )
+ .unwrap();
+
+ let variants: Vec<String> = events()
+ .iter()
+ .map(|mtype| {
+ let mut fields: Vec<String> = mtype.enum_index_types();
+ fields.push("String".to_owned()); // Instance
+ let fields_str = {
+ let s = fields.join(", ");
+ if fields.len() > 1 {
+ format!("({s})")
+ } else {
+ s
+ }
+ };
+
+ format!(
+ " {}: Arc<Mutex<HashMap<{},{}>>>",
+ mtype.metric_name(),
+ fields_str,
+ mtype.collector_type(),
+ )
+ })
+ .collect();
+
+ f.write_all(variants.join(",\n").as_bytes()).unwrap();
+ f.write_all(b"\n}\n\n").unwrap();
+
+ // Create a struct to hold all the possible metrics
+ f.write_all(
+ b"
+
+impl MetricCollector {
+ pub fn new() -> MetricCollector {
+ Default::default()
+ }
+
+ pub fn record(&self, instance: String, event: Event) {
+ match event {
+",
+ )
+ .unwrap();
+
+ let variants: Vec<String> = events()
+ .iter()
+ .map(|mtype| {
+ let fields: Vec<String> = mtype.enum_field_names();
+
+ let variant_match = if !fields.is_empty() {
+ format!("{}({})", &mtype.variant(), fields.join(", "))
+ } else {
+ mtype.variant()
+ };
+
+ let mut index_names: Vec<String> = mtype.enum_index_names();
+ index_names.push("instance".to_owned());
+
+ let mut index_fields = index_names.join(", ");
+ if index_names.len() > 1 {
+ index_fields = format!("({index_fields})");
+ }
+
+ format!(
+ "
+ Event::{} => {{
+ let mut accum_table = self.{}
+ .lock()
+ .expect(\"Failed to unwrap metric mutex for {}\");
+ let accum = accum_table
+ .entry({})
+ .or_insert(0);
+ *accum += {};
+ }}
+ ",
+ variant_match,
+ &mtype.metric_name(),
+ &mtype.metric_name(),
+ index_fields,
+ &mtype.record_value(),
+ )
+ })
+ .collect();
+
+ f.write_all(variants.join(",\n").as_bytes()).unwrap();
+ f.write_all(b"\n }\n").unwrap();
+ f.write_all(b"\n }\n").unwrap();
+
+ f.write_all(
+ b"pub fn prometheus_output(&self) -> String {
+ let mut output = String::new();
+",
+ )
+ .unwrap();
+
+ let variants: Vec<String> = events()
+ .iter()
+ .map(|mtype| {
+ let mut index_fields: Vec<String> = mtype.enum_index_names();
+ index_fields.push("instance".to_owned());
+ let ref_index_fields: Vec<String> = index_fields.clone();
+
+ let for_matcher = if index_fields.len() > 1 {
+ format!("({})", ref_index_fields.join(", "))
+ } else {
+ ref_index_fields.join(", ")
+ };
+
+ let key_value_pairs: Vec<String> = index_fields
+ .iter()
+ .map(|name| format!(" format!(\"{name}=\\\"{{}}\\\"\", {name})",))
+ .collect();
+ format!(
+ "
+ output.push_str(\"# HELP tickborg_{} {}\n\");
+ output.push_str(\"# TYPE tickborg_{} {}\n\");
+
+ let table = self.{}.lock()
+ .expect(\"Failed to unwrap metric mutex for {}\");
+ let values: Vec<String> = (*table)
+ .iter()
+ .map(|({}, value)| {{
+ let kvs: Vec<String> = vec![
+{}
+ ];
+ format!(\"tickborg_{}{{{{{{}}}}}} {{}}\", kvs.join(\",\"), value)
+ }})
+ .collect();
+ output.push_str(&values.join(\"\n\"));
+ output.push('\\n');
+ ",
+ &mtype.metric_name(),
+ &mtype.description(),
+ &mtype.metric_name(),
+ &mtype.metric_type(),
+ &mtype.metric_name(),
+ &mtype.metric_name(),
+ for_matcher,
+ &key_value_pairs.join(",\n"),
+ &mtype.metric_name(),
+ )
+ })
+ .collect();
+
+ f.write_all(variants.join("\n").as_bytes()).unwrap();
+ f.write_all(b"output\n }").unwrap();
+ f.write_all(b"\n}").unwrap();
+}