~calmbit/suki

d219269b85ddcbbb9f1bb364427de390fe7dcda8 — CalmBit 6 months ago 34df65c
basic tagging functionality
2 files changed, 64 insertions(+), 39 deletions(-)

M src/main.rs
M src/suki.rs
M src/main.rs => src/main.rs +39 -22
@@ 4,7 4,6 @@ mod suki;

fn main() -> Result<(), String> {
    let args: Vec<String> = std::env::args().collect();
    
    if args.is_empty() {
        print_help();
        return Ok(());


@@ 12,56 11,74 @@ fn main() -> Result<(), String> {

    let a = match args.get(1) {
        Some(s) => String::from(s),
        None => panic!("args but no args? sanity check failed")
        None => panic!("args but no args? sanity check failed"),
    };

    match a.as_ref() {
        "t" | "tag" => {
            match args.get(2) {
                Some(s) => return tag(s, args.split_at(3).1),
                None => return Err(String::from("no filename supplied to tag"))
                None => return Err(String::from("no filename supplied to tag")),
            };
        },
        }
        "r" | "remove" => println!("remove"),
        "s" | "search" => println!("search"),
        "h" | "help" => {
            print_help();
            return Ok(());
        },
        }
        "v" | "version" => {
            print_version();
            return Ok(());
        }
        _ => println!("bad")
        _ => println!("bad"),
    };

    Ok(())

}

fn tag(filename: &str, tags: &[String]) -> Result<(), String> {
    let dir = curr_dir();
    println!("file: {}, tags: {:?}", filename, tags);

    let file = suki::SukiFile::new(&dir)?;

    println!("{:?}", file);

    let mut file = suki::SukiFile::new(&dir)?;

    if !tags.is_empty() {
        for t in tags {
            let mut found = false;
            for st in &mut file.tags {
                if t.eq(&st.tag) {
                    st.files.push(String::from(filename));
                    found = true;
                    break;
                }
            } 
            if !found {
                let mut new_tag = suki::SukiTag::new(t);
                new_tag.files.push(String::from(filename));
                file.tags.push(new_tag);
            }
        }
    }

    file.serialize("contrib")

    //Ok(())
}

fn print_version() {
    println!("{} version {} - the simple unique krap itemizer", bin_name(), VERSION);
    println!(
        "{} version {} - the simple unique krap itemizer",
        bin_name(),
        VERSION
    );
}

fn print_help() {
    print_version();
    println!("commands:");
    println!("\t<t | tag> [flags] <filename> [tags]      adds file to the specified tags");
    println!("\t<r | remove> [flags] <filename> [tags]   removes the tag(s) from the file specified");
    println!(
        "\t<r | remove> [flags] <filename> [tags]   removes the tag(s) from the file specified"
    );
    println!("\t<s | search> [flags] [tags]              searches the tag database for files with the corresponding tag(s)");
    println!("\t<h | help>                               displays this help");
    println!("\t<v | version>                            displays version");


@@ 74,11 91,11 @@ fn bin_name() -> String {
        Ok(p) => match p.file_stem() {
            Some(s) => match s.to_str() {
                Some(st) => String::from(st),
                None => panic!("Path stem invalid unicode from '{:?}'", s)
                None => panic!("Path stem invalid unicode from '{:?}'", s),
            },
            None => panic!("Unable to resolve file stem from '{:?}'", p)
        }
        Err(e) => panic!("{}", e)
            None => panic!("Unable to resolve file stem from '{:?}'", p),
        },
        Err(e) => panic!("{}", e),
    }
}



@@ 86,8 103,8 @@ fn curr_dir() -> String {
    match std::env::current_dir() {
        Ok(p) => match p.to_str() {
            Some(s) => String::from(s),
            None => panic!("unable to resolve dir {:?} to string", p)
            None => panic!("unable to resolve dir {:?} to string", p),
        },
        Err(e) => panic!("{}", e)
        Err(e) => panic!("{}", e),
    }
}
\ No newline at end of file
}

M src/suki.rs => src/suki.rs +25 -17
@@ 5,22 5,31 @@ use std::io::{Read, Write};
/// SukiFiles.
#[derive(std::fmt::Debug)]
pub struct SukiTag {
    tag: String,
    files: Vec<String>,
    pub tag: String,
    pub files: Vec<String>,
}

/// SukiFiles are the primary operative component of suki, encapsulating all of
/// the tag -> filename relationships that `.suki` files encode for.
#[derive(std::fmt::Debug)]
pub struct SukiFile {
    tags: Vec<SukiTag>,
    pub tags: Vec<SukiTag>,
}

impl SukiTag {
    pub fn new(tag: &str) -> SukiTag {
        SukiTag {
            tag: String::from(tag),
            files: Vec::new(),
        }
    }
}

impl SukiFile {
    pub fn serialize(self, path: &str) -> Result<(), String> {
        let mut file = match open_and_clear_suki(path) {
            Ok(f) => f,
            Err(e) => return Err(String::from(e.description())) 
            Err(e) => return Err(String::from(e.description())),
        };

        let mut buf = String::new();


@@ 31,18 40,17 @@ impl SukiFile {
            for f in tag.files {
                buf.push_str(&format!("\t{}\n", f))
            }
            
        }

        match file.write_all(buf.as_bytes()) {
            Ok(_) => (),
            Err(e) => return Err(String::from(e.description()))
        }; 
            Err(e) => return Err(String::from(e.description())),
        };

        Ok(())
    }

    pub fn new(path: &str) -> Result<SukiFile, String> {

        let mut file_buffer = SukiFile { tags: Vec::new() };

        let mut txt_file = match open_suki(path) {


@@ 65,7 73,9 @@ impl SukiFile {
            if l.starts_with('\t') {
                match tag_buffer.as_mut() {
                    Some(s) => s.files.push(String::from(&l[1..])),
                    None => return Err(format!("bad syntax - cannot start suki file with filename")),
                    None => {
                        return Err(format!("bad syntax - cannot start suki file with filename"))
                    }
                }
                continue;
            }


@@ 84,20 94,18 @@ impl SukiFile {
                }
                // Set up the brand new tag with its label, and an empty vector
                // for its filenames.
                tag_buffer = Some(SukiTag {
                tag_buffer = Some(SukiTag::new(
                    // Right here we cut off the colon at the end of the line.
                    tag: String::from(&l[..l.len()-1]),
                    files: Vec::new(),
                });
                    &l[..l.len() - 1],
                ));
                continue;
            } 
            }

            // TODO: This is arbitrary. Pending removal/change of message?
            return Err(format!(
                "bad syntax at line {} - missing ':' at end of label descriptor",
                line_no
            ));
            
        }

        // At worse, we should have at least one tag left over that didn't get


@@ 110,7 118,7 @@ impl SukiFile {
}

/// Opens a `.suki` file lying at the root of the given path.
/// 
///
/// Returns: an `std::io::Result` containing a handle to the file, or an I/O
/// error if something goes wrong.
fn open_suki(path: &str) -> std::io::Result<std::fs::File> {


@@ 134,7 142,7 @@ fn open_and_clear_suki(path: &str) -> std::io::Result<std::fs::File> {

/// A convienince function for divining the actual literal path of a suki file
/// in a directory.
/// 
///
/// Returns: a `String` containing the path argument with `/.suki` appended.
fn suki_path(path: &str) -> String {
    let mut path = String::from(path);