Skip to content

Commit

Permalink
adds implementation of Catalog (#448)
Browse files Browse the repository at this point in the history
  • Loading branch information
desaikd committed Nov 22, 2022
1 parent b01a603 commit aa33f0f
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
124 changes: 124 additions & 0 deletions src/catalog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use crate::result::IonResult;
use crate::result::{illegal_operation, illegal_operation_raw};
use crate::shared_symbol_table::SharedSymbolTable;
use std::collections::{BTreeMap, HashMap};

/// A Catalog is a collection of Shared Symbol Tables.
/// For more information about the concept of a catalog,
/// see [the `symbols` section of the specification](https://amzn.github.io/ion-docs/docs/symbols.html#the-catalog).
pub trait Catalog {
/// Returns the Shared Symbol Table with given table name
/// If a table with the given name doesn't exists or if the table name is an empty string
/// then returns an error
/// If a table with multiple versions exists for the given name then it will return the latest version of table
fn get_table(&self, name: &str) -> IonResult<SharedSymbolTable>;
/// Returns the Shared Symbol Table with given table name and version
/// If a table with given name and version doesn't exists then it returns an error
fn get_table_with_version(&self, name: &str, version: usize) -> IonResult<SharedSymbolTable>;
}

struct MapCatalog {
tables_by_name: HashMap<String, BTreeMap<usize, SharedSymbolTable>>,
}

impl MapCatalog {
pub fn new() -> Self {
Self {
tables_by_name: HashMap::new(),
}
}
}

impl Catalog for MapCatalog {
fn get_table(&self, name: &str) -> IonResult<SharedSymbolTable> {
if name.is_empty() {
return illegal_operation("symbol table with empty name doesn't exists");
}

let versions: &BTreeMap<usize, SharedSymbolTable> =
self.tables_by_name.get(name).ok_or_else(|| {
illegal_operation_raw(format!("symbol table with name: {} does not exist", name))
})?;

let (_highest_version, table) = versions.iter().rev().next().ok_or_else(|| {
illegal_operation_raw(format!("symbol table with name: {} does not exist", name))
})?;
Ok(table.to_owned())
}

fn get_table_with_version(&self, name: &str, version: usize) -> IonResult<SharedSymbolTable> {
if name.is_empty() {
return illegal_operation("symbol table with empty name doesn't exists");
}

let versions: &BTreeMap<usize, SharedSymbolTable> =
self.tables_by_name.get(name).ok_or_else(|| {
illegal_operation_raw(format!("symbol table with name: {} does not exist", name))
})?;

let table = versions.get(&version).ok_or_else(|| {
illegal_operation_raw(format!("symbol table with name: {} does not exist", name))
})?;
Ok(table.to_owned())
}
}

impl MapCatalog {
/// Adds a Shared Symbol Table with name into the Catalog
fn put_table(&mut self, table: SharedSymbolTable) {
match self.tables_by_name.get_mut(table.name()) {
None => {
let mut versions: BTreeMap<usize, SharedSymbolTable> = BTreeMap::new();
let table_name = table.name().to_owned();
versions.insert(table.version(), table);
self.tables_by_name.insert(table_name, versions);
}
Some(versions) => {
versions.insert(table.version(), table);
}
};
}
}

#[cfg(test)]
mod tests {
use crate::catalog::{Catalog, MapCatalog};
use crate::shared_symbol_table::SharedSymbolTable;
use crate::IonResult;

#[test]
fn get_table_with_name_test() -> IonResult<()> {
let sst = SharedSymbolTable::new(
"T".to_string(),
1,
vec![Some("true".to_string()), Some("false".to_string())],
)?;
let mut catalog = MapCatalog::new();
catalog.put_table(sst);

// get table with name "T"
assert!(catalog.get_table("T").is_ok());

// verify that table with name "S" doesn't exist
assert!(catalog.get_table("S").is_err());
Ok(())
}

#[test]
fn get_table_with_version_test() -> IonResult<()> {
let sst = SharedSymbolTable::new(
"T".to_string(),
1,
vec![Some("true".to_string()), Some("false".to_string())],
)?;
let mut catalog = MapCatalog::new();
catalog.put_table(sst);

// get table with name "T" and version 1
assert!(catalog.get_table_with_version("T", 1).is_ok());

// verify that table with name "T" and version 2 doesn't exist
assert!(catalog.get_table_with_version("T", 2).is_err());
Ok(())
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ pub mod text;
pub mod types;
pub mod value;

mod catalog;
pub mod constants;
pub mod ion_eq;
mod raw_symbol_token;
mod raw_symbol_token_ref;
mod reader;
mod shared_symbol_table;
mod stream_reader;
mod symbol;
mod symbol_ref;
Expand Down
37 changes: 37 additions & 0 deletions src/shared_symbol_table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::result::illegal_operation;
use crate::IonResult;

#[derive(Debug, Clone, PartialEq, Eq)]
/// Stores [SharedSymbolTable] with the table name, version and imports
/// For more information on [SharedSymbolTable]: https://amzn.github.io/ion-docs/docs/symbols.html#shared-symbol-tables
pub struct SharedSymbolTable {
name: String,
version: usize,
symbols: Vec<Option<String>>,
}

impl SharedSymbolTable {
pub fn new(name: String, version: usize, imports: Vec<Option<String>>) -> IonResult<Self> {
// As per Ion Specification, the name field should be a string with length at least one.
// If the field has any other value, then materialization of this symbol table must fail.
if name.is_empty() {
return illegal_operation("shared symbol table with empty name is not allowed");
}

Ok(Self {
name,
version,
symbols: imports,
})
}

/// Returns the version of this [SharedSymbolTable]
pub fn version(&self) -> usize {
self.version
}

/// Returns the name of this [SharedSymbolTable]
pub fn name(&self) -> &str {
&self.name
}
}

0 comments on commit aa33f0f

Please sign in to comment.