Skip to content

Commit

Permalink
Only use one content type per target label
Browse files Browse the repository at this point in the history
  • Loading branch information
ia0 committed Jul 1, 2024
1 parent b02e937 commit 7a0256c
Show file tree
Hide file tree
Showing 4 changed files with 562 additions and 691 deletions.
107 changes: 50 additions & 57 deletions rust/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::borrow::Cow;
use std::fmt::Write;
use std::path::{Path, PathBuf};
use std::sync::Arc;
Expand Down Expand Up @@ -105,8 +106,7 @@ struct Format {
///
/// %p The file path
/// %c A short code describing the content type
/// %d A short description of the content type
/// %D A long description of the content type
/// %d The description of the content type
/// %g The group of the content type
/// %m The magic of the content type
/// %M The MIME type of the content type
Expand Down Expand Up @@ -431,7 +431,7 @@ impl Response {
format.push_str(match () {
() if flags.modifiers.mime_type => "%M",
() if flags.modifiers.label => "%c",
() => "%D (%g)",
() => "%d (%g)",
});
format.push_str(if flags.modifiers.output_score { " %S" } else { "" });
format
Expand All @@ -443,12 +443,11 @@ impl Response {
Some('%') => match format.next() {
Some('p') => write!(&mut result, "{}", self.path.display())?,
Some('c') => write!(&mut result, "{}", self.code())?,
Some('d') => write!(&mut result, "{}", self.short_desc()?)?,
Some('D') => write!(&mut result, "{}", self.long_desc()?)?,
Some('d') => write!(&mut result, "{}", self.desc()?)?,
Some('g') => write!(&mut result, "{}", self.group())?,
Some('m') => write!(&mut result, "{}", self.magic())?,
Some('M') => write!(&mut result, "{}", self.mime())?,
Some('e') => write!(&mut result, "{}", self.extension())?,
Some('e') => write!(&mut result, "{}", join(self.extension()))?,
Some('s') => write!(&mut result, "{:.2}", self.score())?,
Some('S') => write!(&mut result, "{}%", (100. * self.score()).trunc())?,
Some(c) => result.push(c),
Expand All @@ -469,10 +468,10 @@ impl Response {
let output = JsonResult {
ct_label: Some(self.code().to_string()),
score: Some(self.score()),
group: Some(self.group()),
group: Some(self.group().to_string()),
mime_type: Some(self.mime().to_string()),
magic: Some(self.magic().to_string()),
description: Some(self.long_desc()?.to_string()),
description: Some(self.desc()?.to_string()),
};
let dl = if self.is_dl { output.clone() } else { JsonResult::default() };
Ok(Json { path: self.path.to_path_buf(), dl, output })
Expand All @@ -489,68 +488,60 @@ impl Response {
}
}

fn short_desc(&self) -> Result<String> {
fn desc(&self) -> Result<Cow<str>> {
Ok(match &self.output {
CliOutput::Empty => "Empty file".to_string(),
CliOutput::Empty => "Empty file".into(),
CliOutput::Symlink => {
format!("Symbolic link to {}", std::fs::read_link(&self.path)?.display())
format!("Symbolic link to {}", std::fs::read_link(&self.path)?.display()).into()
}
CliOutput::Directory => "A directory".to_string(),
CliOutput::Error(e) => e.clone(),
CliOutput::Label(x) => join(x.short_desc()),
CliOutput::Output(x) => join(x.label().short_desc()),
CliOutput::Directory => "A directory".into(),
CliOutput::Error(e) => e.into(),
CliOutput::Label(x) => x.desc().into(),
CliOutput::Output(x) => x.label().desc().into(),
})
}

fn long_desc(&self) -> Result<String> {
Ok(match &self.output {
CliOutput::Label(x) => join(x.long_desc()),
CliOutput::Output(x) => join(x.label().long_desc()),
_ => return self.short_desc(),
})
}

fn group(&self) -> String {
fn group(&self) -> &str {
match &self.output {
CliOutput::Empty => "inode".to_string(),
CliOutput::Symlink => "inode".to_string(),
CliOutput::Directory => "inode".to_string(),
CliOutput::Error(_) => "error".to_string(),
CliOutput::Label(x) => join(x.group()),
CliOutput::Output(x) => join(x.label().group()),
CliOutput::Empty => "inode",
CliOutput::Symlink => "inode",
CliOutput::Directory => "inode",
CliOutput::Error(_) => "error",
CliOutput::Label(x) => x.group(),
CliOutput::Output(x) => x.label().group(),
}
}

fn magic(&self) -> String {
fn magic(&self) -> Cow<str> {
match &self.output {
CliOutput::Empty => self.code().to_string(),
CliOutput::Symlink => format!("symlink link to {}", self.path.display()),
CliOutput::Directory => self.code().to_string(),
CliOutput::Error(_) => self.code().to_string(),
CliOutput::Label(x) => join(x.magic()),
CliOutput::Output(x) => join(x.label().magic()),
CliOutput::Empty => self.code().into(),
CliOutput::Symlink => format!("symlink link to {}", self.path.display()).into(),
CliOutput::Directory => self.code().into(),
CliOutput::Error(_) => self.code().into(),
CliOutput::Label(x) => x.magic().into(),
CliOutput::Output(x) => x.label().magic().into(),
}
}

fn mime(&self) -> String {
fn mime(&self) -> &str {
match &self.output {
CliOutput::Empty => "inode/x-empty".to_string(),
CliOutput::Symlink => "inode/symlink".to_string(),
CliOutput::Directory => "inode/directory".to_string(),
CliOutput::Error(_) => "error".to_string(),
CliOutput::Label(x) => join(x.mime()),
CliOutput::Output(x) => join(x.label().mime()),
CliOutput::Empty => "inode/x-empty",
CliOutput::Symlink => "inode/symlink",
CliOutput::Directory => "inode/directory",
CliOutput::Error(_) => "error",
CliOutput::Label(x) => x.mime(),
CliOutput::Output(x) => x.label().mime(),
}
}

fn extension(&self) -> String {
fn extension(&self) -> &[&str] {
match &self.output {
CliOutput::Empty => String::new(),
CliOutput::Symlink => String::new(),
CliOutput::Directory => String::new(),
CliOutput::Error(_) => String::new(),
CliOutput::Label(x) => join(x.extension()),
CliOutput::Output(x) => join(x.label().extension()),
CliOutput::Empty => &[],
CliOutput::Symlink => &[],
CliOutput::Directory => &[],
CliOutput::Error(_) => &[],
CliOutput::Label(x) => x.extension(),
CliOutput::Output(x) => x.label().extension(),
}
}

Expand All @@ -566,13 +557,13 @@ impl Response {
}

fn color(&self) -> Option<Color> {
let groups = match &self.output {
let group = match &self.output {
CliOutput::Error(_) => return Some(Color::Red),
CliOutput::Label(x) => x.group(),
CliOutput::Output(x) => x.label().group(),
_ => &[],
CliOutput::Label(x) => Some(x.group()),
CliOutput::Output(x) => Some(x.label().group()),
_ => None,
};
groups.iter().find_map(|x| group_color(x))
group.iter().find_map(|x| group_color(x))
}
}

Expand All @@ -591,12 +582,14 @@ fn group_color(group: &str) -> Option<Color> {

fn join<T: AsRef<str>>(xs: impl IntoIterator<Item = T>) -> String {
let mut result = String::new();
result.push('[');
for (i, x) in xs.into_iter().enumerate() {
if i != 0 {
result.push_str(" | ");
result.push('|');
}
result.push_str(x.as_ref());
}
result.push(']');
result
}

Expand Down
71 changes: 34 additions & 37 deletions rust/gen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeMap;
use std::fs::File;
use std::io::Write;

Expand All @@ -22,28 +22,36 @@ use serde::Deserialize;
fn main() -> Result<()> {
let configs: BTreeMap<String, Config> =
serde_json::from_reader(File::open("data/config.json")?)?;
let mut labels = BTreeMap::<String, Vec<Label>>::new();
for (short, config) in configs {
let mut labels = BTreeMap::<String, Option<Label>>::new();
for (key, config) in configs {
if !config.in_scope_for_output_content_type {
continue;
}
if ["directory", "empty", "symlink"].contains(&short.as_str()) {
if ["directory", "empty", "symlink"].contains(&key.as_str()) {
continue;
}
let target = match config.target_label {
Some(x) => x,
None => continue,
};
let target = config.target_label.unwrap_or_else(|| key.clone());
let is_target = target == key;
let entry = labels.entry(target).or_default();
if !is_target {
continue;
}
assert!(entry.is_none());
let exts = config.extensions;
let mime = unwrap(config.mime_type, "mime_type", &short)?;
let group = unwrap(config.group, "group", &short)?;
let magic = unwrap(config.magic, "magic", &short)?;
let long = unwrap(config.description, "description", &short)?;
let mime = unwrap(config.mime_type, "mime_type", &key)?;
let group = unwrap(config.group, "group", &key)?;
let magic = unwrap(config.magic, "magic", &key)?;
let desc = unwrap(config.description, "description", &key)?;
let text = config.tags.iter().any(|x| *x == "text");
let label = Label { short, long, magic, group, mime, exts, text };
labels.entry(target).or_default().push(label);
*entry = Some(Label { desc, magic, group, mime, exts, text });
}
let labels = labels;
let labels = labels
.into_iter()
.map(|(target, label)| {
let label = label.with_context(|| format!("missing label for {target}"))?;
Ok((target, label))
})
.collect::<Result<BTreeMap<_, _>>>()?;
let mut output = File::create("../lib/src/label.rs")?;
let header = std::fs::read_to_string(file!())?;
let header = header.split("\n\n").next().context("main.rs does not contain an empty line")?;
Expand All @@ -54,24 +62,23 @@ fn main() -> Result<()> {
writeln!(output, "/// Content type of a file.")?;
writeln!(output, "#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n#[repr(u32)]")?;
writeln!(output, "pub enum Label {{")?;
for (target, labels) in &labels {
let doc = merge_ref(labels, |x| &x.long).join(" | ");
writeln!(output, " /// {doc}")?;
for (target, Label { desc, .. }) in &labels {
writeln!(output, " /// {desc}")?;
writeln!(output, " {},", capitalize(target))?;
}
writeln!(output, "}}\n")?;
writeln!(output, "pub(crate) const MAX_LABEL: u32 = {};\n", labels.len() - 1)?;
writeln!(output, "pub(crate) const METADATA: [Metadata; {}] = [", labels.len())?;
for (target, labels) in &labels {
for (target, label) in &labels {
let Label { desc, magic, group, mime, exts, text } = label;
writeln!(output, " Metadata {{")?;
writeln!(output, " code: {target:?},")?;
writeln!(output, " short_desc: &{:?},", merge_ref(labels, |x| &x.short))?;
writeln!(output, " long_desc: &{:?},", merge_ref(labels, |x| &x.long))?;
writeln!(output, " magic: &{:?},", merge_ref(labels, |x| &x.magic))?;
writeln!(output, " group: &{:?},", merge_ref(labels, |x| &x.group))?;
writeln!(output, " mime: &{:?},", merge_ref(labels, |x| &x.mime))?;
writeln!(output, " extension: &{:?},", merge(labels, |x| &x.exts))?;
writeln!(output, " is_text: {:?},", labels.iter().all(|x| x.text))?;
writeln!(output, " desc: {desc:?},")?;
writeln!(output, " magic: {magic:?},")?;
writeln!(output, " group: {group:?},")?;
writeln!(output, " mime: {mime:?},")?;
writeln!(output, " extension: &{exts:?},")?;
writeln!(output, " is_text: {text:?},")?;
writeln!(output, " }},")?;
}
writeln!(output, "];")?;
Expand All @@ -92,8 +99,7 @@ struct Config {

#[derive(Debug)]
struct Label {
short: String,
long: String,
desc: String,
magic: String,
group: String,
mime: String,
Expand All @@ -105,15 +111,6 @@ fn unwrap<T>(x: Option<T>, f: &str, n: &str) -> Result<T> {
x.ok_or_else(|| anyhow!("missing {f} for {n:?}"))
}

fn merge_ref<T: Ord + AsRef<str>>(labels: &[Label], field: impl Fn(&Label) -> &T) -> Vec<String> {
merge(labels, |x| std::slice::from_ref(field(x)))
}

fn merge<T: Ord + AsRef<str>>(labels: &[Label], field: impl Fn(&Label) -> &[T]) -> Vec<String> {
let labels: BTreeSet<&T> = labels.iter().flat_map(field).collect();
labels.into_iter().map(|x| x.as_ref().to_string()).collect()
}

fn capitalize(xs: &str) -> String {
let mut xs = xs.as_bytes().to_vec();
xs[0] = xs[0].to_ascii_uppercase();
Expand Down
Loading

0 comments on commit 7a0256c

Please sign in to comment.