Skip to content

Instantly share code, notes, and snippets.

@RandyMcMillan
Forked from rust-play/playground.rs
Last active November 30, 2025 20:46
Show Gist options
  • Select an option

  • Save RandyMcMillan/a3fa7b7b450064853ae635b750dff44b to your computer and use it in GitHub Desktop.

Select an option

Save RandyMcMillan/a3fa7b7b450064853ae635b750dff44b to your computer and use it in GitHub Desktop.
lojban_gemini.rs
// --- Dependencies ---
// This simple example relies only on standard library features.
use std::error::Error;
use std::fmt;
// --- Error Handling ---
/// A simple custom error type for parsing issues.
#[derive(Debug)]
struct ParseError {
details: String,
}
impl ParseError {
fn new(details: &str) -> ParseError {
ParseError { details: details.to_string() }
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Parse Error: {}", self.details)
}
}
impl Error for ParseError {}
/// Type alias for simplified error handling in the parser.
type Result<T> = std::result::Result<T, ParseError>;
// --- Command Definition ---
/// Represents the unambiguous Lojban commands Gemini is designed to execute.
#[derive(Debug, PartialEq)]
enum GeminiCommand {
/// ko sisku [query]
Search { query: String },
/// ko pilno la .youtube. [query]
UseYoutube { query: String },
/// ko skicu [topic]
Summarize { topic: String },
/// ko finti [artifact]
GenerateText { artifact: String },
/// ko finti le pixra be [subject]
GenerateImage { subject: String },
/// ko pilnoi la .remindr. [task]
SetReminder { task: String },
/// A command structure that couldn't be parsed.
Unknown,
}
// --- Parser Function ---
/// Parses a Lojban command string into a structured GeminiCommand.
fn parse_lojban_command(input: &str) -> Result<GeminiCommand> {
// 1. Normalize and split the input
let parts: Vec<&str> = input.trim().splitn(3, ' ').collect();
if parts.len() < 2 || parts[0] != "ko" {
return Err(ParseError::new("Invalid command structure. Must begin with 'ko'."));
}
// Command keyword is always the second element
let keyword = parts[1];
// The argument/sumti is the rest of the string after "ko [keyword]"
let argument = parts.get(2).unwrap_or(&"").trim();
// The special case for "lo" is removed for simplicity, all commands require a sumti
if argument.is_empty() {
return Err(ParseError::new(&format!("Missing argument (sumti) for command: {}", keyword)));
}
// 2. Match the keyword and parse the argument
match keyword {
"sisku" => Ok(GeminiCommand::Search { query: argument.to_string() }),
"pilno" => {
// Check for the specific tool-use command: ko pilno la .youtube. [query]
let tool_prefix = "la .youtube.";
if argument.starts_with(tool_prefix) {
// Ensure we handle the space after the tool name
let query = argument.trim_start_matches(tool_prefix).trim();
if query.is_empty() {
return Err(ParseError::new("Missing query after 'ko pilno la .youtube.'."));
}
Ok(GeminiCommand::UseYoutube { query: query.to_string() })
} else {
Err(ParseError::new("Unknown 'pilno' command or missing tool name (expected 'la .youtube.')."))
}
},
"skicu" => Ok(GeminiCommand::Summarize { topic: argument.to_string() }),
"finti" => {
// Check for the image generation case: ko finti le pixra be [subject]
let image_prefix = "le pixra be";
if argument.starts_with(image_prefix) {
// Extract the subject from after "le pixra be"
let subject = argument.trim_start_matches(image_prefix).trim();
if subject.is_empty() {
return Err(ParseError::new("Missing subject after 'ko finti le pixra be'."));
}
Ok(GeminiCommand::GenerateImage { subject: subject.to_string() })
} else {
// Default to general text/code generation
Ok(GeminiCommand::GenerateText { artifact: argument.to_string() })
}
},
"pilnoi" => {
// Check for the reminder tool
let reminder_prefix = "la .remindr.";
if argument.starts_with(reminder_prefix) {
let task = argument.trim_start_matches(reminder_prefix).trim();
if task.is_empty() {
return Err(ParseError::new("Missing task after 'ko pilnoi la .remindr.'."));
}
Ok(GeminiCommand::SetReminder { task: task.to_string() })
} else {
Err(ParseError::new("Unknown 'pilnoi' tool specified."))
}
}
_ => Err(ParseError::new(&format!("Unknown Lojban keyword: {}", keyword))),
}
}
// --- Main Function (Demonstration) ---
fn main() {
println!("Lojban Command Parser: Test Suite\n---");
let commands = vec![
"ko sisku le krasi progaritme", // Search
"ko finti le pixra be le lartu gerku", // Generate Image
"ko pilno la .youtube. le zmadu nanba be'o", // Use YouTube
"ko pilnoi la .remindr. le nu djica le titnanba",// Set Reminder
"ko skicu le gismu", // Summarize
"ko finti le krasi", // Generate Text
"ko pilno la .gugl.", // Invalid/Unknown pilno
"sisku le nanmu", // Missing 'ko'
"ko sisku", // Missing sumti
];
for cmd_str in commands {
match parse_lojban_command(cmd_str) {
Ok(command) => {
println!("✅ **Input:** '{}'", cmd_str);
println!(" **Parsed:** {:?}", command);
},
Err(e) => {
println!("❌ **Input:** '{}'", cmd_str);
println!(" **Error:** {}", e);
},
}
println!("---");
}
}
// --- Test Module ---
#[cfg(test)]
mod tests {
use super::*;
// --- Success Tests ---
#[test]
fn test_search_command_success() {
let input = "ko sisku le krasi progaritme";
let expected = GeminiCommand::Search { query: "le krasi progaritme".to_string() };
assert_eq!(parse_lojban_command(input).unwrap(), expected);
}
#[test]
fn test_generate_image_command_success() {
let input = "ko finti le pixra be le lartu gerku";
let expected = GeminiCommand::GenerateImage { subject: "le lartu gerku".to_string() };
assert_eq!(parse_lojban_command(input).unwrap(), expected);
}
#[test]
fn test_generate_text_command_success() {
let input = "ko finti le krasi";
let expected = GeminiCommand::GenerateText { artifact: "le krasi".to_string() };
assert_eq!(parse_lojban_command(input).unwrap(), expected);
}
#[test]
fn test_youtube_command_success() {
let input = "ko pilno la .youtube. le zmadu nanba be'o";
let expected = GeminiCommand::UseYoutube { query: "le zmadu nanba be'o".to_string() };
assert_eq!(parse_lojban_command(input).unwrap(), expected);
}
#[test]
fn test_reminder_command_success() {
let input = "ko pilnoi la .remindr. le nu djica le titnanba";
let expected = GeminiCommand::SetReminder { task: "le nu djica le titnanba".to_string() };
assert_eq!(parse_lojban_command(input).unwrap(), expected);
}
#[test]
fn test_summarize_command_success() {
let input = "ko skicu le gismu";
let expected = GeminiCommand::Summarize { topic: "le gismu".to_string() };
assert_eq!(parse_lojban_command(input).unwrap(), expected);
}
// --- Failure Tests ---
#[test]
fn test_missing_ko_failure() {
let input = "sisku le nanmu";
assert!(parse_lojban_command(input).is_err());
assert!(parse_lojban_command(input).unwrap_err().to_string().contains("Must begin with 'ko'"));
}
#[test]
fn test_missing_sumti_failure() {
let input = "ko sisku";
assert!(parse_lojban_command(input).is_err());
assert!(parse_lojban_command(input).unwrap_err().to_string().contains("Missing argument (sumti) for command: sisku"));
}
#[test]
fn test_unknown_keyword_failure() {
let input = "ko klama le stizu";
assert!(parse_lojban_command(input).is_err());
assert!(parse_lojban_command(input).unwrap_err().to_string().contains("Unknown Lojban keyword: klama"));
}
#[test]
fn test_unknown_pilno_tool_failure() {
let input = "ko pilno la .gugl. le nanmu";
assert!(parse_lojban_command(input).is_err());
assert!(parse_lojban_command(input).unwrap_err().to_string().contains("expected 'la .youtube.'"));
}
#[test]
fn test_incomplete_image_command_failure() {
let input = "ko finti le pixra be";
assert!(parse_lojban_command(input).is_err());
assert!(parse_lojban_command(input).unwrap_err().to_string().contains("Missing subject after 'ko finti le pixra be'."));
}
}
@RandyMcMillan
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment