Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 57 additions & 117 deletions src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,114 +28,27 @@ use crate::parser::compiled;
/// A capability database.
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct Database {
name: String,
aliases: Vec<String>,
description: String,
inner: HashMap<String, Value, BuildHasherDefault<FnvHasher>>,
}

/// Builder for a new `Database`.
#[derive(Default, Debug)]
pub struct Builder {
name: Option<String>,
aliases: Vec<String>,
description: Option<String>,
inner: HashMap<String, Value, BuildHasherDefault<FnvHasher>>,
}

impl Builder {
/// Build the database.
pub fn build(self) -> Result<Database, ()> {
Ok(Database {
name: self.name.ok_or(())?,
aliases: self.aliases,
description: self.description.ok_or(())?,
inner: self.inner,
})
}

/// Set the terminal name.
pub fn name<T: Into<String>>(&mut self, name: T) -> &mut Self {
self.name = Some(name.into());
self
}

/// Set the terminal aliases.
pub fn aliases<T, I>(&mut self, iter: I) -> &mut Self
where
T: Into<String>,
I: IntoIterator<Item = T>,
{
self.aliases = iter.into_iter().map(|a| a.into()).collect();
self
}

/// Set the terminal description.
pub fn description<T: Into<String>>(&mut self, description: T) -> &mut Self {
self.description = Some(description.into());
self
}

/// Set a capability.
///
/// ## Example
///
/// ```
/// use terminfo::{Database, capability as cap};
///
/// let mut info = Database::new();
/// info.name("foo");
/// info.description("foo terminal");
///
/// // Set the amount of available colors.
/// info.set(cap::MaxColors(16));
///
/// info.build().unwrap();
/// ```
pub fn set<'a, C: Capability<'a>>(&'a mut self, value: C) -> &mut Self {
if !self.inner.contains_key(C::name()) {
if let Some(value) = C::into(value) {
self.inner.insert(C::name().into(), value);
}
}

self
}
/// The terminal name.
pub name: String,

/// Set a raw capability.
///
/// ## Example
///
/// ```
/// use terminfo::{Database, capability as cap};
///
/// let mut info = Database::new();
/// info.name("foo");
/// info.description("foo terminal");
///
/// // Set the amount of available colors.
/// info.raw("colors", 16);
///
/// info.build().unwrap();
/// ```
pub fn raw<S: AsRef<str>, V: Into<Value>>(&mut self, name: S, value: V) -> &mut Self {
let name = name.as_ref();
let name = names::ALIASES.get(name).copied().unwrap_or(name);
/// Aliases of the terminal.
pub aliases: Vec<String>,

if !self.inner.contains_key(name) {
self.inner.insert(name.into(), value.into());
}
/// The terminal description.
pub description: String,

self
}
inner: HashMap<String, Value, BuildHasherDefault<FnvHasher>>,
}

