Enabled logging, using log & env_logger. Fixes #54

This commit is contained in:
Aaronepower 2016-09-20 21:49:47 +01:00
parent 9ec7499a2d
commit 2e5c46d71a
9 changed files with 226 additions and 90 deletions

165
Cargo.lock generated
View file

@ -4,18 +4,28 @@ version = "4.2.0"
dependencies = [
"clap 2.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
"encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"maplit 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_cbor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_codegen 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_codegen 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_yaml 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "aho-corasick"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -25,10 +35,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "aster"
version = "0.25.0"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -53,7 +63,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -132,6 +142,14 @@ name = "encoding_index_tests"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "env_logger"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "glob"
version = "0.2.11"
@ -153,7 +171,7 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -166,6 +184,14 @@ name = "maplit"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memchr"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.1.35"
@ -176,27 +202,27 @@ name = "num_cpus"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quasi"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quasi_codegen"
version = "0.18.0"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aster 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.42.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"aster 0.26.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -204,12 +230,12 @@ name = "rand"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rayon"
version = "0.4.0"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -217,6 +243,23 @@ dependencies = [
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex"
version = "0.1.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
"regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "regex-syntax"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-serialize"
version = "0.3.19"
@ -229,7 +272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "0.8.4"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -243,35 +286,35 @@ dependencies = [
[[package]]
name = "serde_codegen"
version = "0.8.4"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aster 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_codegen 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_codegen_internals 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.42.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"aster 0.26.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"quasi_codegen 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_codegen_internals 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_codegen_internals"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "0.8.1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -279,7 +322,7 @@ name = "serde_yaml"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -290,29 +333,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syntex"
version = "0.42.2"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_syntax 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syntex_errors"
version = "0.42.0"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syntex_pos"
version = "0.42.0"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
@ -320,15 +363,15 @@ dependencies = [
[[package]]
name = "syntex_syntax"
version = "0.42.0"
version = "0.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_errors 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syntex_pos 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -355,15 +398,32 @@ name = "term_size"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread-id"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thread_local"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "toml"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -381,6 +441,11 @@ name = "unicode-xid"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "utf8-ranges"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.6.0"
@ -388,7 +453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "walkdir"
version = "0.1.5"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -24,14 +24,19 @@ version = "0.8.0"
[dependencies]
encoding = "0.2.33"
glob = "~0.2.11"
log = "0.3.6"
maplit = "~0.1.3"
rayon = "~0.4.0"
rayon = "0.4.2"
walkdir = "~0.1.5"
[dependencies.clap]
features = ["yaml"]
version = "~2.10.0"
[dependencies.env_logger]
features = [""]
version = "~0.3.5"
[dependencies.rustc-serialize]
optional = true
version = "~0.3.19"

View file

@ -47,3 +47,7 @@ args:
possible_values: [files, lines, blanks, code, comments]
short: s
takes_value: true
- verbose:
help: 'Set verbose output level: 1 for File IO errors 2: for unknown extensions'
short: v
multiple: true

View file

@ -373,7 +373,10 @@ impl LanguageType {
"xml" => Some(Xml),
"yaml" | "yml" => Some(Yaml),
"zsh" => Some(Zsh),
_ => None,
extension => {
info!("Unknown extension: {}", extension);
None
},
}
} else {
None

View file

@ -33,9 +33,9 @@ const TOML_ERROR: &'static str = "Tokei was not compiled with the `toml-io` flag
#[cfg(not(feature = "yaml"))]
const YAML_ERROR: &'static str = "Tokei was not compiled with the `yaml` flag.";
fn count_files(language_tuple: &mut (&LanguageType, &mut Language)) {
fn count_files(mut language_tuple: (&LanguageType, &mut Language)) {
let &mut (name, ref mut language) = language_tuple;
let (name, ref mut language) = language_tuple;
if language.files.is_empty() {
return;
@ -49,12 +49,12 @@ fn count_files(language_tuple: &mut (&LanguageType, &mut Language)) {
let mut quote;
for file in files {
let mut stats = Stats::new(opt_or_cont!(file.to_str()));
let mut stats = Stats::new(opt_warn!(file.to_str(), "Couldn't convert path to String."));
stack.clear();
contents.clear();
quote = None;
rs_or_cont!(rs_or_cont!(File::open(file)).read_to_end(&mut contents));
rs_warn!(rs_warn!(File::open(file)).read_to_end(&mut contents));
let text = match encoding::decode(&contents, DecoderTrap::Replace, encoding::all::UTF_8) {
@ -241,12 +241,8 @@ impl Languages {
pub fn get_statistics<'a, I>(&mut self, paths: I, ignored: I)
where I: Into<Cow<'a, [&'a str]>>
{
fs::get_all_files(paths.into(), ignored.into(), &mut self.inner);
let mut language_iter: Vec<_> = self.inner.iter_mut().collect();
language_iter.par_iter_mut().for_each(count_files);
self.inner.par_iter_mut().for_each(count_files);
}
/// Constructs a new, blank `Languages`.

View file

@ -50,6 +50,8 @@
//! }
//! ```
#[macro_use]
extern crate log;
extern crate encoding;
extern crate glob;
#[macro_use]

View file

@ -14,6 +14,16 @@ use walkdir::{WalkDir, WalkDirIterator};
use language::{Language, LanguageType};
use language::LanguageType::*;
macro_rules! get_language {
($languages:expr, $entry:expr) => {
if let Some(language_type) = LanguageType::from_extension($entry) {
opt_warn!($languages.get_mut(&language_type), "Unknown Language? Shouldn't happen.")
} else {
continue;
}
}
}
pub fn get_all_files<'a>(paths: Cow<'a, [&'a str]>,
ignored_directories: Cow<'a, [&'a str]>,
languages: &mut BTreeMap<LanguageType, Language>) {
@ -24,19 +34,19 @@ pub fn get_all_files<'a>(paths: Cow<'a, [&'a str]>,
if let Err(_) = Path::new(path).metadata() {
if let Ok(paths) = glob(path) {
'path: for path in paths {
let path = rs_or_cont!(path);
let path = rs_warn!(path);
let path_str = opt_warn!(path.to_str(),
"DURING FILE LOOKUP: Couldn't convert path to string.");
for ig in &*ignored_directories {
if opt_or_cont!(path.to_str()).contains(ig) {
if path_str.contains(ig) {
continue 'path;
}
}
let mut language = if opt_or_cont!(path.to_str()).contains("Makefile") {
let mut language = if path_str.contains("Makefile") {
languages.get_mut(&Makefile).unwrap()
} else {
opt_or_cont!(
languages.get_mut(
&opt_or_cont!(LanguageType::from_extension(&path))))
get_language!(languages, &path)
};
language.files.push(path.to_owned());
@ -53,14 +63,14 @@ pub fn get_all_files<'a>(paths: Cow<'a, [&'a str]>,
});
for entry in walker {
let entry = rs_or_cont!(entry);
let entry = rs_warn!(entry);
let mut language = if opt_or_cont!(entry.path().to_str()).contains("Makefile") {
let mut language = if opt_warn!(entry.path().to_str(),
"Walkdir: Couldn't convert path to string")
.contains("Makefile") {
languages.get_mut(&Makefile).unwrap()
} else {
opt_or_cont!(
languages.get_mut(&opt_or_cont!(LanguageType::from_extension(entry.path())))
)
get_language!(languages, entry.path())
};
language.files.push(entry.path().to_owned());
@ -108,7 +118,10 @@ pub fn get_filetype_from_shebang<P: AsRef<Path>>(file: P) -> Option<&'static str
match words.next() {
Some("python") | Some("python2") | Some("python3") => Some("py"),
Some("sh") => Some("sh"),
_ => None,
env => {
info!("Unknown environment: {:?}", env);
None
}
}
}
_ => None,

View file

@ -2,20 +2,52 @@
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
macro_rules! opt_or_cont {
($option:expr) => {
macro_rules! opt_info {
($option:expr, $message:expr) => {
match $option {
Some(result) => result,
None => continue,
None => {
info!($message);
continue;
},
}
}
}
macro_rules! rs_or_cont {
macro_rules! rs_info {
($result:expr, $message: expr) => {
match $result {
Ok(result) => result,
Err(error) => {
use std::error::Error;
info!("{}", error.description());
continue;
},
}
}
}
macro_rules! opt_warn {
($option:expr, $message:expr) => {
match $option {
Some(result) => result,
None => {
warn!($message);
continue;
},
}
}
}
macro_rules! rs_warn {
($result:expr) => {
match $result {
Ok(result) => result,
Err(_) => continue,
Err(error) => {
use std::error::Error;
warn!("{}", error.description());
continue;
},
}
}
}

View file

@ -4,6 +4,8 @@
#[macro_use]
extern crate clap;
extern crate log;
extern crate env_logger;
// #[cfg(feature = "cbor")]
// extern crate serde_cbor;
#[cfg(feature = "json")]
@ -24,6 +26,8 @@ use std::time::Duration;
use std::sync::mpsc::channel;
use clap::App;
use log::LogLevelFilter;
use env_logger::LogBuilder;
// #[cfg(feature = "cbor")]
// use rustc_serialize::hex::FromHex;
@ -52,6 +56,7 @@ fn main() {
let input_option = matches.value_of("file_input");
let output_option = matches.value_of("output");
let language_option = matches.is_present("languages");
let verbose_option = matches.occurrences_of("verbose");
let sort_option = matches.value_of("sort");
let ignored_directories = {
let mut ignored_directories: Vec<&str> = vec![".git"];
@ -63,6 +68,20 @@ fn main() {
ignored_directories
};
if verbose_option != 0 {
let mut builder = LogBuilder::new();
match verbose_option {
1 => {
builder.filter(None, LogLevelFilter::Warn);
}
2 => {
builder.filter(None, LogLevelFilter::Info);
}
_ => {/* NOOP */}
}
builder.init().unwrap();
}
let mut languages = Languages::new();
if language_option {
@ -78,18 +97,6 @@ fn main() {
add_input(input, &mut languages);
}
if output_option == None {
println!("{}", ROW);
println!(" {:<12} {:>12} {:>12} {:>12} {:>12} {:>12}",
"Language",
"Files",
"Lines",
"Code",
"Comments",
"Blanks");
println!("{}", ROW);
}
let mut total = Language::new_blank();
let print_animation = output_option == None;
@ -114,6 +121,15 @@ fn main() {
languages.get_statistics(paths, ignored_directories);
if output_option == None {
println!("{}", ROW);
println!(" {:<12} {:>12} {:>12} {:>12} {:>12} {:>12}",
"Language",
"Files",
"Lines",
"Code",
"Comments",
"Blanks");
println!("{}", ROW);
}
for (name, language) in &languages {