From 33612ba569cfb1c53f3b07eccb30718f8215a37f Mon Sep 17 00:00:00 2001
From: Dominik George <dominik.george@teckids.org>
Date: Thu, 13 May 2021 23:59:40 +0200
Subject: [PATCH] [NSS] Refactor more jq program loading code into function

---
 src/oauth.rs | 88 ++++++++++++++++++++++++----------------------------
 1 file changed, 41 insertions(+), 47 deletions(-)

diff --git a/src/oauth.rs b/src/oauth.rs
index 72f3f62..8e5aa79 100644
--- a/src/oauth.rs
+++ b/src/oauth.rs
@@ -151,73 +151,67 @@ fn get_data(conf: &Config, prefix: &str, endpoint: &str, param: String, token: &
         .text()?)
 }
 
-fn get_jq_prog(conf: &Config, prefix: &str, endpoint: &str, rev: bool) -> Option<String> {
+fn get_jq_prog(conf: &Config, prefix: &str, endpoint: &str, multi: bool, rev: bool) -> jq_rs::Result<jq_rs::JqProgram>  {
+    // Generate config key to find jq program under
     let prog_key = match rev {
         false => full_key(vec![prefix, "maps", endpoint]),
         true => full_key(vec![prefix, "maps.rev", endpoint]),
     };
-    match get_optional(&conf, &prog_key) {
-        Some(v) => Some (v),
+
+    // Retrieve jq program code from config
+    let mut jq_code: String = match get_optional(&conf, &prog_key) {
+        // We got the value immediately
+        Some(v) => v,
         None => {
             if rev {
                 // Do not fallback to more generic program for reverse mapping
-                return None;
-            }
-            // Try falling back to more generic program
-            match endpoint.find('.') {
-                Some(i) => {
-                    debug!("JQ mapping program for {} not found; trying more generic definition", endpoint);
-                    get_jq_prog(conf, prefix, &endpoint[..i], rev)
-                },
-                None => None
+                ".".to_string()
+            } else {
+                // Try falling back to more generic program
+                match endpoint.find('.') {
+                    Some(i) => {
+                        debug!("JQ mapping program for {} not found; trying more generic definition", endpoint);
+                        // Call recursively (and return verbatim, as we get a compiled program)
+                        return get_jq_prog(conf, prefix, &endpoint[..i], multi, rev)
+                    }
+                    // Lookup failed ultimately; fallback to jq "identity" (no-op)
+                    None => ".".to_string()
+                }
             }
         }
+    };
+
+    // If multi mode is passed, the program will be applied to an array of entries
+    // We thus wrap it in a call to jq's `map()`
+    if multi {
+        jq_code = "map(".to_string() + &jq_code + ")";
     }
+
+    debug!("Compiling JQ program for endpoint {}: {}", endpoint, jq_code);
+    jq_rs::compile(&jq_code)
 }
 
 pub fn get_data_jq<T: for<'de> Deserialize<'de>, V: Serialize>(conf: &Config, prefix: &str, endpoint: &str, param: V, token: &BasicTokenResponse, multi: bool) -> Result<T, Box<dyn error::Error>> {
-    let res: Option<String> = get_jq_prog(&conf, prefix, endpoint, false);
-    let jq_code = match res {
-        Some(s) => {
-            debug!("Found jq mapping program for endpoint {}: {}", endpoint, s);
-            match multi {
-                true => "map(".to_string() + &s + ")",
-                false => s
-            }
-        },
-        None => {
-            debug!("No jq mapping program for endpoint {}; using default (no-op)", endpoint);
-            ".".to_string()
-        }
-    };
-    let mut jq_prog = jq_rs::compile(&jq_code)?;
-    let res: Option<String> = get_jq_prog(&conf, prefix, endpoint, true);
-    let jq_code = match res {
-        Some(s) => {
-            debug!("Found jq reverse mapping program for endpoint {}: {}", endpoint, s);
-            s
-        },
-        None => {
-            debug!("No jq reverse mapping program for endpoint {}; using default (no-op)", endpoint);
-            ".".to_string()
-        }
-    };
-    let mut jq_prog_rev = jq_rs::compile(&jq_code)?;
+    // Get jq mapping programs for forward and reverse mappings
+    let mut jq_prog_fwd = get_jq_prog(&conf, prefix, endpoint, multi, false)?;
+    let mut jq_prog_rev = get_jq_prog(&conf, prefix, endpoint, false, true)?;
 
     // Convert and transform the passed param using the reverse JQ mapping program
     //  1. Serialize into JSON value (atomic) to be bale to pass into jq
-    let param_serialized = serde_json::to_string(&param)?;
+    let param = serde_json::to_string(&param)?;
     //  2. Transform using the JQ program loaded above
-    let param_trans = jq_prog_rev.run(&param_serialized)?.trim().to_string();
+    let param = jq_prog_rev.run(&param)?.trim().to_string();
     //  3. Deserialize into serde_json value so we get numbers as numbers, strings properly unquoted
-    let param_deserialized: serde_json::Value = serde_json::from_str(&param_trans)?;
-    let param = match param_deserialized {
+    let param: serde_json::Value = serde_json::from_str(&param)?;
+    let param = match param {
         serde_json::Value::String(v) => v,   // We want strings verbatim without JSON quoting
-        _ => param_deserialized.to_string()  // We want numbers converted to string
+        _ => param.to_string()  // We want numbers converted to string
     };
 
-    let data_raw = get_data(&conf, prefix, endpoint, param, token)?;
-    let data_trans = jq_prog.run(&data_raw)?;
+    // Retrieve data via HTTP and transform using jq forward mapping program
+    let data = get_data(&conf, prefix, endpoint, param, token)?;
+    let data = jq_prog_fwd.run(&data)?;
 
-    Ok(serde_json::from_str(&data_trans)?)
+    // Deserialize transformed JSON and return
+    Ok(serde_json::from_str(&data)?)
 }
-- 
GitLab