« Back to Index

Rust: Parse program releases

View original Gist on GitHub

Tags: #rust #exercise #io #file #semver #trait

parse-program-releases.rs

#![allow(unused)]

use std::{
    fmt::Display,
    fs::File,
    io::{BufRead, BufReader, Read},
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct SemVer {
    major: u16,
    minor: u16,
    patch: u16,
}

impl SemVer {
    fn new(major: u16, minor: u16, patch: u16) -> SemVer {
        SemVer {
            major,
            minor,
            patch,
        }
    }

    fn new_short(major: u16) -> SemVer {
        Self::new(major, 0, 0)
    }

    fn info(&self) {
        println!(
            "hi, I'm a semver: {}.{}.{}",
            self.major, self.minor, self.patch
        )
    }

    fn patch(&mut self) -> &mut u16 {
        &mut self.patch
    }
}

impl Default for SemVer {
    fn default() -> Self {
        Self::new_short(1)
    }
}

impl Display for SemVer {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
    }
}

// http://doc.rust-lang.org/1.59.0/std/convert/trait.From.html
impl From<&str> for SemVer {
    fn from(s: &str) -> Self {
        let vs: Vec<u16> = s.split('.').filter_map(|item| item.parse().ok()).collect();
        assert!(vs.len() == 3);
        SemVer {
            major: vs[0],
            minor: vs[1],
            patch: vs[2],
        }
    }
}

#[derive(Debug)]
struct Program {
    name: String,
    release_history: Vec<SemVer>,
}

fn main() -> Result<(), std::io::Error> {
    // create a `Vec` to hold the list of programs
    let mut programs: Vec<Program> = Vec::new();

    // open "releases.txt", bail on error
    let f = File::open("releases.txt")?;
    let reader = BufReader::new(f);

    // use a `BufReader` to iterate over the lines of the file handle
    // if the line can be read (it might be invalid data), split it on ","
    // take the first element of your split - that's the name
    // the rest is a list of &str slices that each can be MAPPED INTO a SemVer!
    // we're still in iterator land - time to collect and push the result to our program vec
    // 
    // TODO: Investigate reducing down with either .map or .filter_map.
    for line in reader.lines().flatten() {
        let line: Vec<&str> = line.split(',').collect();
        let program = line[0];

        if !program.is_empty() {
            let versions = line[1..].iter().filter(|item| !item.is_empty());
            let mut semvers: Vec<SemVer> = Vec::new();
            for version in versions {
                semvers.push(SemVer::from(*version))
            }
            if !semvers.is_empty() {
                programs.push(Program{
                    name: program.to_string(),
                    release_history: semvers,
                });
            }
        }
    }

    // finally, print the program vec.
    println!("{:#?}", programs);

    Ok(())
}

releases.txt

hello-world,0.0.1,0.0.5,1.0.0

no-versions-with-comma,
semver-training,0.0.1
no-versions-no-comma
foo-with-sparse-array-of-versions,1.0.1,,2.0.0
file-io,0.1.5,1.0.1,2.0.0,2.0.5