Skip to content

Commit

Permalink
basic test
Browse files Browse the repository at this point in the history
  • Loading branch information
amitu committed Oct 26, 2023
1 parent 2454e07 commit 532bb0a
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 25 deletions.
93 changes: 68 additions & 25 deletions fastn-core/src/tutor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ pub async fn process(
));
}

let state: TutorState =
let state =
match tokio::fs::read(dirs::home_dir().unwrap().join(".fastn").join("tutor.json")).await {
Ok(v) => serde_json::from_slice(&v)?,
Err(e) => match e.kind() {
std::io::ErrorKind::NotFound => TutorStateFS::default(),
_ => return Err(e.into()),
},
}
.try_into()?;
.to_state(std::env::current_dir()?)?;

doc.from_json(&state, &kind, &value)
}
Expand All @@ -45,21 +45,21 @@ struct TutorStateFS {
current: String,
}

#[derive(Debug, serde::Serialize)]
#[derive(Debug, serde::Serialize, PartialEq)]
struct TutorState {
workshops: Vec<Workshop>,
}

impl TryFrom<TutorStateFS> for TutorState {
type Error = ftd::interpreter::Error;

fn try_from(state: TutorStateFS) -> Result<Self, Self::Error> {
// loop over all folders in current folder
impl TutorStateFS {
fn to_state<T: AsRef<std::path::Path>>(
self: TutorStateFS,
path: T,
) -> ftd::interpreter::Result<TutorState> {
let mut workshops = vec![];
static RE: once_cell::sync::Lazy<regex::Regex> =
once_cell::sync::Lazy::new(|| regex::Regex::new(r"^[a-zA-Z]-[a-zA-Z]+.*$").unwrap());

for entry in std::fs::read_dir(std::env::current_dir()?)? {
for entry in std::fs::read_dir(path)? {
let entry = entry?;
let path = entry.path();
if !path.is_dir() {
Expand All @@ -70,25 +70,24 @@ impl TryFrom<TutorStateFS> for TutorState {
continue;
}

workshops.push(Workshop::load(&path, &state)?);
workshops.push(Workshop::load(&path, &self)?);
}

Ok(TutorState { workshops })
}
}

#[derive(Debug, serde::Serialize)]
#[derive(Debug, serde::Serialize, PartialEq)]
struct Workshop {
title: String,
about: String,
url: String,
done: bool,
current: bool,
tutorials: Vec<Tutorial>,
}

impl Workshop {
fn load(path: &std::path::Path, state: &TutorStateFS) -> ftd::interpreter::Result<Self> {
let (title, about) = title_and_about_from_readme(path)?;
let mut tutorials = vec![];
let id = path.file_name().unwrap().to_string_lossy();

Expand All @@ -103,35 +102,33 @@ impl Workshop {
}

Ok(Workshop {
title: title.to_string(),
about: about.to_string(),
title: title_from_readme(path)?,
url: format!("/{id}/"),
done: !tutorials.iter().any(|t| !t.done),
current: tutorials.iter().any(|t| t.current),
tutorials,
})
}
}

fn title_and_about_from_readme(
folder: &std::path::Path,
) -> ftd::interpreter::Result<(String, String)> {
fn title_from_readme(folder: &std::path::Path) -> ftd::interpreter::Result<String> {
let content = std::fs::read_to_string(folder.join("README.md"))?;
let (title, about) = match content.split_once("\n\n") {
let (title, _about) = match content.split_once("\n\n") {
Some(v) => v,
None => {
return Err(ftd::interpreter::Error::OtherError(
"invalid README.md".into(),
))
}
};
Ok((title.to_string(), about.to_string()))
Ok(title.replacen("# ", "", 1))
}

#[derive(Debug, serde::Serialize)]
#[derive(Debug, serde::Serialize, PartialEq)]
struct Tutorial {
id: String,
url: String,
title: String,
about: String,
done: bool,
current: bool,
}
Expand All @@ -142,14 +139,13 @@ impl Tutorial {
path: &std::path::Path,
state: &TutorStateFS,
) -> ftd::interpreter::Result<Self> {
let (title, about) = title_and_about_from_readme(path)?;
let id = format!("{parent}/{}", path.file_name().unwrap().to_string_lossy());

Ok(Tutorial {
title: title.to_string(),
about: about.to_string(),
title: title_from_readme(path)?,
done: state.done.contains(&id),
current: state.current == id,
url: format!("/{id}/"),
id,
})
}
Expand All @@ -160,3 +156,50 @@ pub fn is_tutor() -> bool {
// with either of these are passed we allow APIs like /-/shutdown/, `/-/start/` etc
std::env::args().any(|e| e == "tutor" || e == "--tutor")
}

#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;

#[test]
fn test() {
assert_eq!(
super::TutorStateFS {
done: vec![],
current: "".to_string(),
}
.to_state("tutor-tests/one")
.unwrap(),
super::TutorState {
workshops: vec![
super::Workshop {
title: "Build Websites Using `fastn`".to_string(),
url: "/a-website/".to_string(),
done: false,
current: false,
tutorials: vec![super::Tutorial {
id: "a-website/01-hello-world".to_string(),
url: "/a-website/01-hello-world/".to_string(),
title: "Install and start using `fastn`".to_string(),
done: false,
current: false,
}],
},
super::Workshop {
title: "Build User Interfaces Using `fastn`".to_string(),
url: "/b-ui/".to_string(),
done: false,
current: false,
tutorials: vec![super::Tutorial {
id: "b-ui/01-hello-world".to_string(),
url: "/b-ui/01-hello-world/".to_string(),
title: "Install and start using `fastn`".to_string(),
done: false,
current: false,
}],
}
]
}
)
}
}
3 changes: 3 additions & 0 deletions fastn-core/tutor-tests/one/a-website/01-hello-world/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Install and start using `fastn`

In this exercise we will install fastn and create a basic hello world program.
3 changes: 3 additions & 0 deletions fastn-core/tutor-tests/one/a-website/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Build Websites Using `fastn`

This workshop teaches you how to build websites using `fastn`.
3 changes: 3 additions & 0 deletions fastn-core/tutor-tests/one/b-ui/01-hello-world/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Install and start using `fastn`

In this exercise we will install fastn and create a basic hello world program.
3 changes: 3 additions & 0 deletions fastn-core/tutor-tests/one/b-ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Build User Interfaces Using `fastn`

This workshop teaches you how to build user interfaces using `fastn`.

0 comments on commit 532bb0a

Please sign in to comment.