summary refs log tree commit diff
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@gmail.com>2020-12-25 21:10:37 -0800
committerIrene Knapp <ireneista@gmail.com>2020-12-25 21:10:37 -0800
commitefc68f54e3476de0bd209995c36043c26131b8df (patch)
tree676d6915d9daa336586aeb50fb46dcaf09622efb
parentf5ec40b8fbbe7d4409d94dafcbfcdd41b8a6202b (diff)
refactor path parsing into a separate file, implement some Display traits, etc
-rw-r--r--src/error.rs35
-rw-r--r--src/main.rs28
-rw-r--r--src/path.rs169
-rw-r--r--src/path/parser.lalrpop18
-rw-r--r--src/prelude.rs2
-rw-r--r--src/result.rs3
6 files changed, 209 insertions, 46 deletions
diff --git a/src/error.rs b/src/error.rs
index 96be297..6fe18db 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,3 +1,6 @@
+use crate::path::GenericPath;
+
+
 type ParseError<'a> =
     lalrpop_util::ParseError<usize, lalrpop_util::lexer::Token<'a>, &'a str>;
 
@@ -5,6 +8,10 @@ type ParseError<'a> =
 pub enum Error {
     IO(std::io::Error),
     Parse(String),
+    PathListHasEmptyComponents(String),
+    PathIsAFile(String),
+    PathIsRelative(GenericPath),
+    PathInvalid(GenericPath),
 }
 
 impl std::error::Error for Error { }
@@ -14,18 +21,22 @@ impl std::fmt::Display for Error {
         match self {
             Error::IO(e) => e.fmt(f),
             Error::Parse(e) => e.fmt(f),
-        }
-    }
-}
-
-impl std::cmp::PartialEq for Error {
-    fn eq(&self, other: &Self) -> bool {
-        match (self, other) {
-            (Error::IO(_), Error::IO(_)) =>
-                false,
-            (Error::Parse(_), Error::Parse(_)) =>
-                false,
-            _  => false,
+            Error::PathListHasEmptyComponents(path_list) =>
+              f.write_fmt(format_args!(
+                  "Path list has empty components: {}",
+                  path_list)),
+            Error::PathIsAFile(path) =>
+              f.write_fmt(format_args!(
+                  "There's a file at {}, not a directory.",
+                  path)),
+            Error::PathIsRelative(path) =>
+              f.write_fmt(format_args!(
+                  "The path {} is relative, not absolute.",
+                  path)),
+            Error::PathInvalid(path) =>
+              f.write_fmt(format_args!(
+                  "This isn't a valid path. {}",
+                  path)),
         }
     }
 }
diff --git a/src/main.rs b/src/main.rs
index 2a5234f..36fb284 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,4 @@
-use error::Error;
+use crate::prelude::*;
 use std::io;
 use std::io::prelude::*;
 
@@ -7,10 +7,10 @@ use std::io::prelude::*;
 lalrpop_mod!(pub commandline);
 pub mod error;
 pub mod path;
