Skip to content

Commit

Permalink
Add terminal ui as separate project (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
phughk authored Mar 2, 2024
1 parent 0d53d69 commit 856c27d
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 108 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[workspace]
resolver = "2"
members = [
"cli-tui",
"engine",
"frontend"
]
]
11 changes: 11 additions & 0 deletions cli-tui/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "cli-tui"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
crossterm = "0.27.0"
laerning-tool = { path = "../engine" }
ratatui = "0.26.1"
42 changes: 42 additions & 0 deletions cli-tui/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crossterm::event::{KeyCode, KeyEventKind};
use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use crossterm::{event, ExecutableCommand};
use ratatui::backend::CrosstermBackend;
use ratatui::style::Stylize;
use ratatui::widgets::Paragraph;
use ratatui::Terminal;
use std::error::Error;
use std::io::stdout;

fn main() -> Result<(), Box<dyn Error>> {
stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
terminal.clear()?;

loop {
terminal.draw(|frame| {
let area = frame.size();
frame.render_widget(
Paragraph::new("Hello Ratatui! (press 'q' to quit)")
.white()
.on_blue(),
area,
);
})?;

if event::poll(std::time::Duration::from_millis(16))? {
if let event::Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
break;
}
}
}
}

stdout().execute(LeaveAlternateScreen)?;
disable_raw_mode()?;
Ok(())
}
10 changes: 7 additions & 3 deletions engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
name = "laerning_tool"
path = "src/lib.rs"

[[bin]]
name="laerning-tool-api"
path="src/main.rs"
name = "laerning-tool-api"
path = "src/main.rs"

[dependencies]
async-trait = "0.1.73"
Expand All @@ -20,7 +24,7 @@ scan_fmt = "0.2.6"
serde = { version = "1.0.171", features = ["derive"] }
serde-xml-rs = "0.6.0"
serde_json = "1.0.103"
surrealdb = { git = "https://github.com/surrealdb/surrealdb.git", rev = "4969e7c9694bc885eaabc0515116eac71b5e5fb7", features = ["kv-mem", "kv-rocksdb"] }
surrealdb = { version = "1.2.0", features = ["kv-mem"] }
tokio = { version = "1.29.1", features = ["macros"] }
tower-http = { version = "0.4.3", features = ["cors"] }
tracing = "0.1.37"
Expand Down
Empty file added engine/src/lib.rs
Empty file.
11 changes: 6 additions & 5 deletions engine/src/xml/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod error;
#[cfg(test)]
mod module_browser_test;

use scan_fmt::scan_fmt;
Expand Down Expand Up @@ -72,12 +73,13 @@ impl<'de> Visitor<'de> for VersionVisitor {
}

/*
TODO: #38 you should not implement `visit_string` without also implementing `visit_str`,
TODO: #38 you should not implement `visit_string` without also implementing `visit_str`,
hence `visit_str` has to be implemented
*/
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error, {
where
E: de::Error,
{
todo!()
}
}
Expand Down Expand Up @@ -150,8 +152,7 @@ pub enum LearningModuleEntryType {
}

pub fn list_modules(directory: &str) -> Result<Vec<LearningModule>, error::Error> {
let paths =
fs::read_dir(directory).map_err(|_e| -> error::Error { error::Error::Io {} })?;
let paths = fs::read_dir(directory).map_err(|_e| -> error::Error { error::Error::Io {} })?;
let mut ret = Vec::new();
for path in paths {
let module = read_module(path.as_ref().unwrap().path().display().to_string());
Expand Down
194 changes: 95 additions & 99 deletions engine/src/xml/module_browser_test.rs
Original file line number Diff line number Diff line change
@@ -1,66 +1,103 @@
#[cfg(test)]
mod tests {
use crate::xml::{
Answer, CategoryDeclaration, EntryTag, LearningModuleEntries, LearningModuleEntry,
LearningModuleEntryType, Question,
};
use serde::Deserialize;
use serde_xml_rs::Deserializer;
use xml::EventReader;
use crate::xml::{
Answer, CategoryDeclaration, EntryTag, LearningModuleEntries, LearningModuleEntry,
LearningModuleEntryType, Question,
};
use serde::Deserialize;
use serde_xml_rs::Deserializer;
use xml::EventReader;

#[test]
fn result_des_test() {
let input = r#"<answer category="category name" correct="true" id="abc-123">This is the expected answer</answer>"#;
let actual =
Answer::deserialize(&mut Deserializer::new(EventReader::new(input.as_bytes())));
let expected = Answer {
category: "category name".to_string(),
correct: true,
id: "abc-123".to_string(),
label: "This is the expected answer".to_string(),
};
match actual {
Ok(act) => {
assert_eq!(act, expected)
}
Err(e) => {
panic!("failed to deserialise: {}", e)
}
#[test]
fn result_des_test() {
let input = r#"<answer category="category name" correct="true" id="abc-123">This is the expected answer</answer>"#;
let actual = Answer::deserialize(&mut Deserializer::new(EventReader::new(input.as_bytes())));
let expected = Answer {
category: "category name".to_string(),
correct: true,
id: "abc-123".to_string(),
label: "This is the expected answer".to_string(),
};
match actual {
Ok(act) => {
assert_eq!(act, expected)
}
Err(e) => {
panic!("failed to deserialise: {}", e)
}
}
}

#[test]
fn category_des_test() {
let input = "<category-declaration id=\"abc-123\">category text</category-declaration>";
let actual = CategoryDeclaration::deserialize(&mut Deserializer::new(EventReader::new(
input.as_bytes(),
)));
let expected = CategoryDeclaration {
id: "abc-123".to_string(),
label: "category text".to_string(),
};
match actual {
Ok(act) => {
assert_eq!(act, expected)
}
Err(e) => {
panic!("failed to deserialise: {}", e)
}
#[test]
fn category_des_test() {
let input = "<category-declaration id=\"abc-123\">category text</category-declaration>";
let actual = CategoryDeclaration::deserialize(&mut Deserializer::new(EventReader::new(
input.as_bytes(),
)));
let expected = CategoryDeclaration {
id: "abc-123".to_string(),
label: "category text".to_string(),
};
match actual {
Ok(act) => {
assert_eq!(act, expected)
}
Err(e) => {
panic!("failed to deserialise: {}", e)
}
}
}

#[test]
fn entry_des_test() {
let input = r#"
#[test]
fn entry_des_test() {
let input = r#"
<entry id="abc-123" type="single-choice" sampleable="true">
<question>the question</question>
<answer correct="true" id="a1">An answer</answer>
</entry>
"#;
let actual = LearningModuleEntry::deserialize(&mut Deserializer::new(EventReader::new(
input.as_bytes(),
)));
let expected = LearningModuleEntry {
let actual = LearningModuleEntry::deserialize(&mut Deserializer::new(EventReader::new(
input.as_bytes(),
)));
let expected = LearningModuleEntry {
entry_type: LearningModuleEntryType::SingleChoice,
id: "abc-123".to_string(),
sampleable: true,
entry_tags: vec![
EntryTag::Question(Question {
label: "the question".to_string(),
}),
EntryTag::Answer(Answer {
correct: true,
id: "a1".to_string(),
category: "".to_string(),
label: "An answer".to_string(),
}),
],
};
match actual {
Ok(act) => {
assert_eq!(act, expected)
}
Err(e) => {
panic!("failed to deserialise: {}", e)
}
}
}

#[test]
fn entries_des_test() {
let input = r#"
<entries>
<entry id="abc-123" type="single-choice" sampleable="true">
<question>the question</question>
<answer correct="true" id="a1">An answer</answer>
</entry>
</entries>
"#;
let actual = LearningModuleEntries::deserialize(&mut Deserializer::new(EventReader::new(
input.as_bytes(),
)));
let expected = LearningModuleEntries {
entries: vec![LearningModuleEntry {
entry_type: LearningModuleEntryType::SingleChoice,
id: "abc-123".to_string(),
sampleable: true,
Expand All @@ -75,55 +112,14 @@ mod tests {
label: "An answer".to_string(),
}),
],
};
match actual {
Ok(act) => {
assert_eq!(act, expected)
}
Err(e) => {
panic!("failed to deserialise: {}", e)
}
}],
};
match actual {
Ok(act) => {
assert_eq!(act, expected)
}
}

#[test]
fn entries_des_test() {
let input = r#"
<entries>
<entry id="abc-123" type="single-choice" sampleable="true">
<question>the question</question>
<answer correct="true" id="a1">An answer</answer>
</entry>
</entries>
"#;
let actual = LearningModuleEntries::deserialize(&mut Deserializer::new(EventReader::new(
input.as_bytes(),
)));
let expected = LearningModuleEntries {
entries: vec![LearningModuleEntry {
entry_type: LearningModuleEntryType::SingleChoice,
id: "abc-123".to_string(),
sampleable: true,
entry_tags: vec![
EntryTag::Question(Question {
label: "the question".to_string(),
}),
EntryTag::Answer(Answer {
correct: true,
id: "a1".to_string(),
category: "".to_string(),
label: "An answer".to_string(),
}),
],
}],
};
match actual {
Ok(act) => {
assert_eq!(act, expected)
}
Err(e) => {
panic!("failed to deserialise: {}", e)
}
Err(e) => {
panic!("failed to deserialise: {}", e)
}
}
}

0 comments on commit 856c27d

Please sign in to comment.