use nominal_api::objects::api::rids::DataSourceRid;
use nominal_api::objects::api::{
Channel as ApiChannel, SeriesDataType as ApiSeriesDataType, Unit as ApiUnit,
};
use nominal_api::objects::datasource::api::ChannelMetadata as SearchChannelMetadata;
use nominal_api::objects::storage::series::api::NominalDataType;
use nominal_api::objects::timeseries::channelmetadata::api::ChannelMetadata as StoredChannelMetadata;
use nominal_api::objects::timeseries::metadata::api::{
CreateSeriesMetadataRequest, LocatorTemplate, NominalLocatorTemplate,
};
use std::collections::BTreeSet;
use crate::core::rid::{parse_rid, rid_to_string};
use crate::{Error, Result};
#[derive(Debug, Clone)]
pub struct Channel {
data_source_rid: String,
name: String,
description: Option<String>,
unit: Option<String>,
data_type: ChannelDataType,
}
impl Channel {
pub fn data_source_rid(&self) -> &str {
&self.data_source_rid
}
pub fn name(&self) -> &str {
&self.name
}
pub fn description(&self) -> Option<&str> {
self.description.as_deref()
}
pub fn unit(&self) -> Option<&str> {
self.unit.as_deref()
}
pub fn data_type(&self) -> &ChannelDataType {
&self.data_type
}
pub(crate) fn from_search(meta: SearchChannelMetadata) -> Result<Self> {
let data_type = meta.data_type().map(ChannelDataType::from).ok_or_else(|| {
Error::MissingChannelDataType {
channel: meta.name().to_string(),
}
})?;
Ok(Self {
data_source_rid: rid_to_string(meta.data_source()),
name: meta.name().to_string(),
description: meta.description().map(str::to_string),
unit: meta.unit().map(|u| u.symbol().to_string()),
data_type,
})
}
pub(crate) fn from_stored(meta: StoredChannelMetadata) -> Result<Self> {
let id = meta.channel_identifier();
let data_type = meta.data_type().map(ChannelDataType::from).ok_or_else(|| {
Error::MissingChannelDataType {
channel: id.channel_name().to_string(),
}
})?;
Ok(Self {
data_source_rid: rid_to_string(id.data_source_rid()),
name: id.channel_name().to_string(),
description: meta.description().map(str::to_string),
unit: meta.unit().map(|u| u.to_string()),
data_type,
})
}
pub(crate) fn from_update(
data_source_rid: impl Into<String>,
name: impl Into<String>,
update: &ChannelUpdate,
) -> Self {
Self {
data_source_rid: data_source_rid.into(),
name: name.into(),
description: update.description.clone(),
unit: match &update.unit_update {
Some(UnitUpdate::Set(unit)) => Some(unit.clone()),
Some(UnitUpdate::Clear) | None => None,
},
data_type: update.data_type.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ChannelDataType {
Double,
Int,
Uint,
String,
Log,
DoubleArray,
StringArray,
Struct,
Video,
Unknown(String),
}
impl From<&ApiSeriesDataType> for ChannelDataType {
fn from(t: &ApiSeriesDataType) -> Self {
match t {
ApiSeriesDataType::Double => Self::Double,
ApiSeriesDataType::Int => Self::Int,
ApiSeriesDataType::Uint => Self::Uint,
ApiSeriesDataType::String => Self::String,
ApiSeriesDataType::Log => Self::Log,
ApiSeriesDataType::DoubleArray => Self::DoubleArray,
ApiSeriesDataType::StringArray => Self::StringArray,
ApiSeriesDataType::Struct => Self::Struct,
ApiSeriesDataType::Video => Self::Video,
ApiSeriesDataType::Unknown(u) => Self::Unknown(u.to_string()),
}
}
}
impl ChannelDataType {
fn into_api(self) -> Option<ApiSeriesDataType> {
Some(match self {
Self::Double => ApiSeriesDataType::Double,
Self::Int => ApiSeriesDataType::Int,
Self::Uint => ApiSeriesDataType::Uint,
Self::String => ApiSeriesDataType::String,
Self::Log => ApiSeriesDataType::Log,
Self::DoubleArray => ApiSeriesDataType::DoubleArray,
Self::StringArray => ApiSeriesDataType::StringArray,
Self::Struct => ApiSeriesDataType::Struct,
Self::Video => ApiSeriesDataType::Video,
Self::Unknown(_) => return None,
})
}
pub(crate) fn into_nominal_data_type(self) -> Result<NominalDataType> {
Ok(match self {
Self::Double => NominalDataType::Double,
Self::Int => NominalDataType::Int64,
Self::Uint => NominalDataType::Uint64,
Self::String => NominalDataType::String,
Self::Log => NominalDataType::Log,
Self::DoubleArray => NominalDataType::DoubleArray,
Self::StringArray => NominalDataType::StringArray,
Self::Struct => NominalDataType::Struct,
Self::Video => NominalDataType::Video,
Self::Unknown(data_type) => {
return Err(Error::UnsupportedChannelDataType { data_type });
}
})
}
}
#[derive(Debug, Default, Clone)]
pub struct ChannelQuery {
data_source_rids: Vec<String>,
substring_matches: Vec<String>,
data_types: Vec<ChannelDataType>,
}
impl ChannelQuery {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn data_source(mut self, rid: impl Into<String>) -> Self {
self.data_source_rids.push(rid.into());
self
}
#[must_use]
pub fn data_sources<I, S>(mut self, rids: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.data_source_rids
.extend(rids.into_iter().map(Into::into));
self
}
#[must_use]
pub fn substring_match(mut self, text: impl Into<String>) -> Self {
self.substring_matches.push(text.into());
self
}
#[must_use]
pub fn data_type(mut self, data_type: ChannelDataType) -> Self {
self.data_types.push(data_type);
self
}
pub(crate) fn substring_match_filters(&self) -> &[String] {
&self.substring_matches
}
pub(crate) fn into_parts(self) -> Result<ChannelSearchParts> {
let data_source_rids = self
.data_source_rids
.iter()
.map(|s| parse_rid::<DataSourceRid>(s).map_err(crate::Error::from))
.collect::<Result<BTreeSet<_>>>()?;
let data_types = self
.data_types
.into_iter()
.filter_map(ChannelDataType::into_api)
.collect::<BTreeSet<_>>();
Ok(ChannelSearchParts {
data_source_rids,
substring_matches: self.substring_matches,
data_types,
})
}
}
pub(crate) struct ChannelSearchParts {
pub data_source_rids: BTreeSet<DataSourceRid>,
pub substring_matches: Vec<String>,
pub data_types: BTreeSet<ApiSeriesDataType>,
}
#[derive(Debug, Clone)]
pub struct ChannelUpdate {
description: Option<String>,
unit_update: Option<UnitUpdate>,
data_type: ChannelDataType,
}
#[derive(Debug, Clone)]
enum UnitUpdate {
Set(String),
Clear,
}
impl ChannelUpdate {
pub fn new(data_type: ChannelDataType) -> Self {
Self {
description: None,
unit_update: None,
data_type,
}
}
#[must_use]
pub fn description(mut self, value: impl Into<String>) -> Self {
self.description = Some(value.into());
self
}
#[must_use]
pub fn unit(mut self, symbol: impl Into<String>) -> Self {
self.unit_update = Some(UnitUpdate::Set(symbol.into()));
self
}
#[must_use]
pub fn clear_unit(mut self) -> Self {
self.unit_update = Some(UnitUpdate::Clear);
self
}
pub(crate) fn into_series_metadata_request(
self,
data_source_rid: &str,
name: &str,
) -> Result<CreateSeriesMetadataRequest> {
let channel = ApiChannel(name.to_string());
let data_type = self.data_type.clone().into_nominal_data_type()?;
let locator =
LocatorTemplate::Nominal(NominalLocatorTemplate::new(channel.clone(), data_type));
let mut b = CreateSeriesMetadataRequest::builder()
.channel(channel)
.data_source_rid(parse_rid::<DataSourceRid>(data_source_rid)?)
.locator(locator);
if let Some(d) = self.description {
b = b.description(d);
}
if let Some(UnitUpdate::Set(symbol)) = self.unit_update {
b = b.unit(ApiUnit(symbol));
}
Ok(b.build())
}
}