impl Database {
/// Create a database builder for constucting a database.
// Clippy is right, the naming is is unconventional, but it’s probably not worth changing
#[allow(clippy::new_ret_no_self)]
pub fn new() -> Builder {
Builder::default()
/// Create a new empty terminfo database with its name and description.
pub fn new<N: Into<String>, D: Into<String>>(name: N, description: D) -> Self {
Self {
name: name.into(),
aliases: Vec::new(),
description: description.into(),
inner: HashMap::default(),
}
}

/// Load a database from the current environment.
Expand Down Expand Up @@ -230,21 +143,6 @@ impl Database {
}
}

/// The terminal name.
pub fn name(&self) -> &str {
&self.name
}

/// The terminal aliases.
pub fn aliases(&self) -> &[String] {
&self.aliases
}

/// The terminal description.
pub fn description(&self) -> &str {
&self.description
}

/// Get a capability.
///
/// ## Example
Expand All @@ -259,6 +157,24 @@ impl Database {
C::from(self.inner.get(C::name()))
}

/// Set a capability.
///
/// ## Example
///
/// ```
/// let mut info = terminfo::Database::new("foo", "foo terminal");
///
/// // Set the amount of available colors.
/// info.set(terminfo::capability::MaxColors(16));
/// ```
pub fn set<'a, C: Capability<'a>>(&mut self, value: C) {
if !self.inner.contains_key(C::name()) {
if let Some(value) = C::into(value) {
self.inner.insert(C::name().into(), value);
}
}
}

/// Get a capability by name.
///
/// ## Note
Expand All @@ -280,4 +196,28 @@ impl Database {

self.inner.get(name)
}

/// Set a raw capability.
///
/// ## Note
///
/// This interface only makes sense for extended capabilities since they
/// don't have standardized types.
///
/// ## Example
///
/// ```
/// let mut info = terminfo::Database::new("foo", "foo terminal");
///
/// // Set the amount of available colors.
/// info.set_raw("colors", 16);
/// ```
pub fn set_raw<S: AsRef<str>, V: Into<Value>>(&mut self, name: S, value: V) {
let name = name.as_ref();
let name = names::ALIASES.get(name).copied().unwrap_or(name);

if !self.inner.contains_key(name) {
self.inner.insert(name.into(), value.into());
}
}
}
32 changes: 15 additions & 17 deletions src/parser/compiled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,22 @@ impl<'a> From<Database<'a>> for crate::Database {
let mut names = source
.names
.split(|&c| c == b'|')
.map(|s| unsafe { str::from_utf8_unchecked(s) })
.map(|s| s.trim())
.collect::<Vec<_>>();
.map(|s| (unsafe { str::from_utf8_unchecked(s) }).trim().to_owned());

let mut database = crate::Database::new();

database.name(names.remove(0)).description(names.pop().unwrap()).aliases(names);
let mut database = crate::Database::new(names.next().unwrap(), "");
database.aliases = names.collect();
database.description = database.aliases.pop().unwrap();

for (index, _) in source.standard.booleans.iter().enumerate().filter(|&(_, &value)| value) {
if let Some(&name) = names::BOOLEAN.get(&(index as u16)) {
database.raw(name, Value::True);
database.set_raw(name, Value::True);
}
}

for (index, &value) in source.standard.numbers.iter().enumerate().filter(|&(_, &n)| n >= 0)
{
if let Some(&name) = names::NUMBER.get(&(index as u16)) {
database.raw(name, Value::Number(value));
database.set_raw(name, Value::Number(value));
}
}

Expand All @@ -62,7 +60,7 @@ impl<'a> From<Database<'a>> for crate::Database {
let string = &source.standard.table[offset as usize..];
let edge = string.iter().position(|&c| c == 0).unwrap();

database.raw(name, Value::String(Vec::from(&string[..edge])));
database.set_raw(name, Value::String(Vec::from(&string[..edge])));
}
}

Expand All @@ -75,25 +73,25 @@ impl<'a> From<Database<'a>> for crate::Database {
.collect::<Vec<_>>();

for (index, _) in extended.booleans.iter().enumerate().filter(|&(_, &value)| value) {
database.raw(names[index], Value::True);
database.set_raw(names[index], Value::True);
}

for (index, &value) in extended.numbers.iter().enumerate().filter(|&(_, &n)| n >= 0) {
database.raw(names[extended.booleans.len() + index], Value::Number(value));
database.set_raw(names[extended.booleans.len() + index], Value::Number(value));
}

for (index, &offset) in extended.strings.iter().enumerate().filter(|&(_, &n)| n >= 0) {
let string = &extended.table[offset as usize..];
let edge = string.iter().position(|&c| c == 0).unwrap();

database.raw(
database.set_raw(
names[extended.booleans.len() + extended.numbers.len() + index],
Value::String(Vec::from(&string[..edge])),
);
}
}

database.build().unwrap()
database
}
}

Expand Down Expand Up @@ -218,18 +216,18 @@ mod test {

#[test]
fn name() {
load("tests/cancer-256color", |db| assert_eq!("cancer-256color", db.name()));
load("tests/cancer-256color", |db| assert_eq!("cancer-256color", db.name));
}

#[test]
fn aliases() {
load("tests/st-256color", |db| assert_eq!(vec!["stterm-256color"], db.aliases()));
load("tests/st-256color", |db| assert_eq!(vec!["stterm-256color"], db.aliases));
}

#[test]
fn description() {
load("tests/cancer-256color", |db| {
assert_eq!("terminal cancer with 256 colors", db.description())
assert_eq!("terminal cancer with 256 colors", db.description)
});
}

Expand All @@ -253,6 +251,6 @@ mod test {

#[test]
fn bigger_numbers() {
load("tests/xterm-256color", |db| assert_eq!("xterm-256color", db.name()));
load("tests/xterm-256color", |db| assert_eq!("xterm-256color", db.name));
}
}