1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (C) 2024 Intel Corporation
// SPDX-License-Identifier: Apache-2.0

//! Utility functionality to assist managing SIMICS projects

use crate::{Error, Result};
use regex::Regex;
use std::{
    fs::{copy, create_dir_all},
    path::{Path, PathBuf},
    str::FromStr,
};
use walkdir::WalkDir;

/// Copy the contents of one directory to another, recursively, overwriting files if they exist but
/// without replacing directories or their contents if they already exist
pub fn copy_dir_contents<P>(src_dir: P, dst_dir: P) -> Result<()>
where
    P: AsRef<Path>,
{
    let src_dir = src_dir.as_ref().to_path_buf();

    if !src_dir.is_dir() {
        return Err(Error::NotADirectory {
            path: src_dir.clone(),
        });
    }

    let dst_dir = dst_dir.as_ref().to_path_buf();

    if !dst_dir.is_dir() {
        create_dir_all(&dst_dir)?;
    }

    for (src, dst) in WalkDir::new(&src_dir)
        .into_iter()
        .filter_map(|p| p.ok())
        .filter_map(|p| {
            let src = p.path().to_path_buf();
            match src.strip_prefix(&src_dir) {
                Ok(suffix) => Some((src.clone(), dst_dir.join(suffix))),
                Err(_) => None,
            }
        })
    {
        if src.is_dir() {
            create_dir_all(&dst)?;
        } else if src.is_file() {
            copy(&src, &dst)?;
        }
    }

    Ok(())
}

/// A library file type
pub enum LibraryType {
    /// A static archive library
    Static,
    /// A dynamic library
    Dynamic,
}

impl FromStr for LibraryType {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self> {
        if s.ends_with(".a") {
            Ok(Self::Static)
        } else if s.ends_with(".so") {
            Ok(Self::Dynamic)
        } else {
            Err(Error::UnrecognizedLibraryTypeExtension {
                library_type: s.to_string(),
            })
        }
    }
}

impl LibraryType {
    /// The suffix of a library
    pub fn suffix(&self) -> String {
        match self {
            Self::Static => ".a".to_string(),
            Self::Dynamic => ".so".to_string(),
        }
    }
}

/// Locate a file recursively using a regex pattern in directory. If there are multiple
/// occurrences of a filename, it is undefined which will be returned.
pub fn find_file_in_dir<P, S>(directory: P, pattern: S) -> Result<PathBuf>
where
    P: AsRef<Path>,
    S: AsRef<str>,
{
    let file_name_regex = Regex::new(pattern.as_ref())?;
    let found_file = WalkDir::new(&directory)
        .into_iter()
        .filter_map(|de| de.ok())
        // is_ok_and is unstable ;_;
        .filter(|de| {
            if let Ok(m) = de.metadata() {
                m.is_file()
            } else {
                false
            }
        })
        .find(|de| {
            if let Some(name) = de.path().file_name() {
                file_name_regex.is_match(&name.to_string_lossy())
            } else {
                false
            }
        })
        .ok_or_else(|| Error::FileNotFoundInDirectory {
            directory: directory.as_ref().to_path_buf(),
            pattern: pattern.as_ref().to_string(),
        })?
        .path()
        .to_path_buf();

    if !found_file.is_file() {
        Err(Error::FileNotFoundInDirectory {
            directory: directory.as_ref().to_path_buf(),
            pattern: pattern.as_ref().to_string(),
        })
    } else {
        Ok(found_file)
    }
}