use std::convert::TryFrom;
use crate::dockerfile_parser::Instruction;
use crate::image::ImageRef;
use crate::parser::{Pair, Rule};
use crate::splicer::*;
use crate::error::*;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FromInstruction {
pub span: Span,
pub image: String,
pub image_span: Span,
pub image_parsed: ImageRef,
pub index: usize,
pub alias: Option<String>,
pub alias_span: Option<Span>
}
impl FromInstruction {
pub(crate) fn from_record(record: Pair, index: usize) -> Result<FromInstruction> {
let span = Span::from_pair(&record);
let mut image_field = None;
let mut alias_field = None;
for field in record.into_inner() {
match field.as_rule() {
Rule::from_image => image_field = Some(field),
Rule::from_alias => alias_field = Some(field),
Rule::comment => continue,
_ => return Err(unexpected_token(field))
};
}
let (image, image_span) = if let Some(image_field) = image_field {
(image_field.as_str().to_string(), Span::from_pair(&image_field))
} else {
return Err(Error::GenericParseError {
message: "missing from image".into()
});
};
let image_parsed = ImageRef::parse(&image);
let (alias, alias_span) = if let Some(alias_field) = alias_field {
(
Some(alias_field.as_str().to_string()),
Some(Span::from_pair(&alias_field))
)
} else {
(None, None)
};
Ok(FromInstruction {
span, index,
image, image_span, image_parsed,
alias, alias_span,
})
}
}
impl<'a> TryFrom<&'a Instruction> for &'a FromInstruction {
type Error = Error;
fn try_from(instruction: &'a Instruction) -> std::result::Result<Self, Self::Error> {
if let Instruction::From(f) = instruction {
Ok(f)
} else {
Err(Error::ConversionError {
from: format!("{:?}", instruction),
to: "FromInstruction".into()
})
}
}
}
#[cfg(test)]
mod tests {
use indoc::indoc;
use super::*;
use crate::test_util::*;
#[test]
fn from_no_alias() -> Result<()> {
let from = parse_direct(
"from alpine:3.10",
Rule::from,
|p| FromInstruction::from_record(p, 0)
)?;
assert_eq!(from, FromInstruction {
span: Span { start: 0, end: 16 },
index: 0,
image: "alpine:3.10".into(),
image_span: Span { start: 5, end: 16 },
image_parsed: ImageRef {
registry: None,
image: "alpine".into(),
tag: Some("3.10".into()),
hash: None
},
alias: None,
alias_span: None
});
Ok(())
}
#[test]
fn from_no_newline() -> Result<()> {
assert!(parse_single(
"from alpine:3.10 from example",
Rule::dockerfile,
).is_err());
Ok(())
}
#[test]
fn from_missing_alias() -> Result<()> {
assert!(parse_single(
"from alpine:3.10 as",
Rule::dockerfile,
).is_err());
Ok(())
}
#[test]
fn from_multiline() -> Result<()> {
let from = parse_direct(
indoc!(r#"
from \
# foo
alpine:3.10 \
# test
# comment
as \
test
"#),
Rule::from,
|p| FromInstruction::from_record(p, 0)
)?;
assert_eq!(from, FromInstruction {
span: Span { start: 0, end: 68 },
index: 0,
image: "alpine:3.10".into(),
image_span: Span { start: 17, end: 28 },
image_parsed: ImageRef {
registry: None,
image: "alpine".into(),
tag: Some("3.10".into()),
hash: None
},
alias: Some("test".into()),
alias_span: Some((64, 68).into())
});
Ok(())
}
}