First draft

This commit is contained in:
Aaronepower 2016-07-17 22:17:49 +01:00
parent 4e671b5fa9
commit bfbfaa60fc
9 changed files with 391 additions and 269 deletions

2
Cargo.lock generated
View file

@ -1,6 +1,6 @@
[root] [root]
name = "tokei" name = "tokei"
version = "3.0.1" version = "3.0.2"
dependencies = [ dependencies = [
"clap 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -21,9 +21,18 @@ name = "tokei"
path = "src/main.rs" path = "src/main.rs"
doc = false doc = false
[features]
default = []
io = ["serde_codegen", "serde", "serializable_enum"]
json = ["io", "serde_json"]
cbor = ["io", "rustc-serialize","serde_cbor"]
toml-io = ["io", "toml/serde"]
yaml = ["io", "serde_yaml"]
all = ["json", "cbor", "toml-io", "yaml"]
# For building serde in stable. # For building serde in stable.
[build-dependencies] [build-dependencies]
serde_codegen = "0.7.10" serde_codegen = {version = "0.7.10", optional = true}
# Dependencies, and why they are used. # Dependencies, and why they are used.
# - Clap: For CLI argument parsing. # - Clap: For CLI argument parsing.
@ -43,15 +52,16 @@ clap = {version = "2.5.1", features = ["yaml"]}
glob = "0.2.11" glob = "0.2.11"
maplit = "0.1.3" maplit = "0.1.3"
rayon = "0.3.1" rayon = "0.3.1"
rustc-serialize = "0.3.19" rustc-serialize = { version = "0.3.19", optional = true }
serde = "0.7.10" serde = { version = "0.7.10", optional = true }
serde_cbor = "0.3.3" serde_cbor = {version = "0.3.3", optional = true }
serde_json = "0.7.1" serde_json = { version = "0.7.1", optional = true }
serde_yaml = "0.2.5" serde_yaml = { version = "0.2.5", optional = true }
serializable_enum = "0.3.0" serializable_enum = { version = "0.3.0", optional = true }
walkdir = "0.1.5" walkdir = "0.1.5"
[dependencies.toml] [dependencies.toml]
version = "0.1.30" version = "0.1.30"
default-features = false default-features = false
features = ["serde"] features = ["serde"]
optional = true

View file

@ -1,13 +1,23 @@
#[cfg(feature = "io")]
extern crate serde_codegen; extern crate serde_codegen;
use std::env; use std::env;
use std::path::Path; use std::path::Path;
pub fn main() { pub fn main() {
expand();
}
#[cfg(feature = "io")]
fn expand() {
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
let src = Path::new("src/lib/lib.rs.in"); let src = Path::new("src/lib/lib.rs.in");
let dst = Path::new(&out_dir).join("lib.rs"); let dst = Path::new(&out_dir).join("lib.rs.in");
serde_codegen::expand(&src, &dst).unwrap(); serde_codegen::expand(&src, &dst).unwrap();
} }
#[cfg(not(feature = "io"))]
fn expand() {}

View file

@ -6,7 +6,8 @@ use sort::Sort::*;
use stats::Stats; use stats::Stats;
/// Struct representing a single Language. /// Struct representing a single Language.
#[derive(Clone, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] #[cfg_attr(feature = "io", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct Language { pub struct Language {
/// Number of blank lines. /// Number of blank lines.
pub blanks: usize, pub blanks: usize,
@ -15,20 +16,20 @@ pub struct Language {
/// Number of comments(both single, and multi-line) /// Number of comments(both single, and multi-line)
pub comments: usize, pub comments: usize,
/// A collection of files to be analysed. /// A collection of files to be analysed.
#[serde(skip_deserializing, skip_serializing)] #[cfg_attr(feature = "io", serde(skip_deserializing, skip_serializing))]
pub files: Vec<PathBuf>, pub files: Vec<PathBuf>,
/// A collection of statistics based on the files provide from `files` /// A collection of statistics based on the files provide from `files`
pub stats: Vec<Stats>, pub stats: Vec<Stats>,
/// Number of total lines. /// Number of total lines.
pub lines: usize, pub lines: usize,
/// A collection of single line comments in the language. ie. `//` in Rust. /// A collection of single line comments in the language. ie. `//` in Rust.
#[serde(skip_deserializing, skip_serializing)] #[cfg_attr(feature = "io", serde(skip_deserializing, skip_serializing))]
pub line_comment: Vec<&'static str>, pub line_comment: Vec<&'static str>,
/// A collection of tuples representing the start and end of multi line comments. ie. `/* comment */` in Rust. /// A collection of tuples representing the start and end of multi line comments. ie. `/* comment */` in Rust.
#[serde(skip_deserializing, skip_serializing)] #[cfg_attr(feature = "io", serde(skip_deserializing, skip_serializing))]
pub multi_line: Vec<(&'static str, &'static str)>, pub multi_line: Vec<(&'static str, &'static str)>,
/// Whether the language supports nested multi line comments or not. /// Whether the language supports nested multi line comments or not.
#[serde(skip_deserializing, skip_serializing)] #[cfg_attr(feature = "io", serde(skip_deserializing, skip_serializing))]
pub nested: bool, pub nested: bool,
} }

View file

@ -10,7 +10,7 @@ use std::path::Path;
use utils::*; use utils::*;
use self::LanguageType::*; use self::LanguageType::*;
serializable_enum! { #[cfg_attr(feature = "io", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum LanguageType { pub enum LanguageType {
/// ActionScript /// ActionScript
@ -162,8 +162,6 @@ serializable_enum! {
/// Zsh /// Zsh
Zsh, Zsh,
} }
LanguageTypeVisitor
}
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -175,86 +173,86 @@ impl ::std::fmt::Display for Error {
write!(f, "{:?}", self) write!(f, "{:?}", self)
} }
} }
// #[cfg(feature = "io")]
impl_as_ref_from_str! { // impl_as_ref_from_str! {
LanguageType { // LanguageType {
ActionScript => "ActionScript", // ActionScript => "ActionScript",
Assembly => "Assembly", // Assembly => "Assembly",
Autoconf => "Autoconf", // Autoconf => "Autoconf",
Bash => "BASH", // Bash => "BASH",
Batch => "Batch", // Batch => "Batch",
C => "C", // C => "C",
CHeader => "C Header", // CHeader => "C Header",
Clojure => "Clojure", // Clojure => "Clojure",
CoffeeScript => "CoffeeScript", // CoffeeScript => "CoffeeScript",
ColdFusion => "ColdFusion", // ColdFusion => "ColdFusion",
ColdFusionScript => "ColdFusion CFScript", // ColdFusionScript => "ColdFusion CFScript",
Coq => "Coq", // Coq => "Coq",
Cpp => "C++", // Cpp => "C++",
CppHeader => "C++ Header", // CppHeader => "C++ Header",
CSharp => "C#", // CSharp => "C#",
CShell => "C Shell", // CShell => "C Shell",
Css => "CSS", // Css => "CSS",
D => "D", // D => "D",
Dart => "Dart", // Dart => "Dart",
DeviceTree => "Device Tree", // DeviceTree => "Device Tree",
Erlang => "Erlang", // Erlang => "Erlang",
FortranLegacy => "FORTRAN Legacy", // FortranLegacy => "FORTRAN Legacy",
FortranModern => "FORTRAN Modern", // FortranModern => "FORTRAN Modern",
Go => "Go", // Go => "Go",
Haskell => "Haskell", // Haskell => "Haskell",
Html => "HTML", // Html => "HTML",
Idris => "Idris", // Idris => "Idris",
Isabelle => "Isabelle", // Isabelle => "Isabelle",
Jai => "JAI", // Jai => "JAI",
Java => "Java", // Java => "Java",
JavaScript => "JavaScript", // JavaScript => "JavaScript",
Json => "JSON", // Json => "JSON",
Jsx => "JSX", // Jsx => "JSX",
Julia => "Julia", // Julia => "Julia",
Kotlin => "Kotlin", // Kotlin => "Kotlin",
Less => "LESS", // Less => "LESS",
LinkerScript => "LD Script", // LinkerScript => "LD Script",
Lisp => "LISP", // Lisp => "LISP",
Lua => "Lua", // Lua => "Lua",
Makefile => "Makefile", // Makefile => "Makefile",
Markdown => "Markdown", // Markdown => "Markdown",
Mustache => "Mustache", // Mustache => "Mustache",
Nim => "Nim", // Nim => "Nim",
ObjectiveC => "Objective C", // ObjectiveC => "Objective C",
ObjectiveCpp => "Objective C++", // ObjectiveCpp => "Objective C++",
OCaml => "OCaml", // OCaml => "OCaml",
Oz => "Oz", // Oz => "Oz",
Pascal => "Pascal", // Pascal => "Pascal",
Perl => "Perl", // Perl => "Perl",
Polly => "Polly", // Polly => "Polly",
Php => "PHP", // Php => "PHP",
Protobuf => "Protocol Buffers", // Protobuf => "Protocol Buffers",
Prolog => "Prolog", // Prolog => "Prolog",
Python => "Python", // Python => "Python",
Qcl => "QCL", // Qcl => "QCL",
R => "R", // R => "R",
Ruby => "Ruby", // Ruby => "Ruby",
RubyHtml => "Ruby HTML", // RubyHtml => "Ruby HTML",
Rust => "Rust", // Rust => "Rust",
Sass => "Sass", // Sass => "Sass",
Scala => "Scala", // Scala => "Scala",
Sml => "Standard ML", // Sml => "Standard ML",
Sql => "SQL", // Sql => "SQL",
Swift => "Swift", // Swift => "Swift",
Tex => "TeX", // Tex => "TeX",
Text => "Plain Text", // Text => "Plain Text",
Toml => "TOML", // Toml => "TOML",
TypeScript => "TypeScript", // TypeScript => "TypeScript",
UnrealScript => "Unreal Script", // UnrealScript => "Unreal Script",
VimScript => "Vim Script", // VimScript => "Vim Script",
Wolfram => "Wolfram", // Wolfram => "Wolfram",
Xml => "XML", // Xml => "XML",
Yaml => "YAML", // Yaml => "YAML",
Zsh => "Zsh", // Zsh => "Zsh",
} // }
Error::Parse // Error::Parse
} // }
impl LanguageType { impl LanguageType {

View file

@ -9,9 +9,13 @@ use std::io::Read;
use std::iter::IntoIterator; use std::iter::IntoIterator;
use std::ops::{AddAssign, Deref, DerefMut}; use std::ops::{AddAssign, Deref, DerefMut};
#[cfg(feature = "cbor")]
use serde_cbor; use serde_cbor;
#[cfg(feature = "json")]
use serde_json; use serde_json;
#[cfg(feature = "yaml")]
use serde_yaml; use serde_yaml;
#[cfg(feature = "toml-io")]
use toml; use toml;
use rayon::prelude::*; use rayon::prelude::*;
@ -20,6 +24,11 @@ use super::{Language, LanguageType};
use super::LanguageType::*; use super::LanguageType::*;
use stats::Stats; use stats::Stats;
const CBOR_ERROR: &'static str = "Tokei was not compiled with the `cbor` flag.";
const JSON_ERROR: &'static str = "Tokei was not compiled with the `json` flag.";
const TOML_ERROR: &'static str = "Tokei was not compiled with the `toml-io` flag.";
const YAML_ERROR: &'static str = "Tokei was not compiled with the `yaml` flag.";
/// A collection of existing languages([_List of Languages_](https://github.com/Aaronepower/tokei#supported-languages)) /// A collection of existing languages([_List of Languages_](https://github.com/Aaronepower/tokei#supported-languages))
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Languages { pub struct Languages {
@ -45,12 +54,17 @@ impl Languages {
/// assert_eq!(12, languages.get_mut(&LanguageType::Rust).unwrap().code); /// assert_eq!(12, languages.get_mut(&LanguageType::Rust).unwrap().code);
/// # } /// # }
/// ``` /// ```
#[cfg(feature = "cbor")]
pub fn from_cbor<'a, I: Into<&'a [u8]>>(cbor: I) -> serde_cbor::Result<Self> { pub fn from_cbor<'a, I: Into<&'a [u8]>>(cbor: I) -> serde_cbor::Result<Self> {
let map = try!(serde_cbor::from_slice(cbor.into())); let map = try!(serde_cbor::from_slice(cbor.into()));
Ok(Self::from_previous(map)) Ok(Self::from_previous(map))
} }
#[cfg(not(feature = "cbor"))]
pub fn from_cbor<'a, I: Into<&'a [u8]>>(cbor: I) -> ! {
panic!(CBOR_ERROR)
}
/// Creates a `Languages` struct from json. /// Creates a `Languages` struct from json.
/// ///
@ -76,12 +90,18 @@ impl Languages {
/// let mut languages = Languages::from_json(json.as_bytes()).unwrap(); /// let mut languages = Languages::from_json(json.as_bytes()).unwrap();
/// assert_eq!(12, languages.get_mut(&LanguageType::Rust).unwrap().code); /// assert_eq!(12, languages.get_mut(&LanguageType::Rust).unwrap().code);
/// ``` /// ```
#[cfg(feature = "json")]
pub fn from_json<'a, I: Into<&'a [u8]>>(json: I) -> serde_json::Result<Self> { pub fn from_json<'a, I: Into<&'a [u8]>>(json: I) -> serde_json::Result<Self> {
let map = try!(serde_json::from_slice(json.into())); let map = try!(serde_json::from_slice(json.into()));
Ok(Self::from_previous(map)) Ok(Self::from_previous(map))
} }
#[cfg(not(feature = "json"))]
pub fn from_json<'a, I: Into<&'a [u8]>>(json: I) -> ! {
panic!(JSON_ERROR)
}
/// Creates a `Languages` struct from json. /// Creates a `Languages` struct from json.
/// ///
/// ```no_run /// ```no_run
@ -106,12 +126,18 @@ impl Languages {
/// ///
/// assert_eq!(12, languages.get_mut(&LanguageType::Rust).unwrap().code); /// assert_eq!(12, languages.get_mut(&LanguageType::Rust).unwrap().code);
/// ``` /// ```
#[cfg(feature = "yaml")]
pub fn from_yaml<'a, I: Into<&'a [u8]>>(yaml: I) -> serde_yaml::Result<Self> { pub fn from_yaml<'a, I: Into<&'a [u8]>>(yaml: I) -> serde_yaml::Result<Self> {
let map = try!(serde_yaml::from_slice(yaml.into())); let map = try!(serde_yaml::from_slice(yaml.into()));
Ok(Self::from_previous(map)) Ok(Self::from_previous(map))
} }
#[cfg(not(feature = "yaml"))]
pub fn from_yaml<'a, I: Into<&'a [u8]>>(yaml: I) -> ! {
panic!(YAML_ERROR)
}
fn from_previous(map: BTreeMap<LanguageType, Language>) -> Self { fn from_previous(map: BTreeMap<LanguageType, Language>) -> Self {
let mut _self = Self::new(); let mut _self = Self::new();
@ -373,10 +399,16 @@ impl Languages {
/// assert_eq!(cbor, languages.to_cbor().unwrap().to_hex()); /// assert_eq!(cbor, languages.to_cbor().unwrap().to_hex());
/// # } /// # }
/// ``` /// ```
#[cfg(feature = "cbor")]
pub fn to_cbor(&self) -> Result<Vec<u8>, serde_cbor::Error> { pub fn to_cbor(&self) -> Result<Vec<u8>, serde_cbor::Error> {
serde_cbor::to_vec(&self.remove_empty()) serde_cbor::to_vec(&self.remove_empty())
} }
#[cfg(not(feature = "cbor"))]
pub fn to_cbor(&self) -> ! {
panic!(CBOR_ERROR)
}
/// Converts `Languages` to JSON. /// Converts `Languages` to JSON.
/// ///
/// ```no_run /// ```no_run
@ -404,14 +436,26 @@ impl Languages {
/// ///
/// assert_eq!(json, languages.to_json().unwrap()); /// assert_eq!(json, languages.to_json().unwrap());
/// ``` /// ```
#[cfg(feature = "json")]
pub fn to_json(&self) -> Result<String, serde_json::Error> { pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(&self.remove_empty()) serde_json::to_string(&self.remove_empty())
} }
#[cfg(not(feature = "json"))]
pub fn to_json(&self) -> ! {
panic!(JSON_ERROR)
}
#[cfg(feature = "toml-io")]
pub fn to_toml(&self) -> String { pub fn to_toml(&self) -> String {
toml::encode_str(&self.remove_empty()) toml::encode_str(&self.remove_empty())
} }
#[cfg(not(feature = "toml-io"))]
pub fn to_toml(&self) -> ! {
panic!(TOML_ERROR)
}
/// Converts `Languages` to YAML. /// Converts `Languages` to YAML.
/// ///
/// ```no_run /// ```no_run
@ -434,9 +478,15 @@ impl Languages {
/// languages.get_statistics(&*vec!["src/lib/build.rs"], &*vec![".git"]); /// languages.get_statistics(&*vec!["src/lib/build.rs"], &*vec![".git"]);
/// ///
/// assert_eq!(yaml, languages.to_yaml().unwrap()); /// assert_eq!(yaml, languages.to_yaml().unwrap());
#[cfg(feature = "yaml")]
pub fn to_yaml(&self) -> Result<String, serde_yaml::Error> { pub fn to_yaml(&self) -> Result<String, serde_yaml::Error> {
serde_yaml::to_string(&self.remove_empty()) serde_yaml::to_string(&self.remove_empty())
} }
#[cfg(not(feature = "yaml"))]
pub fn to_yaml(&self) -> ! {
panic!(YAML_ERROR)
}
} }
impl IntoIterator for Languages { impl IntoIterator for Languages {

View file

@ -49,14 +49,24 @@
#[macro_use] #[macro_use]
extern crate maplit; extern crate maplit;
#[macro_use] #[macro_use]
#[cfg(feature = "io")]
extern crate serializable_enum; extern crate serializable_enum;
extern crate glob; extern crate glob;
extern crate rayon; extern crate rayon;
#[cfg(feature = "io")]
extern crate serde; extern crate serde;
#[cfg(feature = "cbor")]
extern crate serde_cbor; extern crate serde_cbor;
#[cfg(feature = "json")]
extern crate serde_json; extern crate serde_json;
#[cfg(feature = "yaml")]
extern crate serde_yaml; extern crate serde_yaml;
#[cfg(feature = "toml")]
extern crate toml; extern crate toml;
extern crate walkdir; extern crate walkdir;
include!(concat!(env!("OUT_DIR"), "/lib.rs")); #[cfg(feature = "io")]
include!(concat!(env!("OUT_DIR"), "/lib.rs.in"));
#[not(cfg(feature = "io"))]
include!("lib.rs.in");

View file

@ -1,7 +1,8 @@
use std::fmt; use std::fmt;
/// A struct representing the statistics of a file. /// A struct representing the statistics of a file.
#[derive(Clone, Default, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] #[cfg_attr(feature = "io", derive(Deserialize, Serialize))]
#[derive(Clone, Default, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Stats { pub struct Stats {
/// Number of blank lines within the file. /// Number of blank lines within the file.
pub blanks: usize, pub blanks: usize,

View file

@ -4,10 +4,15 @@
#[macro_use] #[macro_use]
extern crate clap; extern crate clap;
#[cfg(feature = "cbor")]
extern crate serde_cbor; extern crate serde_cbor;
#[cfg(feature = "json")]
extern crate serde_json; extern crate serde_json;
#[cfg(feature = "yaml")]
extern crate serde_yaml; extern crate serde_yaml;
#[cfg(feature = "toml-io")]
extern crate toml; extern crate toml;
#[cfg(feature = "cbor")]
extern crate rustc_serialize; extern crate rustc_serialize;
extern crate tokei; extern crate tokei;
@ -20,17 +25,38 @@ use std::time::Duration;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use clap::App; use clap::App;
#[cfg(feature = "cbor")]
use rustc_serialize::hex::FromHex; use rustc_serialize::hex::FromHex;
use tokei::{Languages, Language, LanguageType}; use tokei::{Languages, Language, LanguageType};
use tokei::Sort::*; use tokei::Sort::*;
pub const ROW: &'static str = "-------------------------------------------------------------------\ const ROW: &'static str = "-------------------------------------------------------------------\
------------"; ------------";
pub const BLANKS: &'static str = "blanks"; const BLANKS: &'static str = "blanks";
pub const COMMENTS: &'static str = "comments"; const COMMENTS: &'static str = "comments";
pub const CODE: &'static str = "code"; const CODE: &'static str = "code";
pub const FILES: &'static str = "files"; const FILES: &'static str = "files";
pub const LINES: &'static str = "lines"; const LINES: &'static str = "lines";
const OUTPUT_ERROR: &'static str = "This version of tokei was compiled without any serialization
formats, to enable serialization, reinstall tokei with the features flag.
ALL:
cargo install tokei --features all
JSON:
cargo install tokei --features json
CBOR:
cargo install tokei --features cbor
TOML:
cargo install tokei --features toml
YAML:
cargo install toke --features yaml
You can also have any mix of json cbor toml, or yaml.
";
fn main() { fn main() {
// Get options at the beginning, so the program doesn't have to make any extra calls to get the // Get options at the beginning, so the program doesn't have to make any extra calls to get the
@ -158,19 +184,7 @@ fn main() {
} }
if let Some(format) = output_option { if let Some(format) = output_option {
match &*format { match_output(&format);
"cbor" => {
let cbor: Vec<_> = languages.to_cbor().unwrap();
for byte in cbor {
print!("{:02x}", byte);
}
}
"json" => print!("{}", languages.to_json().unwrap()),
"toml" => print!("{}", languages.to_toml()),
"yaml" => print!("{}", languages.to_yaml().unwrap()),
_ => unreachable!(),
}
} else if let Some(sort_category) = sort_option { } else if let Some(sort_category) = sort_option {
for (_, ref mut language) in &mut languages { for (_, ref mut language) in &mut languages {
@ -229,8 +243,8 @@ fn main() {
/// This originally too a &[u8], but the u8 didn't directly correspond with the hexadecimal u8, so /// This originally too a &[u8], but the u8 didn't directly correspond with the hexadecimal u8, so
/// it had to be changed to a String, and add the rustc_serialize dependency. /// it had to be changed to a String, and add the rustc_serialize dependency.
#[cfg(feature = "io")]
pub fn convert_input(contents: String) -> Option<BTreeMap<LanguageType, Language>> { pub fn convert_input(contents: String) -> Option<BTreeMap<LanguageType, Language>> {
if contents.is_empty() { if contents.is_empty() {
None None
} else if let Ok(result) = serde_json::from_str(&*contents) { } else if let Ok(result) = serde_json::from_str(&*contents) {
@ -250,6 +264,34 @@ pub fn convert_input(contents: String) -> Option<BTreeMap<LanguageType, Language
} }
} }
#[cfg(feature = "io")]
fn match_output(format: &str) {
match format {
"cbor" => {
// let cbor: Vec<u8> = languages.to_cbor().unwrap();
// for byte in cbor {
// print!("{:02x}", byte);
// }
}
"json" => print!("{}", languages.to_json().unwrap()),
"toml" => print!("{}", languages.to_toml()),
"yaml" => print!("{}", languages.to_yaml().unwrap()),
_ => unreachable!(),
}
}
#[cfg(not(feature = "io"))]
fn match_output(format: &str) -> ! {
panic!(OUTPUT_ERROR)
}
#[cfg(not(feature = "io"))]
pub fn convert_input(contents: String) -> ! {
panic!(OUTPUT_ERROR);
}
fn print_language<'a, C>(language: &'a Language, name: C) fn print_language<'a, C>(language: &'a Language, name: C)
where C: Into<Cow<'a, LanguageType>> where C: Into<Cow<'a, LanguageType>>
{ {