Skip to content

Commit

Permalink
[crater] Move target stuff into ::target module
Browse files Browse the repository at this point in the history
I want to enforce some invariants on these types, which requires making
some of their fields non-visible, which requires them to be in a module.

Doing this in a separate PR for the benefit of clearer diffs for
subsequent PRs.
  • Loading branch information
cmyr committed Oct 30, 2024
1 parent 7b18241 commit 09aca87
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 124 deletions.
128 changes: 4 additions & 124 deletions fontc_crater/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

use std::{
collections::BTreeMap,
fmt::Display,
path::{Path, PathBuf},
path::Path,
process::{Command, Stdio},
str::FromStr,
sync::atomic::{AtomicUsize, Ordering},
};

Expand All @@ -16,12 +14,14 @@ use rayon::{prelude::*, ThreadPoolBuilder};
mod args;
mod ci;
mod error;
mod target;
mod ttx_diff_runner;

use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde::{de::DeserializeOwned, Serialize};

use args::{Args, Commands};
use error::Error;
use target::{BuildType, Target, TargetId};

fn main() {
env_logger::init();
Expand All @@ -44,29 +44,6 @@ struct Results<T, E> {
pub(crate) failure: BTreeMap<TargetId, E>,
}

#[derive(Clone, Debug)]
pub(crate) struct Target {
// will be used in gftools mode
pub(crate) config: PathBuf,
pub(crate) source: PathBuf,
pub(crate) build: BuildType,
}

/// Uniquely identify a source + build type (default, gftools)
///
/// this is separate from 'target' because it doesn't preserve the config path.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct TargetId {
pub(crate) path: PathBuf,
pub(crate) build: BuildType,
}

#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum BuildType {
Default,
GfTools,
}

/// The output of trying to run on one font.
///
/// We don't use a normal Result because failure is okay, we will report it all at the end.
Expand Down Expand Up @@ -152,86 +129,6 @@ fn pip_freeze_sha() -> String {
.to_owned()
}

impl Target {
pub(crate) fn id(&self) -> TargetId {
TargetId {
path: self.source.clone(),
build: self.build,
}
}
}

impl Display for Target {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.id().fmt(f)
}
}

impl BuildType {
pub(crate) fn name(&self) -> &'static str {
match self {
BuildType::Default => "default",
BuildType::GfTools => "gftools",
}
}
}

impl Display for BuildType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name())
}
}

impl Display for TargetId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} ({})", self.path.display(), self.build)
}
}

impl Serialize for TargetId {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.to_string().serialize(serializer)
}
}

impl<'de> Deserialize<'de> for TargetId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(deserializer)?;
FromStr::from_str(s).map_err(serde::de::Error::custom)
}
}

impl FromStr for TargetId {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if !s.ends_with(')') {
return Ok(Self {
path: PathBuf::from(s),
build: BuildType::Default,
});
}
// else expect the format,
// PATH (default|gftools)
let (path, type_) = s
.rsplit_once('(')
.ok_or_else(|| "missing opening paren".to_string())?;

let path = PathBuf::from(path.trim());
let type_ = match type_.trim_end_matches(')') {
"default" => BuildType::Default,
"gftools" => BuildType::GfTools,
other => return Err(format!("unknown build type '{other}'")),
};

Ok(TargetId { path, build: type_ })
}
}

impl<T, E> FromIterator<(TargetId, RunResult<T, E>)> for Results<T, E> {
fn from_iter<I: IntoIterator<Item = (TargetId, RunResult<T, E>)>>(iter: I) -> Self {
let mut out = Results::default();
Expand Down Expand Up @@ -319,20 +216,3 @@ fn try_create_dir(path: &Path) -> Result<(), Error> {
error,
})
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn serde_target_id() {
let id = TargetId {
path: PathBuf::from("../my/file.is_here"),
build: BuildType::GfTools,
};

let to_json = serde_json::to_string(&id).unwrap();
let from_json: TargetId = serde_json::from_str(&to_json).unwrap();
assert_eq!(id, from_json)
}
}
125 changes: 125 additions & 0 deletions fontc_crater/src/target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//! targets of a compilation

use std::{fmt::Display, path::PathBuf, str::FromStr};

use serde::{Deserialize, Serialize};

#[derive(Clone, Debug)]
pub(crate) struct Target {
// will be used in gftools mode
pub(crate) config: PathBuf,
pub(crate) source: PathBuf,
pub(crate) build: BuildType,
}

/// Uniquely identify a source + build type (default, gftools)
///
/// this is separate from 'target' because it doesn't preserve the config path.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct TargetId {
pub(crate) path: PathBuf,
pub(crate) build: BuildType,
}

#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) enum BuildType {
Default,
GfTools,
}

impl Target {
pub(crate) fn id(&self) -> TargetId {
TargetId {
path: self.source.clone(),
build: self.build,
}
}
}

impl Display for Target {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.id().fmt(f)
}
}

impl BuildType {
pub(crate) fn name(&self) -> &'static str {
match self {
BuildType::Default => "default",
BuildType::GfTools => "gftools",
}
}
}

impl Display for BuildType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name())
}
}

impl Display for TargetId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} ({})", self.path.display(), self.build)
}
}

impl Serialize for TargetId {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.to_string().serialize(serializer)
}
}

impl<'de> Deserialize<'de> for TargetId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(deserializer)?;
FromStr::from_str(s).map_err(serde::de::Error::custom)
}
}

impl FromStr for TargetId {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if !s.ends_with(')') {
return Ok(Self {
path: PathBuf::from(s),
build: BuildType::Default,
});
}
// else expect the format,
// PATH (default|gftools)
let (path, type_) = s
.rsplit_once('(')
.ok_or_else(|| "missing opening paren".to_string())?;

let path = PathBuf::from(path.trim());
let type_ = match type_.trim_end_matches(')') {
"default" => BuildType::Default,
"gftools" => BuildType::GfTools,
other => return Err(format!("unknown build type '{other}'")),
};

Ok(TargetId { path, build: type_ })
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn serde_target_id() {
let id = TargetId {
path: PathBuf::from("../my/file.is_here"),
build: BuildType::GfTools,
};

let to_json = serde_json::to_string(&id).unwrap();
let from_json: TargetId = serde_json::from_str(&to_json).unwrap();
assert_eq!(id, from_json)
}
}

0 comments on commit 09aca87

Please sign in to comment.