mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
docs: show platforms for each util
This commit is contained in:
parent
5a3434161d
commit
3c09c747dd
2 changed files with 221 additions and 132 deletions
11
docs/theme/head.hbs
vendored
11
docs/theme/head.hbs
vendored
|
@ -5,10 +5,17 @@
|
||||||
main {
|
main {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.version {
|
.additional {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1em;
|
top: 0em;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
.platforms {
|
||||||
|
font-size: 1.5em;
|
||||||
}
|
}
|
||||||
dd > p {
|
dd > p {
|
||||||
margin-top: 0.2em;
|
margin-top: 0.2em;
|
||||||
|
|
214
src/bin/uudoc.rs
214
src/bin/uudoc.rs
|
@ -5,6 +5,7 @@
|
||||||
// spell-checker:ignore tldr
|
// spell-checker:ignore tldr
|
||||||
|
|
||||||
use clap::Command;
|
use clap::Command;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
@ -29,6 +30,7 @@ fn main() -> io::Result<()> {
|
||||||
x => x,
|
x => x,
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
println!("Writing initial info to SUMMARY.md");
|
||||||
let mut summary = File::create("docs/src/SUMMARY.md")?;
|
let mut summary = File::create("docs/src/SUMMARY.md")?;
|
||||||
|
|
||||||
let _ = write!(
|
let _ = write!(
|
||||||
|
@ -44,6 +46,40 @@ fn main() -> io::Result<()> {
|
||||||
* [Multi-call binary](multicall.md)\n",
|
* [Multi-call binary](multicall.md)\n",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
println!("Gathering utils per platform");
|
||||||
|
let utils_per_platform = {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
for platform in ["unix", "macos", "windows"] {
|
||||||
|
let platform_utils: Vec<String> = String::from_utf8(
|
||||||
|
std::process::Command::new("./util/show-utils.sh")
|
||||||
|
.arg(format!("--features=feat_os_{}", platform))
|
||||||
|
.output()?
|
||||||
|
.stdout,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.split(' ')
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect();
|
||||||
|
map.insert(platform, platform_utils);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linux is a special case because it can support selinux
|
||||||
|
let platform_utils: Vec<String> = String::from_utf8(
|
||||||
|
std::process::Command::new("./util/show-utils.sh")
|
||||||
|
.arg("--features=feat_os_unix feat_selinux")
|
||||||
|
.output()?
|
||||||
|
.stdout,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.split(' ')
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.collect();
|
||||||
|
map.insert("linux", platform_utils);
|
||||||
|
|
||||||
|
map
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Writing to utils");
|
||||||
let mut utils = utils.entries().collect::<Vec<_>>();
|
let mut utils = utils.entries().collect::<Vec<_>>();
|
||||||
utils.sort();
|
utils.sort();
|
||||||
for (&name, (_, command)) in utils {
|
for (&name, (_, command)) in utils {
|
||||||
|
@ -52,7 +88,14 @@ fn main() -> io::Result<()> {
|
||||||
}
|
}
|
||||||
let p = format!("docs/src/utils/{}.md", name);
|
let p = format!("docs/src/utils/{}.md", name);
|
||||||
if let Ok(f) = File::create(&p) {
|
if let Ok(f) = File::create(&p) {
|
||||||
write_markdown(f, &mut command(), name, &mut tldr_zip)?;
|
MDWriter {
|
||||||
|
w: Box::new(f),
|
||||||
|
command: command(),
|
||||||
|
name,
|
||||||
|
tldr_zip: &mut tldr_zip,
|
||||||
|
utils_per_platform: &utils_per_platform,
|
||||||
|
}
|
||||||
|
.markdown()?;
|
||||||
println!("Wrote to '{}'", p);
|
println!("Wrote to '{}'", p);
|
||||||
} else {
|
} else {
|
||||||
println!("Error writing to {}", p);
|
println!("Error writing to {}", p);
|
||||||
|
@ -62,31 +105,65 @@ fn main() -> io::Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_markdown(
|
struct MDWriter<'a, 'b> {
|
||||||
mut w: impl Write,
|
w: Box<dyn Write>,
|
||||||
command: &mut Command,
|
command: Command<'a>,
|
||||||
name: &str,
|
name: &'a str,
|
||||||
tldr_zip: &mut zip::ZipArchive<impl Read + Seek>,
|
tldr_zip: &'b mut ZipArchive<Cursor<Vec<u8>>>,
|
||||||
) -> io::Result<()> {
|
utils_per_platform: &'b HashMap<&'b str, Vec<String>>,
|
||||||
write!(w, "# {}\n\n", name)?;
|
|
||||||
write_version(&mut w, command)?;
|
|
||||||
write_usage(&mut w, command, name)?;
|
|
||||||
write_description(&mut w, command)?;
|
|
||||||
write_options(&mut w, command)?;
|
|
||||||
write_examples(&mut w, name, tldr_zip)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_version(w: &mut impl Write, command: &Command) -> io::Result<()> {
|
impl<'a, 'b> MDWriter<'a, 'b> {
|
||||||
|
fn markdown(&mut self) -> io::Result<()> {
|
||||||
|
write!(self.w, "# {}\n\n", self.name)?;
|
||||||
|
self.additional()?;
|
||||||
|
self.usage()?;
|
||||||
|
self.description()?;
|
||||||
|
self.options()?;
|
||||||
|
self.examples()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn additional(&mut self) -> io::Result<()> {
|
||||||
|
writeln!(self.w, "<div class=\"additional\">")?;
|
||||||
|
self.platforms()?;
|
||||||
|
self.version()?;
|
||||||
|
writeln!(self.w, "</div>")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn platforms(&mut self) -> io::Result<()> {
|
||||||
|
writeln!(self.w, "<div class=\"platforms\">")?;
|
||||||
|
for (feature, icon) in [
|
||||||
|
("linux", "linux"),
|
||||||
|
// freebsd is disabled for now because mdbook does not use font-awesome 5 yet.
|
||||||
|
// ("unix", "freebsd"),
|
||||||
|
("macos", "apple"),
|
||||||
|
("windows", "windows"),
|
||||||
|
] {
|
||||||
|
if self.name.contains("sum")
|
||||||
|
|| self.utils_per_platform[feature]
|
||||||
|
.iter()
|
||||||
|
.any(|u| u == self.name)
|
||||||
|
{
|
||||||
|
writeln!(self.w, "<i class=\"fa-brands fa-{}\"></i>", icon)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(self.w, "</div>")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(&mut self) -> io::Result<()> {
|
||||||
writeln!(
|
writeln!(
|
||||||
w,
|
self.w,
|
||||||
"<div class=\"version\">version: {}</div>",
|
"<div class=\"version\">v{}</div>",
|
||||||
command.render_version().split_once(' ').unwrap().1
|
self.command.render_version().split_once(' ').unwrap().1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_usage(w: &mut impl Write, command: &mut Command, name: &str) -> io::Result<()> {
|
fn usage(&mut self) -> io::Result<()> {
|
||||||
writeln!(w, "\n```")?;
|
writeln!(self.w, "\n```")?;
|
||||||
let mut usage: String = command
|
let mut usage: String = self
|
||||||
|
.command
|
||||||
.render_usage()
|
.render_usage()
|
||||||
.lines()
|
.lines()
|
||||||
.skip(1)
|
.skip(1)
|
||||||
|
@ -94,81 +171,79 @@ fn write_usage(w: &mut impl Write, command: &mut Command, name: &str) -> io::Res
|
||||||
.filter(|l| !l.is_empty())
|
.filter(|l| !l.is_empty())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
usage = usage.replace(uucore::execution_phrase(), name);
|
usage = usage.replace(uucore::execution_phrase(), self.name);
|
||||||
writeln!(w, "{}", usage)?;
|
writeln!(self.w, "{}", usage)?;
|
||||||
writeln!(w, "```")
|
writeln!(self.w, "```")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_description(w: &mut impl Write, command: &Command) -> io::Result<()> {
|
fn description(&mut self) -> io::Result<()> {
|
||||||
if let Some(about) = command.get_long_about().or_else(|| command.get_about()) {
|
if let Some(about) = self
|
||||||
writeln!(w, "{}", about)
|
.command
|
||||||
|
.get_long_about()
|
||||||
|
.or_else(|| self.command.get_about())
|
||||||
|
{
|
||||||
|
writeln!(self.w, "{}", about)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_examples(
|
fn examples(&mut self) -> io::Result<()> {
|
||||||
w: &mut impl Write,
|
let content = if let Some(f) =
|
||||||
name: &str,
|
get_zip_content(self.tldr_zip, &format!("pages/common/{}.md", self.name))
|
||||||
tldr_zip: &mut zip::ZipArchive<impl Read + Seek>,
|
{
|
||||||
) -> io::Result<()> {
|
|
||||||
let content = if let Some(f) = get_zip_content(tldr_zip, &format!("pages/common/{}.md", name)) {
|
|
||||||
f
|
f
|
||||||
} else if let Some(f) = get_zip_content(tldr_zip, &format!("pages/linux/{}.md", name)) {
|
} else if let Some(f) =
|
||||||
|
get_zip_content(self.tldr_zip, &format!("pages/linux/{}.md", self.name))
|
||||||
|
{
|
||||||
f
|
f
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
writeln!(w, "## Examples")?;
|
writeln!(self.w, "## Examples")?;
|
||||||
writeln!(w)?;
|
writeln!(self.w)?;
|
||||||
for line in content.lines().skip_while(|l| !l.starts_with('-')) {
|
for line in content.lines().skip_while(|l| !l.starts_with('-')) {
|
||||||
if let Some(l) = line.strip_prefix("- ") {
|
if let Some(l) = line.strip_prefix("- ") {
|
||||||
writeln!(w, "{}", l)?;
|
writeln!(self.w, "{}", l)?;
|
||||||
} else if line.starts_with('`') {
|
} else if line.starts_with('`') {
|
||||||
writeln!(w, "```shell\n{}\n```", line.trim_matches('`'))?;
|
writeln!(self.w, "```shell\n{}\n```", line.trim_matches('`'))?;
|
||||||
} else if line.is_empty() {
|
} else if line.is_empty() {
|
||||||
writeln!(w)?;
|
writeln!(self.w)?;
|
||||||
} else {
|
} else {
|
||||||
println!("Not sure what to do with this line:");
|
println!("Not sure what to do with this line:");
|
||||||
println!("{}", line);
|
println!("{}", line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeln!(w)?;
|
writeln!(self.w)?;
|
||||||
writeln!(
|
writeln!(
|
||||||
w,
|
self.w,
|
||||||
"> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)."
|
"> The examples are provided by the [tldr-pages project](https://tldr.sh) under the [CC BY 4.0 License](https://github.com/tldr-pages/tldr/blob/main/LICENSE.md)."
|
||||||
)?;
|
)?;
|
||||||
writeln!(w, ">")?;
|
writeln!(self.w, ">")?;
|
||||||
writeln!(
|
writeln!(
|
||||||
w,
|
self.w,
|
||||||
"> Please note that, as uutils is a work in progress, some examples might fail."
|
"> Please note that, as uutils is a work in progress, some examples might fail."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> {
|
fn options(&mut self) -> io::Result<()> {
|
||||||
let mut s = String::new();
|
writeln!(self.w, "<h2>Options</h2>")?;
|
||||||
archive.by_name(name).ok()?.read_to_string(&mut s).unwrap();
|
write!(self.w, "<dl>")?;
|
||||||
Some(s)
|
for arg in self.command.get_arguments() {
|
||||||
}
|
write!(self.w, "<dt>")?;
|
||||||
|
|
||||||
fn write_options(w: &mut impl Write, command: &Command) -> io::Result<()> {
|
|
||||||
writeln!(w, "<h2>Options</h2>")?;
|
|
||||||
write!(w, "<dl>")?;
|
|
||||||
for arg in command.get_arguments() {
|
|
||||||
write!(w, "<dt>")?;
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for l in arg.get_long_and_visible_aliases().unwrap_or_default() {
|
for l in arg.get_long_and_visible_aliases().unwrap_or_default() {
|
||||||
if !first {
|
if !first {
|
||||||
write!(w, ", ")?;
|
write!(self.w, ", ")?;
|
||||||
} else {
|
} else {
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
write!(w, "<code>")?;
|
write!(self.w, "<code>")?;
|
||||||
write!(w, "--{}", l)?;
|
write!(self.w, "--{}", l)?;
|
||||||
if let Some(names) = arg.get_value_names() {
|
if let Some(names) = arg.get_value_names() {
|
||||||
write!(
|
write!(
|
||||||
w,
|
self.w,
|
||||||
"={}",
|
"={}",
|
||||||
names
|
names
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -177,19 +252,19 @@ fn write_options(w: &mut impl Write, command: &Command) -> io::Result<()> {
|
||||||
.join(" ")
|
.join(" ")
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
write!(w, "</code>")?;
|
write!(self.w, "</code>")?;
|
||||||
}
|
}
|
||||||
for s in arg.get_short_and_visible_aliases().unwrap_or_default() {
|
for s in arg.get_short_and_visible_aliases().unwrap_or_default() {
|
||||||
if !first {
|
if !first {
|
||||||
write!(w, ", ")?;
|
write!(self.w, ", ")?;
|
||||||
} else {
|
} else {
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
write!(w, "<code>")?;
|
write!(self.w, "<code>")?;
|
||||||
write!(w, "-{}", s)?;
|
write!(self.w, "-{}", s)?;
|
||||||
if let Some(names) = arg.get_value_names() {
|
if let Some(names) = arg.get_value_names() {
|
||||||
write!(
|
write!(
|
||||||
w,
|
self.w,
|
||||||
" {}",
|
" {}",
|
||||||
names
|
names
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -198,14 +273,21 @@ fn write_options(w: &mut impl Write, command: &Command) -> io::Result<()> {
|
||||||
.join(" ")
|
.join(" ")
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
write!(w, "</code>")?;
|
write!(self.w, "</code>")?;
|
||||||
}
|
}
|
||||||
writeln!(w, "</dt>")?;
|
writeln!(self.w, "</dt>")?;
|
||||||
writeln!(
|
writeln!(
|
||||||
w,
|
self.w,
|
||||||
"<dd>\n\n{}\n\n</dd>",
|
"<dd>\n\n{}\n\n</dd>",
|
||||||
arg.get_help().unwrap_or_default().replace('\n', "<br />")
|
arg.get_help().unwrap_or_default().replace('\n', "<br />")
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
writeln!(w, "</dl>\n")
|
writeln!(self.w, "</dl>\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> {
|
||||||
|
let mut s = String::new();
|
||||||
|
archive.by_name(name).ok()?.read_to_string(&mut s).unwrap();
|
||||||
|
Some(s)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue