11// Copyright (c) Sony Pictures Imageworks, et al.
22// SPDX-License-Identifier: Apache-2.0
33// https://github.com/imageworks/spk
4+ use std:: marker:: PhantomData ;
5+
6+ use ngrammatic:: CorpusBuilder ;
47use serde:: { Deserialize , Serialize } ;
8+ use spk_schema_foundation:: option_map:: Stringified ;
59use spk_schema_ident:: BuildIdent ;
610
711use super :: { ComponentSpecList , EmbeddedPackagesList , EnvOp , OpKind , RequirementsList } ;
@@ -13,21 +17,66 @@ use crate::Result;
1317mod install_spec_test;
1418
1519/// A set of structured installation parameters for a package.
16- #[ derive( Clone , Debug , Default , Deserialize , Eq , Hash , Ord , PartialEq , PartialOrd , Serialize ) ]
20+ #[ derive( Clone , Debug , Default , Eq , Hash , Ord , PartialEq , PartialOrd , Serialize ) ]
1721pub struct InstallSpec {
1822 #[ serde( default , skip_serializing_if = "Vec::is_empty" ) ]
1923 pub requirements : RequirementsList ,
2024 #[ serde( default , skip_serializing_if = "Vec::is_empty" ) ]
2125 pub embedded : EmbeddedPackagesList ,
2226 #[ serde( default ) ]
2327 pub components : ComponentSpecList ,
24- #[ serde(
25- default ,
26- deserialize_with = "deserialize_env_conf" ,
27- skip_serializing_if = "Vec::is_empty"
28- ) ]
28+ #[ serde( default , skip_serializing_if = "Vec::is_empty" ) ]
2929 pub environment : Vec < EnvOp > ,
3030}
31+ #[ derive( Clone , Debug , Default , Eq , Hash , Ord , PartialEq , PartialOrd , Serialize ) ]
32+ pub struct LintedInstallSpec {
33+ pub install_spec : InstallSpec ,
34+ pub lints : Vec < String > ,
35+ }
36+
37+ #[ derive( Default ) ]
38+ struct InstallSpecVisitor < D >
39+ where
40+ D : Default ,
41+ {
42+ requirements : RequirementsList ,
43+ embedded : EmbeddedPackagesList ,
44+ components : ComponentSpecList ,
45+ environment : Vec < EnvOp > ,
46+ lints : Vec < String > ,
47+ _phantom : PhantomData < D > ,
48+ }
49+
50+ impl < D > From < InstallSpecVisitor < D > > for InstallSpec
51+ where
52+ D : Default ,
53+ {
54+ fn from ( value : InstallSpecVisitor < D > ) -> Self {
55+ Self {
56+ requirements : value. requirements ,
57+ embedded : value. embedded ,
58+ components : value. components ,
59+ environment : value. environment ,
60+ }
61+ }
62+ }
63+
64+ impl < D > From < InstallSpecVisitor < D > > for LintedInstallSpec
65+ where
66+ D : Default ,
67+ {
68+ fn from ( value : InstallSpecVisitor < D > ) -> Self {
69+ Self {
70+ install_spec : InstallSpec {
71+ requirements : value. requirements ,
72+ embedded : value. embedded ,
73+ components : value. components ,
74+ environment : value. environment ,
75+ } ,
76+ lints : value. lints ,
77+ }
78+ }
79+ }
3180
3281impl InstallSpec {
3382 pub fn is_default ( & self ) -> bool {
@@ -44,37 +93,86 @@ impl InstallSpec {
4493 }
4594}
4695
47- fn deserialize_env_conf < ' de , D > ( deserializer : D ) -> std:: result:: Result < Vec < EnvOp > , D :: Error >
96+ impl < ' de > Deserialize < ' de > for InstallSpec {
97+ fn deserialize < D > ( deserializer : D ) -> std:: result:: Result < Self , D :: Error >
98+ where
99+ D : serde:: de:: Deserializer < ' de > ,
100+ {
101+ Ok ( std:: convert:: Into :: < InstallSpec > :: into (
102+ deserializer. deserialize_map ( InstallSpecVisitor :: < InstallSpec > :: default ( ) ) ?,
103+ ) )
104+ }
105+ }
106+
107+ impl < ' de > Deserialize < ' de > for LintedInstallSpec {
108+ fn deserialize < D > ( deserializer : D ) -> std:: result:: Result < Self , D :: Error >
109+ where
110+ D : serde:: de:: Deserializer < ' de > ,
111+ {
112+ Ok ( std:: convert:: Into :: < LintedInstallSpec > :: into (
113+ deserializer. deserialize_map ( InstallSpecVisitor :: < LintedInstallSpec > :: default ( ) ) ?,
114+ ) )
115+ }
116+ }
117+
118+ impl < ' de , D > serde:: de:: Visitor < ' de > for InstallSpecVisitor < D >
48119where
49- D : serde :: Deserializer < ' de > ,
120+ D : Default + From < InstallSpecVisitor < D > > ,
50121{
51- struct EnvConfVisitor ;
122+ type Value = D ;
52123
53- impl < ' de > serde:: de:: Visitor < ' de > for EnvConfVisitor {
54- type Value = Vec < EnvOp > ;
124+ fn expecting ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
125+ f. write_str ( "a package specification" )
126+ }
127+
128+ fn visit_map < A > ( mut self , mut map : A ) -> std:: result:: Result < Self :: Value , A :: Error >
129+ where
130+ A : serde:: de:: MapAccess < ' de > ,
131+ {
132+ while let Some ( key) = map. next_key :: < Stringified > ( ) ? {
133+ match key. as_str ( ) {
134+ "requirements" => self . requirements = map. next_value :: < RequirementsList > ( ) ?,
135+ "embedded" => self . embedded = map. next_value :: < EmbeddedPackagesList > ( ) ?,
136+ "components" => self . components = map. next_value :: < ComponentSpecList > ( ) ?,
137+ "environment" => self . environment = map. next_value :: < Vec < EnvOp > > ( ) ?,
138+ unknown_config => {
139+ self . lints . push ( format ! ( "Unknown config: {unknown_config}" ) ) ;
140+ let mut corpus = CorpusBuilder :: new ( ) . finish ( ) ;
141+
142+ corpus. add_text ( "requirements" ) ;
143+ corpus. add_text ( "embedded" ) ;
144+ corpus. add_text ( "components" ) ;
145+ corpus. add_text ( "environment" ) ;
146+
147+ let results = corpus. search ( unknown_config, 0.6 ) ;
148+ let no_match = format ! ( "No similar config found for: {}" , unknown_config) ;
149+ let top_match = match results. first ( ) {
150+ Some ( s) => & s. text ,
151+ None => & no_match,
152+ } ;
55153
56- fn expecting ( & self , f : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
57- f. write_str ( "an environment configuration" )
154+ self . lints
155+ . push ( format ! ( "The most similar config is: {}" , top_match) ) ;
156+
157+ // ignore any unrecognized field, but consume the value anyway
158+ // TODO: could we warn about fields that look like typos?
159+ map. next_value :: < serde:: de:: IgnoredAny > ( ) ?;
160+ }
161+ }
58162 }
59163
60- fn visit_seq < A > ( self , mut seq : A ) -> std:: result:: Result < Self :: Value , A :: Error >
61- where
62- A : serde:: de:: SeqAccess < ' de > ,
164+ if self
165+ . environment
166+ . iter ( )
167+ . filter ( |& e| e. kind ( ) == OpKind :: Priority )
168+ . count ( )
169+ > 1
63170 {
64- let mut vec = Vec :: new ( ) ;
65-
66- while let Some ( elem) = seq. next_element :: < EnvOp > ( ) ? {
67- if vec. iter ( ) . any ( |x : & EnvOp | x. kind ( ) == OpKind :: Priority )
68- && elem. kind ( ) == OpKind :: Priority
69- {
70- return Err ( serde:: de:: Error :: custom (
71- "Multiple priority config cannot be set." ,
72- ) ) ;
73- } ;
74- vec. push ( elem) ;
75- }
76- Ok ( vec)
171+ return Err ( serde:: de:: Error :: custom (
172+ "Multiple priority configs cannot be set" ,
173+ ) ) ;
77174 }
175+
176+ Ok ( self . into ( ) )
78177 }
79- deserializer. deserialize_seq ( EnvConfVisitor )
80178}
0 commit comments