mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-04-25 08:53:01 +02:00
Use tables to summarize course content (#2005)
This is more friendly to translation (as it can share the translation of the title). This fixes #1982.
This commit is contained in:
parent
45aa43f406
commit
bb44b1d7a8
@ -36,10 +36,10 @@
|
|||||||
//! top-level item are treated as further slides in the same segment.
|
//! top-level item are treated as further slides in the same segment.
|
||||||
|
|
||||||
use crate::frontmatter::{split_frontmatter, Frontmatter};
|
use crate::frontmatter::{split_frontmatter, Frontmatter};
|
||||||
use crate::markdown::{duration, relative_link};
|
use crate::markdown::{duration, Table};
|
||||||
use mdbook::book::{Book, BookItem, Chapter};
|
use mdbook::book::{Book, BookItem, Chapter};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// Duration, in minutes, of breaks between segments in the course.
|
/// Duration, in minutes, of breaks between segments in the course.
|
||||||
const BREAK_DURATION: u64 = 10;
|
const BREAK_DURATION: u64 = 10;
|
||||||
@ -245,32 +245,26 @@ impl Course {
|
|||||||
|
|
||||||
/// Generate a Markdown schedule for this course, for placement at the given
|
/// Generate a Markdown schedule for this course, for placement at the given
|
||||||
/// path.
|
/// path.
|
||||||
pub fn schedule(&self, at_source_path: impl AsRef<Path>) -> String {
|
pub fn schedule(&self) -> String {
|
||||||
let mut outline = String::from("Course schedule:\n");
|
let mut outline = String::from("Course schedule:\n");
|
||||||
for session in self {
|
for session in self {
|
||||||
writeln!(
|
writeln!(
|
||||||
&mut outline,
|
&mut outline,
|
||||||
" * {} ({}, including breaks)",
|
" * {} ({}, including breaks)\n",
|
||||||
session.name,
|
session.name,
|
||||||
duration(session.minutes())
|
duration(session.minutes())
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let mut segments = Table::new(["Segment".into(), "Duration".into()]);
|
||||||
for segment in session {
|
for segment in session {
|
||||||
|
// Skip short segments (welcomes, wrap-up, etc.)
|
||||||
if segment.minutes() == 0 {
|
if segment.minutes() == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
writeln!(
|
segments
|
||||||
&mut outline,
|
.add_row([segment.name.clone(), duration(segment.minutes())]);
|
||||||
" * [{}]({}) ({})",
|
|
||||||
segment.name,
|
|
||||||
relative_link(
|
|
||||||
&at_source_path,
|
|
||||||
&segment.slides[0].source_paths[0]
|
|
||||||
),
|
|
||||||
duration(segment.minutes())
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
writeln!(&mut outline, "{}\n", segments).unwrap();
|
||||||
}
|
}
|
||||||
outline
|
outline
|
||||||
}
|
}
|
||||||
@ -313,24 +307,18 @@ impl Session {
|
|||||||
|
|
||||||
/// Generate a Markdown outline for this session, for placement at the given
|
/// Generate a Markdown outline for this session, for placement at the given
|
||||||
/// path.
|
/// path.
|
||||||
pub fn outline(&self, at_source_path: impl AsRef<Path>) -> String {
|
pub fn outline(&self) -> String {
|
||||||
let mut outline = String::from("In this session:\n");
|
let mut segments = Table::new(["Segment".into(), "Duration".into()]);
|
||||||
for segment in self {
|
for segment in self {
|
||||||
// Skip short segments (welcomes, wrap-up, etc.)
|
// Skip short segments (welcomes, wrap-up, etc.)
|
||||||
if segment.minutes() == 0 {
|
if segment.minutes() == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
writeln!(
|
segments.add_row([segment.name.clone(), duration(segment.minutes())]);
|
||||||
&mut outline,
|
|
||||||
" * [{}]({}) ({})",
|
|
||||||
segment.name,
|
|
||||||
relative_link(&at_source_path, &segment.slides[0].source_paths[0]),
|
|
||||||
duration(segment.minutes())
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
writeln!(&mut outline,"\nIncluding {BREAK_DURATION} minute breaks, this session should take about {}", duration(self.minutes())).unwrap();
|
format!(
|
||||||
outline
|
"Including {BREAK_DURATION} minute breaks, this session should take about {}. It contains:\n\n{}",
|
||||||
|
duration(self.minutes()), segments)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the total duration of this session.
|
/// Return the total duration of this session.
|
||||||
@ -394,28 +382,19 @@ impl Segment {
|
|||||||
self.into_iter().map(|s| s.minutes()).sum()
|
self.into_iter().map(|s| s.minutes()).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outline(&self, at_source_path: impl AsRef<Path>) -> String {
|
pub fn outline(&self) -> String {
|
||||||
let mut outline = String::from("In this segment:\n");
|
let mut slides = Table::new(["Slide".into(), "Duration".into()]);
|
||||||
for slide in self {
|
for slide in self {
|
||||||
if slide.minutes() == 0 {
|
if slide.minutes() == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
writeln!(
|
slides.add_row([slide.name.clone(), duration(slide.minutes())]);
|
||||||
&mut outline,
|
|
||||||
" * [{}]({}) ({})",
|
|
||||||
slide.name,
|
|
||||||
relative_link(&at_source_path, &slide.source_paths[0]),
|
|
||||||
duration(slide.minutes())
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
writeln!(
|
format!(
|
||||||
&mut outline,
|
"This segment should take about {}. It contains:\n\n{}",
|
||||||
"\nThis segment should take about {}",
|
duration(self.minutes()),
|
||||||
duration(self.minutes())
|
slides,
|
||||||
)
|
)
|
||||||
.unwrap();
|
|
||||||
outline
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
/// Given a source_path for the markdown file being rendered and a source_path
|
/// Given a source_path for the markdown file being rendered and a source_path
|
||||||
@ -57,6 +58,46 @@ pub fn duration(mut minutes: u64) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Table implements Display to format a two-dimensional table as markdown,
|
||||||
|
/// following https://github.github.com/gfm/#tables-extension-.
|
||||||
|
pub struct Table<const N: usize> {
|
||||||
|
header: [String; N],
|
||||||
|
rows: Vec<[String; N]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> Table<N> {
|
||||||
|
pub fn new(header: [String; N]) -> Self {
|
||||||
|
Self { header, rows: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_row(&mut self, row: [String; N]) {
|
||||||
|
self.rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_row<'a, I: Iterator<Item = &'a str>>(
|
||||||
|
&self,
|
||||||
|
f: &mut fmt::Formatter<'_>,
|
||||||
|
iter: I,
|
||||||
|
) -> fmt::Result {
|
||||||
|
write!(f, "|")?;
|
||||||
|
for cell in iter {
|
||||||
|
write!(f, " {} |", cell)?;
|
||||||
|
}
|
||||||
|
write!(f, "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const N: usize> fmt::Display for Table<N> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.write_row(f, self.header.iter().map(|s| s.as_str()))?;
|
||||||
|
self.write_row(f, self.header.iter().map(|_| "-"))?;
|
||||||
|
for row in &self.rows {
|
||||||
|
self.write_row(f, row.iter().map(|s| s.as_str()))?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -152,4 +193,15 @@ mod test {
|
|||||||
fn duration_hours_mins() {
|
fn duration_hours_mins() {
|
||||||
assert_eq!(duration(130), "2 hours and 10 minutes")
|
assert_eq!(duration(130), "2 hours and 10 minutes")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table() {
|
||||||
|
let mut table = Table::new(["a".into(), "b".into()]);
|
||||||
|
table.add_row(["a1".into(), "b1".into()]);
|
||||||
|
table.add_row(["a2".into(), "b2".into()]);
|
||||||
|
assert_eq!(
|
||||||
|
format!("{}", table),
|
||||||
|
"| a | b |\n| - | - |\n| a1 | b1 |\n| a2 | b2 |\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,19 +40,19 @@ pub fn replace(
|
|||||||
let directive: Vec<_> = directive_str.split_whitespace().collect();
|
let directive: Vec<_> = directive_str.split_whitespace().collect();
|
||||||
match directive.as_slice() {
|
match directive.as_slice() {
|
||||||
["session", "outline"] if session.is_some() => {
|
["session", "outline"] if session.is_some() => {
|
||||||
session.unwrap().outline(source_path)
|
session.unwrap().outline()
|
||||||
}
|
}
|
||||||
["segment", "outline"] if segment.is_some() => {
|
["segment", "outline"] if segment.is_some() => {
|
||||||
segment.unwrap().outline(source_path)
|
segment.unwrap().outline()
|
||||||
}
|
}
|
||||||
["course", "outline"] if course.is_some() => {
|
["course", "outline"] if course.is_some() => {
|
||||||
course.unwrap().schedule(source_path)
|
course.unwrap().schedule()
|
||||||
}
|
}
|
||||||
["course", "outline", course_name] => {
|
["course", "outline", course_name] => {
|
||||||
let Some(course) = courses.find_course(course_name) else {
|
let Some(course) = courses.find_course(course_name) else {
|
||||||
return captures[0].to_string();
|
return captures[0].to_string();
|
||||||
};
|
};
|
||||||
course.schedule(source_path)
|
course.schedule()
|
||||||
}
|
}
|
||||||
_ => directive_str.to_owned(),
|
_ => directive_str.to_owned(),
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user