+pub mod prelude;
+pub mod result;
 
 
-pub type Result<T> = std::result::Result<T, Error>;
-
 pub enum Input {
     String(String),
     End,
@@ -72,25 +72,9 @@ fn execute(input: &str) -> Result<()> {
 
     match invocation.as_slice() {
       ["paths", path_list, ..] => {
-        println!("{:?}", path_list);
-        match path::parser::PathListParser::new().parse(path_list) {
-          Ok(parsed_paths) => {
-            println!("paths '{:?}'", parsed_paths);
-          },
-          Err(_) => {
-            match path::parser::PathListAllowingEmptyPathsParser::new()
-              .parse(path_list)
-            {
-              Ok(parsed) => {
-                println!("path list has empty component");
-                println!("{:?}", parsed);
-              },
-              Err(error) => {
-                println!("path list is okay, does not have empty component");
-                println!("{:?}", error);
-              },
-            }
-          },
+        let paths = path::parse_path_list(path_list)?;
+        for path in &paths {
+          println!("{}", path);
         }
       },
       _ => {
diff --git a/src/path.rs b/src/path.rs
index bfb5719..7a81873 100644
--- a/src/path.rs
+++ b/src/path.rs
@@ -1,15 +1,24 @@
+use crate::prelude::*;
+
 lalrpop_mod!(pub parser, "/path/parser.rs");
 
 
 #[derive(Debug)]
-pub struct DirectoryName(String);
+pub struct AbsoluteDirectoryPath {
+  components: Vec<DirectoryName>,
+}
 
 #[derive(Debug)]
 pub struct FileName(String);
 
 #[derive(Debug)]
-pub struct AbsoluteDirectoryPath {
-  components: Vec<DirectoryName>,
+pub struct DirectoryName(String);
+
+#[derive(Debug)]
+pub struct GenericPath {
+  components: Vec<GenericPathComponent>,
+  starts_with_slash: bool,
+  ends_with_slash: bool,
 }
 
 #[derive(Debug)]
@@ -19,9 +28,153 @@ pub enum GenericPathComponent {
   ParentDirectory,
 }
 
-#[derive(Debug)]
-pub struct GenericPath {
-  components: Vec<GenericPathComponent>,
-  starts_with_slash: bool,
-  ends_with_slash: bool,
+
+impl std::fmt::Display for AbsoluteDirectoryPath {
+  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    for component in &self.components {
+      f.write_str("/")?;
+      component.fmt(f)?;
+    }
+
+    f.write_str("/")?;
+
+    Ok(())
+  }
+}
+
+
+impl std::fmt::Display for FileName {
+  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    match self {
+      FileName(name) => {
+        let std_path = std::path::Path::new(&name);
+        f.write_fmt(format_args!("{}", std_path.display()))?;
+      },
+    }
+
+    Ok(())
+  }
 }
+
+
+impl std::fmt::Display for DirectoryName {
+  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    match self {
+      DirectoryName(name) => {
+        let std_path = std::path::Path::new(&name);
+        f.write_fmt(format_args!("{}", std_path.display()))?;
+      },
+    }
+
+    Ok(())
+  }
+}
+
+
+impl std::fmt::Display for GenericPath {
+  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    if self.starts_with_slash {
+      f.write_str("/")?;
+    }
+
+    let mut is_first = true;
+    for component in &self.components {
+      if !is_first {
+        f.write_str("/")?;
+      }
+
+      component.fmt(f)?;
+
+      is_first = false;
+    }
+
+    if self.ends_with_slash {
+      f.write_str("/")?;
+    }
+
+    Ok(())
+  }
+}
+
+
+impl std::fmt::Display for GenericPathComponent {
+  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    match self {
+      GenericPathComponent::FileOrDirectoryName(name) => {
+        let std_path = std::path::Path::new(&name);
+        f.write_fmt(format_args!("{}", std_path.display()))?;
+      },
+      GenericPathComponent::CurrentDirectory => {
+        f.write_str(".")?;
+      },
+      GenericPathComponent::ParentDirectory => {
+        f.write_str("..")?;
+      },
+    }
+
+    Ok(())
+  }
+}
+
+
+pub fn parse_path_list(path_list: &str)
+  -> Result<Vec<AbsoluteDirectoryPath>>
+{
+  match parser::PathListParser::new().parse(path_list) {
+    Ok(parsed_paths) => {
+      let mut result = Vec::new();
+      for generic_path in parsed_paths {
+        let path = absolute_directory_path(generic_path)?;
+        result.push(path);
+      }
+      Ok(result)
+    },
+   Err(original_error) => {
+      match parser::PathListAllowingEmptyPathsParser::new()
+        .parse(path_list)
+      {
+        Ok(_) => {
+          Err(Error::PathListHasEmptyComponents(path_list.to_string()))
+        },
+        Err(_) => {
+          Err(Error::Parse(original_error.to_string()))
+        },
+      }
+    },
+  }
+}
+
+
+pub fn absolute_directory_path(generic_path: GenericPath)
+  -> Result<AbsoluteDirectoryPath>
+{
+  if !generic_path.starts_with_slash {
+    return Err(Error::PathIsRelative(generic_path));
+  }
+
+  let mut flattened_components = Vec::new();
+  for component in &generic_path.components {
+    match component {
+      GenericPathComponent::CurrentDirectory => { },
+      GenericPathComponent::ParentDirectory => {
+        if flattened_components.len() > 0 {
+          flattened_components.pop();
+        } else {
+          return Err(Error::PathInvalid(generic_path));
+        }
+      },
+      GenericPathComponent::FileOrDirectoryName(name) => {
+        flattened_components.push(DirectoryName(name.to_string()));
+      },
+    }
+  }
+
+  if flattened_components.len() == 0 {
+    return Err(Error::PathInvalid(generic_path));
+  }
+
+  Ok(AbsoluteDirectoryPath {
+    components: flattened_components,
+  })
+}
+
diff --git a/src/path/parser.lalrpop b/src/path/parser.lalrpop
index 0b3be9a..3547444 100644
--- a/src/path/parser.lalrpop
+++ b/src/path/parser.lalrpop
@@ -63,14 +63,20 @@ pub PathNoColons: GenericPath = {
 }
 
 PathNoColons2: Vec<GenericPathComponent> = {
-  <PATH_COMPONENT_NO_COLONS> =>
-      vec![GenericPathComponent::FileOrDirectoryName(<>.to_string())],
-  <mut left:PathNoColons2> SLASH <right:PATH_COMPONENT_NO_COLONS> => {
-    left.push(GenericPathComponent::FileOrDirectoryName(right.to_string()));
+  <PathComponent> => vec![<>],
+  <mut left:PathNoColons2> SLASH <right:PathComponent> => {
+    left.push(right);
     left
   }
 }
 
+PathComponent: GenericPathComponent = {
+  DOT => GenericPathComponent::CurrentDirectory,
+  DOT_DOT => GenericPathComponent::ParentDirectory,
+  <PATH_COMPONENT_NO_COLONS> =>
+    GenericPathComponent::FileOrDirectoryName(<>.to_string())
+}
+
 // Whitespace is not allowed.
 match {
   r"[^z:/]+" => PATH_COMPONENT_NO_COLONS,
@@ -78,5 +84,9 @@ match {
   r"/" => SLASH,
 
   ":" => COLON,
+
+  "." => DOT,
+
+  ".." => DOT_DOT,
 }
 
diff --git a/src/prelude.rs b/src/prelude.rs
new file mode 100644
index 0000000..b44fe02
--- /dev/null
+++ b/src/prelude.rs
@@ -0,0 +1,2 @@
+pub use crate::error::Error;
+pub use crate::result::Result;
diff --git a/src/result.rs b/src/result.rs
new file mode 100644
index 0000000..8207680
--- /dev/null
+++ b/src/result.rs
@@ -0,0 +1,3 @@
+use crate::error::Error;
+
+pub type Result<T> = std::result::Result<T, Error>;