Add missing SHELL build-arg

Change-Id: Ic645c0411928f8b69c4d5b21a90462170576e7bc
diff --git a/crates/passwd/Cargo.toml b/crates/passwd/Cargo.toml
new file mode 100644
index 0000000..25b3dc1
--- /dev/null
+++ b/crates/passwd/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "passwd"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+thiserror = "2.0.11"
diff --git a/crates/passwd/src/lib.rs b/crates/passwd/src/lib.rs
new file mode 100644
index 0000000..395a441
--- /dev/null
+++ b/crates/passwd/src/lib.rs
@@ -0,0 +1,81 @@
+use std::num::ParseIntError;
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+pub enum PasswdEntryError {
+    #[error("Failed to parse ID: {0}")]
+    FailedToParseID(#[from] ParseIntError),
+
+    #[error("Invalid passwd entry: {0}")]
+    InvalidPasswdEntry(String),
+}
+
+#[derive(Debug, Clone)]
+pub struct PasswdEntry {
+    pub name: String,
+    pub passwd: String,
+    pub uid: u32,
+    pub gid: u32,
+    pub gecos: String,
+    pub dir: String,
+    pub shell: String,
+}
+
+impl PasswdEntry {
+    pub fn from_line(line: &str) -> Result<PasswdEntry, PasswdEntryError> {
+        let parts: Vec<&str> = line.split(":").map(|part| part.trim()).collect();
+
+        if parts.len() != 7 {
+            return Err(PasswdEntryError::InvalidPasswdEntry(line.to_string()));
+        }
+
+        Ok(PasswdEntry {
+            name: parts[0].to_string(),
+            passwd: parts[1].to_string(),
+            uid: parts[2].parse()?,
+            gid: parts[3].parse()?,
+            gecos: parts[4].to_string(),
+            dir: parts[5].to_string(),
+            shell: parts[6].to_string(),
+        })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_from_line_valid() {
+        let line = "username:x:1000:1000:User Name:/home/username:/bin/bash";
+        let entry = PasswdEntry::from_line(line).unwrap();
+        assert_eq!(entry.name, "username");
+        assert_eq!(entry.passwd, "x");
+        assert_eq!(entry.uid, 1000);
+        assert_eq!(entry.gid, 1000);
+        assert_eq!(entry.gecos, "User Name");
+        assert_eq!(entry.dir, "/home/username");
+        assert_eq!(entry.shell, "/bin/bash");
+    }
+
+    #[test]
+    fn test_from_line_invalid_uid() {
+        let line = "username:x:invalid:1000:User Name:/home/username:/bin/bash";
+        let result = PasswdEntry::from_line(line);
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_from_line_invalid_gid() {
+        let line = "username:x:1000:invalid:User Name:/home/username:/bin/bash";
+        let result = PasswdEntry::from_line(line);
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn test_from_line_missing_fields() {
+        let line = "username:x:1000:1000:User Name:/home/username";
+        let result = PasswdEntry::from_line(line);
+        assert!(result.is_err());
+    }
+}