#![doc(html_root_url = "https://docs.rs/googleads-rs/23.2.0")]
#[allow(clippy::all)]
#[allow(clippy::doc_lazy_continuation)]
#[allow(unused_must_use)]
mod protos {
include!(concat!(env!("OUT_DIR"), "/protos.rs"));
}
pub use protos::*;
use once_cell::sync::Lazy;
use prost::Message;
use prost_reflect::{DescriptorPool, DynamicMessage, ReflectMessage, Value};
use std::io::Cursor;
static DESCRIPTOR_POOL: Lazy<DescriptorPool> = Lazy::new(|| {
let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin"));
DescriptorPool::decode(bytes.as_ref()).expect("Failed to decode file descriptor set")
});
const GOOGLE_ADS_ROW_FQN: &str = "google.ads.googleads.v23.services.GoogleAdsRow";
impl google::ads::googleads::v23::services::GoogleAdsRow {
pub fn get(&self, field_name: &str) -> String {
let encoded = self.encode_to_vec();
let descriptor = DESCRIPTOR_POOL
.get_message_by_name(GOOGLE_ADS_ROW_FQN)
.expect("GoogleAdsRow descriptor not found");
let dynamic_msg = DynamicMessage::decode(descriptor, Cursor::new(&encoded))
.expect("Failed to decode GoogleAdsRow as DynamicMessage");
self.get_field_from_dynamic(&dynamic_msg, field_name)
}
pub fn get_many(&self, field_names: &[&str]) -> Vec<String> {
let encoded = self.encode_to_vec();
let descriptor = DESCRIPTOR_POOL
.get_message_by_name(GOOGLE_ADS_ROW_FQN)
.expect("GoogleAdsRow descriptor not found");
let dynamic_msg = DynamicMessage::decode(descriptor, Cursor::new(&encoded))
.expect("Failed to decode GoogleAdsRow as DynamicMessage");
field_names
.iter()
.map(|field_name| self.get_field_from_dynamic(&dynamic_msg, field_name))
.collect()
}
fn get_field_from_dynamic(&self, dyn_msg: &DynamicMessage, field_name: &str) -> String {
match field_name {
field if field.starts_with("campaign.asset_automation_settings") => {
self.format_asset_automation_settings(dyn_msg)
}
field
if field.starts_with("ad_group_ad.ad.responsive_search_ad.headlines")
|| field.starts_with("ad_group_ad.ad.responsive_search_ad.descriptions") =>
{
self.format_value_at_path(dyn_msg, &format!("{}.text", field_name))
}
_ => self.format_value_at_path(dyn_msg, field_name),
}
}
fn format_asset_automation_settings(&self, dyn_msg: &DynamicMessage) -> String {
let campaign_field = dyn_msg
.descriptor()
.get_field_by_name("campaign")
.expect("campaign field not found");
if !dyn_msg.has_field(&campaign_field) {
return String::new();
}
let campaign_value = dyn_msg.get_field(&campaign_field);
let campaign_msg = match &*campaign_value {
Value::Message(msg) => msg,
_ => return String::new(),
};
let settings_field = campaign_msg
.descriptor()
.get_field_by_name("asset_automation_settings")
.expect("asset_automation_settings field not found");
if !campaign_msg.has_field(&settings_field) {
return String::new();
}
let settings_value = campaign_msg.get_field(&settings_field);
let settings_list = match &*settings_value {
Value::List(list) => list,
_ => return String::new(),
};
settings_list
.iter()
.filter_map(|item| match item {
Value::Message(setting_msg) => {
let type_field = match setting_msg
.descriptor()
.get_field_by_name("asset_automation_type")
{
Some(f) => f,
None => return None,
};
let type_value = setting_msg.get_field(&type_field);
let status_field = match setting_msg
.descriptor()
.get_field_by_name("asset_automation_status")
{
Some(f) => f,
None => return None,
};
let status_value = setting_msg.get_field(&status_field);
let type_name = self.format_scalar(&type_value, &type_field);
let status_name = self.format_scalar(&status_value, &status_field);
Some(format!("{}:{}", type_name, status_name))
}
_ => None,
})
.collect::<Vec<_>>()
.join(", ")
}
fn format_field_mask(&self, field_mask: &DynamicMessage) -> String {
let paths_field = match field_mask.descriptor().get_field_by_name("paths") {
Some(f) => f,
None => return String::new(),
};
let paths_value = field_mask.get_field(&paths_field);
match &*paths_value {
Value::List(list) => list
.iter()
.filter_map(|item| match item {
Value::String(s) => Some(s.clone()),
_ => None,
})
.collect::<Vec<_>>()
.join(", "),
_ => String::new(),
}
}
fn format_value_at_path(&self, msg: &DynamicMessage, path: &str) -> String {
let path_segments: Vec<&str> = path.split('.').collect();
self.format_value_recursive(msg, &path_segments, None)
}
fn format_value_recursive(
&self,
msg: &DynamicMessage,
path: &[&str],
_field_desc: Option<&prost_reflect::FieldDescriptor>,
) -> String {
if path.is_empty() {
return format!("{:?}", msg);
}
let segment = path[0];
if segment.is_empty() {
return "not implemented by googleads-rs".to_string();
}
let remaining = &path[1..];
let desc = match msg.descriptor().get_field_by_name(segment) {
Some(desc) => desc,
None => return "not implemented by googleads-rs".to_string(),
};
if desc.supports_presence() && !msg.has_field(&desc) {
if !remaining.is_empty() {
if let prost_reflect::Kind::Message(msg_desc) = desc.kind() {
if msg_desc.get_field_by_name(remaining[0]).is_none() {
return "not implemented by googleads-rs".to_string();
}
}
}
return String::new();
}
let value = msg.get_field(&desc);
match &*value {
Value::Message(sub_msg) => {
if remaining.is_empty() {
if sub_msg.descriptor().full_name() == "google.protobuf.FieldMask" {
self.format_field_mask(sub_msg)
} else {
"not implemented by googleads-rs".to_string()
}
} else {
self.format_value_recursive(sub_msg, remaining, None)
}
}
Value::List(list) => {
if remaining.is_empty() {
self.format_list(list, &desc)
} else {
list.iter()
.map(|item| match item {
Value::Message(sub) => {
self.format_value_recursive(sub, remaining, None)
}
_ => String::new(),
})
.collect::<Vec<_>>()
.join(", ")
}
}
_ => {
if remaining.is_empty() {
self.format_scalar(&value, &desc)
} else {
"not implemented by googleads-rs".to_string()
}
}
}
}
fn format_scalar(&self, value: &Value, field_desc: &prost_reflect::FieldDescriptor) -> String {
match value {
Value::EnumNumber(n) => {
if let prost_reflect::Kind::Enum(enum_desc) = field_desc.kind() {
enum_desc
.get_value(*n)
.map(|v| v.name().to_string())
.unwrap_or_else(|| n.to_string())
} else {
n.to_string()
}
}
Value::String(s) => s.clone(),
Value::Bool(b) => b.to_string(),
Value::I32(i) => i.to_string(),
Value::I64(i) => i.to_string(),
Value::U32(u) => u.to_string(),
Value::U64(u) => u.to_string(),
Value::F32(f) => f.to_string(),
Value::F64(d) => d.to_string(),
Value::Bytes(b) => format!("{:?}", b),
_ => format!("{:?}", value),
}
}
fn format_list(&self, items: &[Value], field_desc: &prost_reflect::FieldDescriptor) -> String {
if items.is_empty() {
return String::new();
}
let is_message_list = items.iter().any(|v| matches!(v, Value::Message(_)));
let sep = if is_message_list { "; " } else { ", " };
items
.iter()
.map(|item| match item {
Value::Message(msg) => self.format_message_compact(msg),
_ => self.format_scalar(item, field_desc),
})
.collect::<Vec<_>>()
.join(sep)
}
fn format_message_compact(&self, msg: &DynamicMessage) -> String {
let fields: Vec<String> = msg
.descriptor()
.fields()
.filter_map(|field_desc| {
if field_desc.supports_presence() && !msg.has_field(&field_desc) {
return None;
}
let value = msg.get_field(&field_desc);
let formatted_value = match &*value {
Value::Message(sub_msg) => self.format_message_compact(sub_msg),
_ => self.format_scalar(&value, &field_desc),
};
if formatted_value.is_empty() {
None
} else {
Some(format!("{}:{}", field_desc.name(), formatted_value))
}
})
.collect();
fields.join(" ")
}
}