2020-05-17 21:52:39 +02:00
// This Source Code Form is subject to the terms of
// the Mozilla Public License, v. 2.0. If a copy of the
// MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/.
2020-08-06 00:06:18 +02:00
// Source https://docs.oracle.com/cd/E23824_01/html/E21796/pkg-5.html
2020-05-19 22:14:28 +02:00
use regex ::{ RegexSet , Regex } ;
2021-04-19 09:35:05 -03:00
use std ::collections ::{ HashMap } ;
2021-04-19 23:38:56 -03:00
use std ::fs ::{ File as OsFile , read_to_string } ;
2020-05-17 01:17:19 +02:00
use std ::io ::BufRead ;
use std ::io ::BufReader ;
2020-05-25 16:13:22 +02:00
use crate ::payload ::Payload ;
use std ::clone ::Clone ;
2020-06-04 19:03:51 +02:00
use crate ::digest ::Digest ;
use std ::str ::FromStr ;
2021-04-19 09:35:05 -03:00
use std ::path ::{ Path } ;
use std ::fmt ;
use crate ::errors ::Result ;
2021-04-19 23:38:56 -03:00
use pest ::Parser ;
2020-05-25 16:13:22 +02:00
2021-04-13 22:20:43 -03:00
pub trait FacetedAction {
2020-05-25 16:13:22 +02:00
// Add a facet to the action if the facet is already present the function returns false.
fn add_facet ( & mut self , facet : Facet ) -> bool ;
// Remove a facet from the action.
fn remove_facet ( & mut self , facet : Facet ) -> bool ;
}
2020-06-04 16:09:09 +02:00
#[ derive(Debug, Default) ]
2020-05-25 16:13:22 +02:00
pub struct Action {
kind : ActionKind ,
2020-06-04 16:09:09 +02:00
payload : Payload ,
2021-04-19 23:38:56 -03:00
payload_string : String ,
2020-05-25 16:13:22 +02:00
properties : Vec < Property > ,
2021-04-19 09:35:05 -03:00
facets : HashMap < String , Facet > ,
2020-05-25 16:13:22 +02:00
}
2020-06-04 16:09:09 +02:00
impl Action {
2021-04-13 22:20:43 -03:00
pub fn new ( kind : ActionKind ) -> Action {
2020-06-04 16:09:09 +02:00
Action {
kind ,
payload : Payload ::default ( ) ,
2021-04-19 23:38:56 -03:00
payload_string : String ::new ( ) ,
2020-06-04 16:09:09 +02:00
properties : Vec ::new ( ) ,
2021-04-19 09:35:05 -03:00
facets : HashMap ::new ( ) ,
2020-06-04 16:09:09 +02:00
}
}
}
2020-05-25 16:13:22 +02:00
impl FacetedAction for Action {
fn add_facet ( & mut self , facet : Facet ) -> bool {
2021-04-19 09:35:05 -03:00
return self . facets . insert ( facet . name . clone ( ) , facet . clone ( ) ) = = None
2020-05-25 16:13:22 +02:00
}
fn remove_facet ( & mut self , facet : Facet ) -> bool {
2021-04-19 09:35:05 -03:00
return self . facets . remove ( & facet . name ) = = Some ( facet )
2020-05-25 16:13:22 +02:00
}
}
2020-05-17 21:52:39 +02:00
2021-04-19 09:35:05 -03:00
#[ derive(Debug, Default, PartialEq) ]
2020-05-17 21:52:39 +02:00
pub struct Dir {
pub path : String ,
pub group : String ,
pub owner : String ,
pub mode : String , //TODO implement as bitmask
2020-05-19 22:14:28 +02:00
pub revert_tag : String ,
pub salvage_from : String ,
2021-04-19 09:35:05 -03:00
pub facets : HashMap < String , Facet > ,
2020-05-19 22:14:28 +02:00
}
2021-04-19 23:38:56 -03:00
impl From < Action > for Dir {
fn from ( act : Action ) -> Self {
let mut dir = Dir ::default ( ) ;
for prop in act . properties {
match prop . key . as_str ( ) {
" path " = > dir . path = prop . value ,
" owner " = > dir . owner = prop . value ,
" group " = > dir . group = prop . value ,
" mode " = > dir . mode = prop . value ,
" revert-tag " = > dir . revert_tag = prop . value ,
" salvage-from " = > dir . salvage_from = prop . value ,
_ = > {
if is_facet ( prop . key . clone ( ) ) {
dir . add_facet ( Facet ::from_key_value ( prop . key , prop . value ) ) ;
}
}
}
}
dir
}
}
2020-05-25 16:13:22 +02:00
impl FacetedAction for Dir {
fn add_facet ( & mut self , facet : Facet ) -> bool {
2021-04-19 09:35:05 -03:00
return self . facets . insert ( facet . name . clone ( ) , facet . clone ( ) ) = = None
2020-05-25 16:13:22 +02:00
}
fn remove_facet ( & mut self , facet : Facet ) -> bool {
2021-04-19 09:35:05 -03:00
return self . facets . remove ( & facet . name ) = = Some ( facet )
2020-05-25 16:13:22 +02:00
}
}
2021-04-19 09:35:05 -03:00
#[ derive(Debug, Default, PartialEq) ]
2020-05-21 17:49:51 +02:00
pub struct File {
2021-04-19 09:35:05 -03:00
pub payload : Option < Payload > ,
2020-05-21 17:49:51 +02:00
pub path : String ,
pub group : String ,
pub owner : String ,
pub mode : String , //TODO implement as bitmask
pub preserve : bool ,
pub overlay : bool ,
pub original_name : String ,
pub revert_tag : String ,
pub sys_attr : String ,
pub properties : Vec < Property > ,
2021-04-19 09:35:05 -03:00
pub facets : HashMap < String , Facet > ,
2020-05-21 17:49:51 +02:00
}
2021-04-13 22:20:43 -03:00
impl File {
2021-04-19 09:35:05 -03:00
pub fn read_from_path ( p : & Path ) -> Result < File > {
2021-04-13 22:20:43 -03:00
let mut f = File ::default ( ) ;
match p . to_str ( ) {
2021-04-19 09:35:05 -03:00
Some ( str ) = > {
f . path = str . to_string ( ) ;
f . payload = Some ( Payload ::compute_payload ( p ) ? ) ;
} ,
2021-04-13 22:20:43 -03:00
None = > return Err ( FileError ::FilePathIsNoStringError ) ? ,
}
//TODO group owner mode
Ok ( f )
}
}
2021-04-19 23:38:56 -03:00
impl From < Action > for File {
fn from ( act : Action ) -> Self {
let mut file = File ::default ( ) ;
let mut p = act . payload . clone ( ) ;
if ! act . payload_string . is_empty ( ) {
if act . payload_string . contains ( " / " ) {
file . properties . push ( Property {
key : " original-path " . to_string ( ) ,
value : act . payload_string
} ) ;
} else {
p . primary_identifier = Digest ::from_str ( & act . payload_string ) . unwrap ( ) ;
}
}
for prop in act . properties {
match prop . key . as_str ( ) {
" path " = > file . path = prop . value ,
" owner " = > file . owner = prop . value ,
" group " = > file . group = prop . value ,
" mode " = > file . mode = prop . value ,
" revert-tag " = > file . revert_tag = prop . value ,
" original_name " = > file . original_name = prop . value ,
" sysattr " = > file . sys_attr = prop . value ,
" overlay " = > file . overlay = match string_to_bool ( & prop . value ) {
Ok ( b ) = > b ,
_ = > false ,
} ,
" preserve " = > file . preserve = match string_to_bool ( & prop . value ) {
Ok ( b ) = > b ,
_ = > false ,
} ,
" chash " | " pkg.content-hash " = > p . additional_identifiers . push ( Digest ::from_str ( & prop . value ) . unwrap ( ) ) ,
_ = > {
if is_facet ( prop . key . clone ( ) ) {
file . add_facet ( Facet ::from_key_value ( prop . key , prop . value ) ) ;
} else {
file . properties . push ( prop . clone ( ) ) ;
}
}
}
}
file
}
}
2020-05-25 16:13:22 +02:00
impl FacetedAction for File {
fn add_facet ( & mut self , facet : Facet ) -> bool {
2021-04-19 09:35:05 -03:00
return self . facets . insert ( facet . name . clone ( ) , facet . clone ( ) ) = = None
2020-05-25 16:13:22 +02:00
}
fn remove_facet ( & mut self , facet : Facet ) -> bool {
2021-04-19 09:35:05 -03:00
return self . facets . remove ( & facet . name ) = = Some ( facet )
2020-05-25 16:13:22 +02:00
}
}
2021-04-13 22:20:43 -03:00
#[ derive(Debug, Fail) ]
pub enum FileError {
#[ fail(display = " file path is not a string " ) ]
FilePathIsNoStringError ,
}
2020-08-06 00:06:18 +02:00
//TODO implement multiple FMRI for require-any
2021-04-19 09:35:05 -03:00
#[ derive(Debug, Default, PartialEq) ]
2020-08-06 00:06:18 +02:00
pub struct Dependency {
pub fmri : String , //TODO make FMRI
pub dependency_type : String , //TODO make enum
pub predicate : String , //TODO make FMRI
pub root_image : String , //TODO make boolean
pub optional : Vec < Property > ,
2021-04-19 09:35:05 -03:00
pub facets : HashMap < String , Facet > ,
2020-08-06 00:06:18 +02:00
}
2021-04-19 23:38:56 -03:00
impl From < Action > for Dependency {
fn from ( act : Action ) -> Self {
let mut dep = Dependency ::default ( ) ;
for prop in act . properties {
match prop . key . as_str ( ) {
" fmri " = > dep . fmri = prop . value ,
" type " = > dep . dependency_type = prop . value ,
" predicate " = > dep . predicate = prop . value ,
" root-image " = > dep . root_image = prop . value ,
_ = > {
if is_facet ( prop . key . clone ( ) ) {
dep . add_facet ( Facet ::from_key_value ( prop . key , prop . value ) ) ;
} else {
dep . optional . push ( prop . clone ( ) ) ;
}
}
}
}
dep
}
}
2020-08-06 00:06:18 +02:00
impl FacetedAction for Dependency {
fn add_facet ( & mut self , facet : Facet ) -> bool {
2021-04-19 09:35:05 -03:00
return self . facets . insert ( facet . name . clone ( ) , facet . clone ( ) ) = = None
2020-08-06 00:06:18 +02:00
}
fn remove_facet ( & mut self , facet : Facet ) -> bool {
2021-04-19 09:35:05 -03:00
return self . facets . remove ( & facet . name ) = = Some ( facet )
2020-08-06 00:06:18 +02:00
}
}
2021-04-19 09:35:05 -03:00
#[ derive(Hash, Eq, PartialEq, Debug, Default, Clone) ]
2020-05-19 22:14:28 +02:00
pub struct Facet {
pub name : String ,
pub value : String ,
2020-05-17 21:52:39 +02:00
}
2020-05-17 01:17:19 +02:00
2021-04-19 23:38:56 -03:00
impl Facet {
fn from_key_value ( key : String , value : String ) -> Facet {
Facet {
name : get_facet_key ( key ) ,
value ,
}
}
}
2021-04-19 09:35:05 -03:00
#[ derive(Debug, Default, PartialEq) ]
2020-05-17 01:17:19 +02:00
pub struct Attr {
2020-05-17 21:52:39 +02:00
pub key : String ,
pub values : Vec < String > ,
2021-04-19 09:35:05 -03:00
pub properties : HashMap < String , Property > ,
2020-05-17 21:52:39 +02:00
}
2021-04-19 23:38:56 -03:00
impl From < Action > for Attr {
fn from ( act : Action ) -> Self {
let mut attr = Attr ::default ( ) ;
for prop in act . properties {
match prop . key . as_str ( ) {
" name " = > attr . key = prop . value ,
" value " = > attr . values . push ( prop . value ) ,
_ = > {
attr . properties . insert ( prop . key . clone ( ) , Property {
key : prop . key ,
value : prop . value
} ) ;
}
}
}
attr
}
}
#[ derive(Hash, Eq, PartialEq, Debug, Default, Clone) ]
2020-05-17 21:52:39 +02:00
pub struct Property {
pub key : String ,
pub value : String ,
2020-05-17 01:17:19 +02:00
}
2021-04-19 09:35:05 -03:00
#[ derive(Debug, Default, PartialEq) ]
2020-05-17 01:17:19 +02:00
pub struct Manifest {
2020-05-17 21:52:39 +02:00
pub attributes : Vec < Attr > ,
2020-05-19 22:14:28 +02:00
pub directories : Vec < Dir > ,
2020-05-21 17:49:51 +02:00
pub files : Vec < File > ,
2020-08-06 00:06:18 +02:00
pub dependencies : Vec < Dependency > ,
2020-05-17 01:17:19 +02:00
}
impl Manifest {
pub fn new ( ) -> Manifest {
2020-05-18 10:32:16 +02:00
return Manifest {
2020-05-17 21:52:39 +02:00
attributes : Vec ::new ( ) ,
2020-05-19 22:14:28 +02:00
directories : Vec ::new ( ) ,
2020-05-21 17:49:51 +02:00
files : Vec ::new ( ) ,
2020-08-06 00:06:18 +02:00
dependencies : Vec ::new ( ) ,
2020-05-17 01:17:19 +02:00
} ;
}
2021-04-13 22:20:43 -03:00
pub fn add_file ( & mut self , f : File ) {
self . files . push ( f ) ;
}
2021-04-19 09:35:05 -03:00
2021-04-19 23:38:56 -03:00
fn add_action ( & mut self , act : Action ) {
match act . kind {
ActionKind ::Attr = > {
self . attributes . push ( act . into ( ) ) ;
}
ActionKind ::Dir = > {
self . directories . push ( act . into ( ) ) ;
}
ActionKind ::File = > {
self . files . push ( act . into ( ) ) ;
}
ActionKind ::Dependency = > {
self . dependencies . push ( act . into ( ) ) ;
}
ActionKind ::User = > {
}
ActionKind ::Group = > {
}
ActionKind ::Driver = > {
}
ActionKind ::License = > {
}
ActionKind ::Link = > {
}
ActionKind ::Legacy = > {
}
ActionKind ::Transform = > {
}
ActionKind ::Unknown { action } = > ( ) ,
}
}
pub fn parse_file ( f : String ) -> Result < Manifest > {
let content = read_to_string ( Path ::new ( & f ) ) ? ;
Manifest ::parse_string ( content )
}
pub fn parse_string ( content : String ) -> Result < Manifest > {
let mut m = Manifest ::new ( ) ;
let pairs = ManifestParser ::parse ( Rule ::manifest , & content ) ? ;
for p in pairs {
match p . as_rule ( ) {
Rule ::manifest = > {
for manifest in p . clone ( ) . into_inner ( ) {
match manifest . as_rule ( ) {
Rule ::action = > {
let mut act = Action ::default ( ) ;
for action in manifest . clone ( ) . into_inner ( ) {
match action . as_rule ( ) {
Rule ::action_name = > {
act . kind = get_action_kind ( action . as_str ( ) ) ;
}
Rule ::payload = > {
act . payload_string = action . as_str ( ) . clone ( ) . into ( ) ;
}
Rule ::property = > {
let mut property = Property ::default ( ) ;
for prop in action . clone ( ) . into_inner ( ) {
match prop . as_rule ( ) {
Rule ::property_name = > {
property . key = prop . as_str ( ) . clone ( ) . into ( ) ;
}
Rule ::property_value = > {
property . value = prop . as_str ( ) . clone ( ) . into ( ) ;
}
_ = > panic! ( " unexpected rule {:?} inside action expected property_name or property_value " , prop . as_rule ( ) )
}
}
act . properties . push ( property ) ;
}
Rule ::EOI = > ( ) ,
_ = > panic! ( " unexpected rule {:?} inside action expected payload, property, action_name " , action . as_rule ( ) ) ,
}
}
m . add_action ( act ) ;
}
Rule ::EOI = > ( ) ,
_ = > panic! ( " unexpected rule {:?} inside manifest expected action " , manifest . as_rule ( ) ) ,
}
}
}
_ = > panic! ( " unexpected rule {:?} inside pair expected manifest " , p . as_rule ( ) ) ,
}
}
2021-04-19 09:35:05 -03:00
2021-04-19 23:38:56 -03:00
Ok ( m )
2021-04-19 09:35:05 -03:00
}
2020-05-17 01:17:19 +02:00
}
2020-05-25 16:13:22 +02:00
#[ derive(Debug) ]
2021-04-13 22:20:43 -03:00
pub enum ActionKind {
2020-05-17 21:52:39 +02:00
Attr ,
Dir ,
File ,
Dependency ,
User ,
Group ,
Driver ,
License ,
Link ,
2020-05-18 16:57:21 +02:00
Legacy ,
Unknown { action : String } ,
2021-04-19 09:35:05 -03:00
Transform ,
2020-05-17 21:52:39 +02:00
}
2020-06-04 19:03:51 +02:00
impl Default for ActionKind {
fn default ( ) -> Self { ActionKind ::Unknown { action : String ::new ( ) } }
}
2020-05-18 16:57:21 +02:00
//TODO Multierror and no failure for these cases
#[ derive(Debug, Fail) ]
2020-05-17 01:17:19 +02:00
pub enum ManifestError {
2020-05-18 16:57:21 +02:00
#[ fail(display = " unknown action {} at line {} " , action, line) ]
UnknownAction {
line : usize ,
action : String ,
} ,
2020-05-19 22:14:28 +02:00
#[ fail(display = " action string \" {} \" at line {} is invalid: {} " , action, line, message) ]
InvalidAction {
line : usize ,
action : String ,
message : String ,
} ,
2020-05-17 01:17:19 +02:00
}
2021-04-19 09:35:05 -03:00
#[ derive(Parser) ]
2021-04-19 21:34:40 -03:00
#[ grammar = " actions/manifest.pest " ]
2021-04-19 09:35:05 -03:00
struct ManifestParser ;
2021-04-19 23:38:56 -03:00
fn get_action_kind ( act : & str ) -> ActionKind {
return match act {
" set " = > ActionKind ::Attr ,
" depend " = > ActionKind ::Dependency ,
" dir " = > ActionKind ::Dir ,
" file " = > ActionKind ::File ,
" license " = > ActionKind ::License ,
" hardlink " = > ActionKind ::Link ,
" link " = > ActionKind ::Link ,
" driver " = > ActionKind ::Driver ,
" group " = > ActionKind ::Group ,
" user " = > ActionKind ::User ,
" legacy " = > ActionKind ::Legacy ,
" <transform " = > ActionKind ::Transform ,
_ = > ActionKind ::Unknown { action : act . into ( ) } ,
}
}
fn is_facet ( s : String ) -> bool {
s . starts_with ( " facet. " )
}
fn get_facet_key ( facet_string : String ) -> String {
match facet_string . find ( " . " ) {
Some ( idx ) = > {
facet_string . clone ( ) . split_off ( idx + 1 )
} ,
None = > facet_string . clone ( )
}
}
2021-04-19 09:35:05 -03:00
pub fn parse_manifest_file ( filename : String ) -> Result < Manifest > {
2020-05-18 16:57:21 +02:00
let mut m = Manifest ::new ( ) ;
2020-05-21 17:49:51 +02:00
let f = OsFile ::open ( filename ) ? ;
2020-05-17 01:17:19 +02:00
let file = BufReader ::new ( & f ) ;
2020-05-18 16:57:21 +02:00
for ( line_nr , line_read ) in file . lines ( ) . enumerate ( ) {
2021-04-19 09:35:05 -03:00
let line = line_read ? ;
if ! line . starts_with ( " # " ) {
handle_manifest_line ( & mut m , line . trim_start ( ) , line_nr ) ? ;
}
2020-05-18 10:32:16 +02:00
}
2020-05-18 16:57:21 +02:00
return Ok ( m ) ;
2020-05-17 01:17:19 +02:00
}
2021-04-19 09:35:05 -03:00
pub fn parse_manifest_string ( manifest : String ) -> Result < Manifest > {
2020-05-17 01:17:19 +02:00
let mut m = Manifest ::new ( ) ;
2020-05-18 16:57:21 +02:00
for ( line_nr , line ) in manifest . lines ( ) . enumerate ( ) {
2020-05-19 10:39:06 +02:00
handle_manifest_line ( & mut m , line . trim_start ( ) , line_nr ) ? ;
}
return Ok ( m ) ;
}
2020-05-18 16:57:21 +02:00
2021-04-19 09:35:05 -03:00
fn handle_manifest_line ( manifest : & mut Manifest , line : & str , line_nr : usize ) -> Result < ( ) > {
2020-05-19 10:39:06 +02:00
match determine_action_kind ( & line ) {
ActionKind ::Attr = > {
manifest . attributes . push ( parse_attr_action ( String ::from ( line ) ) ? ) ;
}
ActionKind ::Dir = > {
2020-05-19 22:14:28 +02:00
manifest . directories . push ( parse_dir_action ( String ::from ( line ) , line_nr ) ? ) ;
2020-05-19 10:39:06 +02:00
}
ActionKind ::File = > {
2020-05-21 17:49:51 +02:00
manifest . files . push ( parse_file_action ( String ::from ( line ) , line_nr ) ? ) ;
2020-05-19 10:39:06 +02:00
}
ActionKind ::Dependency = > {
2020-08-06 00:06:18 +02:00
manifest . dependencies . push ( parse_depend_action ( String ::from ( line ) , line_nr ) ? ) ;
2020-05-19 10:39:06 +02:00
}
ActionKind ::User = > {
2020-05-18 16:57:21 +02:00
2020-05-19 10:39:06 +02:00
}
ActionKind ::Group = > {
2020-05-18 16:57:21 +02:00
2020-05-19 10:39:06 +02:00
}
ActionKind ::Driver = > {
2020-05-18 16:57:21 +02:00
2020-05-19 10:39:06 +02:00
}
ActionKind ::License = > {
2020-05-18 16:57:21 +02:00
2020-05-19 10:39:06 +02:00
}
ActionKind ::Link = > {
2020-05-18 16:57:21 +02:00
2020-05-19 10:39:06 +02:00
}
ActionKind ::Legacy = > {
2020-05-18 16:57:21 +02:00
2021-04-19 09:35:05 -03:00
}
ActionKind ::Transform = > {
2020-05-19 10:39:06 +02:00
}
ActionKind ::Unknown { action } = > {
2021-04-19 09:35:05 -03:00
if ! action . is_empty ( ) {
Err ( ManifestError ::UnknownAction { action , line : line_nr } ) ? ;
}
2020-05-17 01:17:19 +02:00
}
}
2020-05-19 10:39:06 +02:00
Ok ( ( ) )
2020-05-17 01:17:19 +02:00
}
2021-04-19 09:35:05 -03:00
fn add_facet_to_action < T : FacetedAction > ( action : & mut T , facet_string : String , line : String , line_nr : usize ) -> Result < ( ) > {
2020-08-06 00:06:18 +02:00
let mut facet_key = match facet_string . find ( " . " ) {
2020-05-25 16:13:22 +02:00
Some ( idx ) = > {
facet_string . clone ( ) . split_off ( idx + 1 )
} ,
None = > return Err ( ManifestError ::InvalidAction { action : line , line : line_nr , message : String ::from ( " separation dot not found but string contains facet. " ) } ) ?
} ;
2020-08-06 00:06:18 +02:00
let value = match facet_key . find ( " = " ) {
2020-05-25 16:13:22 +02:00
Some ( idx ) = > {
2020-08-06 00:06:18 +02:00
facet_key . split_off ( idx + 1 )
2020-05-25 16:13:22 +02:00
} ,
None = > return Err ( ManifestError ::InvalidAction { action : line , line : line_nr , message : String ::from ( " no value present for facet " ) } ) ?
} ;
2020-08-06 00:06:18 +02:00
facet_key . truncate ( facet_key . len ( ) - 1 ) ;
if ! action . add_facet ( Facet { name : clean_string_value ( facet_key . as_str ( ) ) , value : clean_string_value ( value . as_str ( ) ) } ) {
2020-05-25 16:13:22 +02:00
return Err ( ManifestError ::InvalidAction { action : line , line : line_nr , message : String ::from ( " double declaration of facet " ) } ) ?
}
Ok ( ( ) )
}
2020-05-18 16:57:21 +02:00
fn determine_action_kind ( line : & str ) -> ActionKind {
let mut act = String ::new ( ) ;
for c in line . trim_start ( ) . chars ( ) {
if c = = ' ' {
break
}
act . push ( c )
}
return match act . as_str ( ) {
" set " = > ActionKind ::Attr ,
" depend " = > ActionKind ::Dependency ,
" dir " = > ActionKind ::Dir ,
" file " = > ActionKind ::File ,
" license " = > ActionKind ::License ,
" hardlink " = > ActionKind ::Link ,
" link " = > ActionKind ::Link ,
" driver " = > ActionKind ::Driver ,
" group " = > ActionKind ::Group ,
" user " = > ActionKind ::User ,
" legacy " = > ActionKind ::Legacy ,
2021-04-19 09:35:05 -03:00
" <transform " = > ActionKind ::Transform ,
2020-05-18 16:57:21 +02:00
_ = > ActionKind ::Unknown { action : act } ,
2020-05-17 01:17:19 +02:00
}
}
2020-05-21 17:49:51 +02:00
fn clean_string_value ( orig : & str ) -> String {
return String ::from ( orig ) . trim_end ( ) . replace ( & [ '"' , '\\' ] [ .. ] , " " )
}
2021-04-19 09:35:05 -03:00
fn string_to_bool ( orig : & str ) -> Result < bool > {
2020-05-21 17:49:51 +02:00
match & String ::from ( orig ) . trim ( ) . to_lowercase ( ) [ .. ] {
" true " = > Ok ( true ) ,
" false " = > Ok ( false ) ,
" t " = > Ok ( true ) ,
" f " = > Ok ( false ) ,
2021-04-19 09:35:05 -03:00
_ = > Err ( failure ::err_msg ( " not a boolean like value " ) )
2020-05-21 17:49:51 +02:00
}
}
2021-04-19 09:35:05 -03:00
fn parse_depend_action ( line : String , line_nr : usize ) -> Result < Dependency > {
2020-08-06 00:06:18 +02:00
let mut act = Dependency ::default ( ) ;
let regex_set = RegexSet ::new ( & [
r # "([^ ]+)=([^"][^ ]+[^"])"# ,
r # "([^ ]+)="(.+)"#
] ) ? ;
for ( pat , _ ) in regex_set . matches ( line . trim_start ( ) ) . into_iter ( ) . map ( | match_idx | ( & regex_set . patterns ( ) [ match_idx ] , match_idx ) ) {
let regex = Regex ::new ( & pat ) ? ;
for cap in regex . captures_iter ( line . clone ( ) . trim_start ( ) ) {
let full_cap_idx = 0 ;
let key_cap_idx = 1 ;
let val_cap_idx = 2 ;
match & cap [ key_cap_idx ] {
" fmri " = > act . fmri = clean_string_value ( & cap [ val_cap_idx ] ) ,
" type " = > act . dependency_type = clean_string_value ( & cap [ val_cap_idx ] ) ,
" predicate " = > act . predicate = clean_string_value ( & cap [ val_cap_idx ] ) ,
" root-image " = > act . root_image = clean_string_value ( & cap [ val_cap_idx ] ) ,
_ = > {
let key_val_string = String ::from ( & cap [ full_cap_idx ] ) ;
if key_val_string . contains ( " facet. " ) {
match add_facet_to_action ( & mut act , key_val_string , line . clone ( ) , line_nr ) {
Ok ( _ ) = > continue ,
Err ( e ) = > return Err ( e ) ? ,
}
} else {
act . optional . push ( Property { key : clean_string_value ( & cap [ key_cap_idx ] ) , value : clean_string_value ( & cap [ val_cap_idx ] ) } ) ;
}
}
}
}
}
Ok ( act )
}
2021-04-19 09:35:05 -03:00
fn parse_file_action ( line : String , line_nr : usize ) -> Result < File > {
2020-05-21 17:49:51 +02:00
let mut act = File ::default ( ) ;
let regex_set = RegexSet ::new ( & [
r "file ([a-zA-Z0-9]+) " ,
r # "([^ ]+)=([^"][^ ]+[^"])"# ,
r # "([^ ]+)="(.+)"#
] ) ? ;
2021-04-19 21:34:40 -03:00
let mut p = Payload ::default ( ) ;
2020-05-21 17:49:51 +02:00
for ( pat , idx ) in regex_set . matches ( line . trim_start ( ) ) . into_iter ( ) . map ( | match_idx | ( & regex_set . patterns ( ) [ match_idx ] , match_idx ) ) {
let regex = Regex ::new ( & pat ) ? ;
2020-06-04 19:03:51 +02:00
for cap in regex . captures_iter ( line . clone ( ) . trim_start ( ) ) {
2020-05-21 17:49:51 +02:00
if idx = = 0 {
2021-04-19 21:34:40 -03:00
p . primary_identifier = Digest ::from_str ( & cap [ 1 ] ) ? ;
2020-05-21 17:49:51 +02:00
continue ;
}
let full_cap_idx = 0 ;
let key_cap_idx = 1 ;
let val_cap_idx = 2 ;
match & cap [ key_cap_idx ] {
" path " = > act . path = clean_string_value ( & cap [ val_cap_idx ] ) ,
" owner " = > act . owner = clean_string_value ( & cap [ val_cap_idx ] ) ,
" group " = > act . group = clean_string_value ( & cap [ val_cap_idx ] ) ,
" mode " = > act . mode = clean_string_value ( & cap [ val_cap_idx ] ) ,
" revert-tag " = > act . revert_tag = clean_string_value ( & cap [ val_cap_idx ] ) ,
" original_name " = > act . original_name = clean_string_value ( & cap [ val_cap_idx ] ) ,
" sysattr " = > act . sys_attr = clean_string_value ( & cap [ val_cap_idx ] ) ,
" overlay " = > act . overlay = match string_to_bool ( & cap [ val_cap_idx ] ) {
Ok ( b ) = > b ,
2021-04-19 21:34:40 -03:00
Err ( e ) = > return Err ( ManifestError ::InvalidAction { action : line , line : line_nr , message : e . to_string ( ) } ) ?
} ,
2020-05-21 17:49:51 +02:00
" preserve " = > act . preserve = match string_to_bool ( & cap [ val_cap_idx ] ) {
Ok ( b ) = > b ,
2021-04-19 21:34:40 -03:00
Err ( e ) = > return Err ( ManifestError ::InvalidAction { action : line , line : line_nr , message : e . to_string ( ) } ) ?
2020-05-21 17:49:51 +02:00
} ,
2021-04-19 21:34:40 -03:00
" chash " | " pkg.content-hash " = > p . additional_identifiers . push ( match Digest ::from_str ( clean_string_value ( & cap [ val_cap_idx ] ) . as_str ( ) ) {
2020-08-04 22:32:44 +02:00
Ok ( d ) = > d ,
Err ( e ) = > return Err ( e ) ?
} ) ,
2020-05-21 17:49:51 +02:00
_ = > {
2020-08-04 22:32:44 +02:00
let key_val_string = String ::from ( & cap [ full_cap_idx ] ) ;
2020-05-21 17:49:51 +02:00
if key_val_string . contains ( " facet. " ) {
2020-06-04 19:03:51 +02:00
match add_facet_to_action ( & mut act , key_val_string , line . clone ( ) , line_nr ) {
2020-05-25 16:13:22 +02:00
Ok ( _ ) = > continue ,
Err ( e ) = > return Err ( e ) ? ,
2020-05-21 17:49:51 +02:00
}
2020-08-04 22:32:44 +02:00
} else {
act . properties . push ( Property { key : clean_string_value ( & cap [ key_cap_idx ] ) , value : clean_string_value ( & cap [ val_cap_idx ] ) } ) ;
2020-05-21 17:49:51 +02:00
}
}
}
}
}
2021-04-19 21:34:40 -03:00
act . payload = Some ( p ) ;
2020-05-21 17:49:51 +02:00
Ok ( act )
}
2021-04-19 09:35:05 -03:00
fn parse_dir_action ( line : String , line_nr : usize ) -> Result < Dir > {
2020-05-19 22:14:28 +02:00
let mut act = Dir ::default ( ) ;
2020-05-21 12:04:47 +02:00
let regex_set = RegexSet ::new ( & [
r # "([^ ]+)=([^"][^ ]+[^"])"# ,
r # "([^ ]+)="(.+)"#
] ) ? ;
for pat in regex_set . matches ( line . trim_start ( ) ) . into_iter ( ) . map ( | match_idx | & regex_set . patterns ( ) [ match_idx ] ) {
let regex = Regex ::new ( & pat ) ? ;
2020-06-04 19:03:51 +02:00
for cap in regex . captures_iter ( line . clone ( ) . trim_start ( ) ) {
2020-05-21 12:04:47 +02:00
let full_cap_idx = 0 ;
let key_cap_idx = 1 ;
let val_cap_idx = 2 ;
match & cap [ key_cap_idx ] {
2020-05-25 16:13:22 +02:00
" path " = > act . path = clean_string_value ( & cap [ val_cap_idx ] ) ,
" owner " = > act . owner = clean_string_value ( & cap [ val_cap_idx ] ) ,
" group " = > act . group = clean_string_value ( & cap [ val_cap_idx ] ) ,
" mode " = > act . mode = clean_string_value ( & cap [ val_cap_idx ] ) ,
" revert-tag " = > act . revert_tag = clean_string_value ( & cap [ val_cap_idx ] ) ,
" salvage-from " = > act . salvage_from = clean_string_value ( & cap [ val_cap_idx ] ) ,
2020-05-21 12:04:47 +02:00
_ = > {
2020-05-25 16:13:22 +02:00
let key_val_string = clean_string_value ( & cap [ full_cap_idx ] ) ;
2020-05-21 12:04:47 +02:00
if key_val_string . contains ( " facet. " ) {
2020-06-04 19:03:51 +02:00
match add_facet_to_action ( & mut act , key_val_string , line . clone ( ) , line_nr ) {
2020-05-25 16:13:22 +02:00
Ok ( _ ) = > continue ,
Err ( e ) = > return Err ( e ) ? ,
2020-05-21 12:04:47 +02:00
}
2020-05-19 22:14:28 +02:00
}
}
}
}
}
2020-05-21 12:04:47 +02:00
2020-05-19 22:14:28 +02:00
Ok ( act )
}
2021-04-19 09:35:05 -03:00
fn parse_attr_action ( line : String ) -> Result < Attr > {
2020-05-18 14:57:55 +02:00
// Do a full line match to see if we can fast path this.
// This also catches values with spaces, that have not been properly escaped.
// Note: values with spaces must be properly escaped or the rest here will fail. Strings with
// unescaped spaces are never valid but sadly present in the wild.
// Fast path will fail if a value has multiple values or a '=' sign in the values
2020-05-18 16:57:21 +02:00
let full_line_regex = Regex ::new ( r "^set name=([^ ]+) value=(.+)$" ) ? ;
2020-05-18 14:57:55 +02:00
if full_line_regex . is_match ( line . trim_start ( ) ) {
match full_line_regex . captures ( line . trim_start ( ) ) {
Some ( captures ) = > {
let mut fast_path_fail = false ;
let mut val = String ::from ( & captures [ 2 ] ) ;
if val . contains ( " = " ) {
fast_path_fail = true ;
}
if val . contains ( " value= " ) {
fast_path_fail = true ;
}
if val . contains ( " name= " ) {
fast_path_fail = true ;
}
val = val . replace ( & [ '"' , '\\' ] [ .. ] , " " ) ;
2020-05-21 12:04:47 +02:00
//TODO knock out single quotes somehow without
2020-05-18 14:57:55 +02:00
if ! fast_path_fail {
return Ok ( Attr {
key : String ::from ( & captures [ 1 ] ) ,
values : vec ! [ val ] ,
.. Attr ::default ( )
} ) ;
}
}
None = > ( ) ,
} ;
}
2020-05-17 21:52:39 +02:00
//Todo move regex initialisation out of for loop into static area
2020-05-18 16:57:21 +02:00
let name_regex = Regex ::new ( r "name=([^ ]+) value=" ) ? ;
2020-05-17 21:52:39 +02:00
let mut key = String ::new ( ) ;
2020-05-17 01:17:19 +02:00
for cap in name_regex . captures_iter ( line . trim_start ( ) ) {
2020-05-17 21:52:39 +02:00
key = String ::from ( & cap [ 1 ] ) ;
2020-05-17 01:17:19 +02:00
}
let mut values = Vec ::new ( ) ;
2020-05-18 16:57:21 +02:00
let value_no_space_regex = Regex ::new ( r # "value="(.+)""# ) ? ;
2020-05-17 01:17:19 +02:00
2020-05-18 16:57:21 +02:00
let value_space_regex = Regex ::new ( r # "value=([^"][^ ]+[^"])"# ) ? ;
2020-05-17 01:17:19 +02:00
2021-04-19 09:35:05 -03:00
let mut properties = HashMap ::new ( ) ;
2020-05-18 16:57:21 +02:00
let optionals_regex_no_quotes = Regex ::new ( r # "([^ ]+)=([^"][^ ]+[^"])"# ) ? ;
let optionals_regex_quotes = Regex ::new ( r # "([^ ]+)=([^"][^ ]+[^"])"# ) ? ;
2020-05-17 21:52:39 +02:00
2020-05-17 01:17:19 +02:00
for cap in value_no_space_regex . captures_iter ( line . trim_start ( ) ) {
2020-05-17 21:52:39 +02:00
values . push ( String ::from ( cap [ 1 ] . trim ( ) ) ) ;
2020-05-17 01:17:19 +02:00
}
for cap in value_space_regex . captures_iter ( line . trim_start ( ) ) {
2020-05-17 21:52:39 +02:00
values . push ( String ::from ( cap [ 1 ] . trim ( ) ) ) ;
}
for cap in optionals_regex_quotes . captures_iter ( line . trim_start ( ) ) {
if cap [ 1 ] . trim ( ) . starts_with ( " name " ) | | cap [ 1 ] . trim ( ) . starts_with ( " value " ) {
continue ;
}
2021-04-19 09:35:05 -03:00
properties . insert ( String ::from ( cap [ 1 ] . trim ( ) ) , Property {
2020-05-17 22:02:35 +02:00
key : String ::from ( cap [ 1 ] . trim ( ) ) ,
value : String ::from ( cap [ 2 ] . trim ( ) ) ,
2020-05-17 21:52:39 +02:00
} ) ;
}
for cap in optionals_regex_no_quotes . captures_iter ( line . trim_start ( ) ) {
if cap [ 1 ] . trim ( ) . starts_with ( " name " ) | | cap [ 1 ] . trim ( ) . starts_with ( " value " ) {
continue ;
}
2021-04-19 09:35:05 -03:00
properties . insert ( String ::from ( cap [ 1 ] . trim ( ) ) , Property {
2020-05-17 22:02:35 +02:00
key : String ::from ( cap [ 1 ] . trim ( ) ) ,
value : String ::from ( cap [ 2 ] . trim ( ) ) ,
2020-05-17 21:52:39 +02:00
} ) ;
2020-05-17 01:17:19 +02:00
}
2020-05-18 10:32:16 +02:00
Ok ( Attr {
2020-05-17 21:52:39 +02:00
key ,
values ,
properties ,
2020-05-17 01:17:19 +02:00
} )
2020-05-18 10:32:16 +02:00
}