use crate::api::ColumnAffinity;
#[derive(Debug, PartialEq, Eq)]
pub enum Argument {
Column(ColumnDeclaration),
Config(ConfigOption),
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ColumnDeclaration {
pub name: String,
pub declared_type: Option<String>,
pub constraints: Option<String>,
}
impl ColumnDeclaration {
fn new(
name: &str,
declared_type: Option<&str>,
constraints: Option<&str>,
) -> ColumnDeclaration {
ColumnDeclaration {
name: name.to_owned(),
declared_type: declared_type.map(|d| d.to_owned()),
constraints: constraints.map(|d| d.to_owned()),
}
}
pub fn affinity(&self) -> ColumnAffinity {
match &self.declared_type {
Some(declared_type) => ColumnAffinity::from_declared_type(declared_type.as_str()),
None => crate::api::ColumnAffinity::Blob,
}
}
pub fn vtab_declaration(&self) -> String {
format!(
"'{}' {}",
self.name,
self.declared_type.as_ref().map_or("", |d| d.as_str())
)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ConfigOption {
pub key: String,
pub value: ConfigOptionValue,
}
#[derive(Debug, PartialEq, Eq)]
pub enum ConfigOptionValue {
Quoted(String),
SqliteParameter(String),
Bareword(String),
}
pub fn parse_argument(argument: &str) -> std::result::Result<Argument, String> {
match arg_is_config_option(argument) {
Ok(Some(config_option)) => return Ok(Argument::Config(config_option)),
Ok(None) => (),
Err(err) => return Err(err),
};
match arg_is_column_declaration(argument) {
Ok(Some(column_declaration)) => return Ok(Argument::Column(column_declaration)),
Ok(None) => (),
Err(err) => return Err(err),
};
Err("argument is neither a configuration option or column declaration.".to_owned())
}
fn arg_is_config_option(arg: &str) -> Result<Option<ConfigOption>, String> {
let mut split = arg.split('=');
let key = match split.next() {
Some(k) => k,
None => return Ok(None),
};
let value = match split.next() {
Some(k) => k,
None => return Ok(None),
};
Ok(Some(ConfigOption {
key: key.to_owned(),
value: parse_config_option_value(key.to_string(), value)?,
}))
}
fn parse_config_option_value(key: String, value: &str) -> Result<ConfigOptionValue, String> {
let value = value.trim();
match value.chars().next() {
Some('\'') | Some('"') => {
let mut chars = value.chars();
chars.next();
chars.next_back();
Ok(ConfigOptionValue::Quoted(chars.as_str().to_owned()))
}
Some(':') | Some('@') => {
Ok(ConfigOptionValue::SqliteParameter(value.to_owned()))
}
Some(_) => {
Ok(ConfigOptionValue::Bareword(value.to_owned()))
}
None => Err(format!("Empty value for key '{}'", key)),
}
}
pub fn arg_is_column_declaration(arg: &str) -> Result<Option<ColumnDeclaration>, String> {
if arg.trim().is_empty() {
return Ok(None);
}
let mut split = arg.split(' ');
let name = split.next().ok_or("asdf")?;
let declared_type = split.next();
let constraints = None;
Ok(Some(ColumnDeclaration::new(
name,
declared_type,
constraints,
)))
}
#[cfg(test)]
mod tests {
use crate::vtab_argparse::*;
#[test]
fn test_parse_argument() {
assert_eq!(
parse_argument("name text"),
Ok(Argument::Column(ColumnDeclaration::new(
"name",
Some("text"),
None,
)))
);
assert_eq!(
parse_argument("name"),
Ok(Argument::Column(
ColumnDeclaration::new("name", None, None,)
))
);
assert_eq!(
parse_argument("option='quoted'"),
Ok(Argument::Config(ConfigOption {
key: "option".to_owned(),
value: ConfigOptionValue::Quoted("quoted".to_owned())
}))
);
assert_eq!(
parse_argument("option=:param"),
Ok(Argument::Config(ConfigOption {
key: "option".to_owned(),
value: ConfigOptionValue::SqliteParameter(":param".to_owned())
}))
);
assert_eq!(
parse_argument("option=bareword"),
Ok(Argument::Config(ConfigOption {
key: "option".to_owned(),
value: ConfigOptionValue::Bareword("bareword".to_owned())
}))
);
}
}